Skip to content

run_api_docs docstring check demands a docstring for re-exported module bindings, which the re-exporting package cannot supply #47

Description

@ChrisRackauckas-Claude

Summary

run_api_docs's docstrings check requires a docstring for every name in public_api_names(pkg), including re-exported module bindings. A package that re-exports a dependency's module (export SciMLBase) cannot satisfy that check: Julia resolves Docs.Binding(pkg, :SciMLBase) to SciMLBase.SciMLBase, so any @doc written in the re-exporting package is stored in the dependency's doc registry, not its own. The only ways to make the check green are (a) a module docstring in the dependency, or (b) docstring piracy at load time.

The rendered check already handles this case — _requires_local_rendering returns false for a value that isa Module and is owned outside pkg's hierarchy, and the run_api_docs docstring says "Re-exported modules inherit the defining package's rendered module documentation." The docstrings check has no analogous exemption. That asymmetry looks unintended.

Reproduction

SciMLTesting v2.6.2, Julia 1.12.6. SciMLLogging (like SciMLBase) has no module docstring in any released version — src/SciMLLogging.jl line 1 is a bare module SciMLLogging.

using SciMLLogging: SciMLLogging

module Facade
    using SciMLLogging: SciMLLogging
    export SciMLLogging
end

println("hasdoc(Facade, :SciMLLogging) before      = ", Base.Docs.hasdoc(Facade, :SciMLLogging))
println("Docs.Binding(Facade, :SciMLLogging)       = ", Base.Docs.Binding(Facade, :SciMLLogging))

# What `@doc "..." SciMLLogging` inside the facade package would do.
Core.eval(Facade, :(Base.@doc "Re-export of `SciMLLogging`." SciMLLogging))

println("hasdoc(Facade, :SciMLLogging) after       = ", Base.Docs.hasdoc(Facade, :SciMLLogging))
println("keys(Docs.meta(Facade))                   = ", collect(keys(Base.Docs.meta(Facade))))
println("SciMLLogging.SciMLLogging in meta(SciMLLogging)? = ",
    Base.Docs.Binding(SciMLLogging, :SciMLLogging) in keys(Base.Docs.meta(SciMLLogging)))

Output:

hasdoc(Facade, :SciMLLogging) before      = false
Docs.Binding(Facade, :SciMLLogging)       = SciMLLogging.SciMLLogging
hasdoc(Facade, :SciMLLogging) after       = true
keys(Docs.meta(Facade))                   = Any[]
SciMLLogging.SciMLLogging in meta(SciMLLogging)? = true

Docs.meta(Facade) stays empty and the entry lands in Docs.meta(SciMLLogging): the "local" docstring is a write into the dependency.

Where it bites

SciML/OrdinaryDiffEq.jl master. src/OrdinaryDiffEq.jl has export SciMLBase, SciMLLogging, remake, ..., so both module bindings are public API:

┌ Info: run_api_docs: public API names missing a docstring
│   pkg = OrdinaryDiffEq
│   undocumented =
│    2-element Vector{Symbol}:
│     :SciMLBase
└     :SciMLLogging
ERROR: LoadError: Some tests did not pass: 87 passed, 1 failed, 0 errored, 0 broken.
public API has docstrings: Test Failed at ~/.julia/packages/SciMLTesting/v2jEQ/src/SciMLTesting.jl:1205
  Expression: isempty(undocumented)
   Evaluated: isempty([:SciMLBase, :SciMLLogging])

Its docs/make.jl works around it with load-time piracy that only runs during the docs build, which is why the Documentation lane is green while the QA lane is red:

@eval SciMLBase @doc "The common interfaces and types used throughout the SciML ecosystem." SciMLBase
@eval SciMLLogging @doc "The common logging interface used by SciML solvers." SciMLLogging

Proposed fix

In the docstrings branch of run_api_docs, skip a public name whose value isa Module and is not owned by pkg's module hierarchy — the same ownership test _requires_local_rendering already applies. A package's own submodules would keep their docstring obligation; only re-exported foreign modules would be exempt, matching the documented "a re-exported name documented in its defining package counts as documented" intent (there is simply nothing to follow when the defining package has no module docstring).

Orthogonally, SciML/SciMLBase.jl and SciML/SciMLLogging.jl should grow real module docstrings; that would make this particular instance green without any exemption, and would let OrdinaryDiffEq drop the two @eval lines from docs/make.jl. But it does not fix the general case, since a re-exporting package cannot force its dependency to be documented and cannot raise a compat floor for a docstring.

Meanwhile OrdinaryDiffEq is using the documented per-repo escape hatch api_docs_kwargs = (; ignore = (:SciMLBase, :SciMLLogging)), which should be removed once this is resolved.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions