JuliaDocs / Documenter.jl

A documentation generator for Julia.
https://documenter.juliadocs.org
MIT License
808 stars 475 forks source link

Should be able to alias full paths to modules #1781

Open frankier opened 2 years ago

frankier commented 2 years ago

It should be possible to alias the full paths to modules as generated by autodocs. The general reason is that this gives more flexibility for packge authors to display their public interface as they see it. The specific reason is that currently https://github.com/Roger-luo/FromFile.jl inserts modules that are essentially a private part of its implementation into documentation. It would be nice to have a way to work around this.

For an example, see https://astroautomata.com/SymbolicRegression.jl/stable/types/ . Notice the file paths in the module paths.

mortenpi commented 2 years ago

Does FromFile.jl evaluate the definitions into a generated submodule with a convoluted name?Documenter tries to reflect things as they are, so if a binding lives in a strange submodule, Documenter reflects that. Arguably, FromFile should place the bindings into the top-level module if that is the intended public API.

That said, it might be possible to attach docstrings to different bindings somehow, as they are just stored in a special dictonary object in each module (which you can access via Docs.meta(MyModule)). That way Documenter wouldn't need to do anything special, and I think this would be preferred as then the information about the "aliasing" is stored with the docstring directly.

nsajko commented 2 months ago

IMO this is a serious issue, it's misrepresentation of user package API.

For example, in my package CallableExpressions.jl, I have some doc strings in the top-level module, where the names are imported from private modules. The generated documentation misleadingly mentions the names of the private modules.

The first doc string, for example, is for DynamicExpression: https://gitlab.com/nsajko/CallableExpressions.jl/-/blob/656b3e70536a7b815f49630d6aa0e4a8315964c8/src/CallableExpressions.jl#L57-62

The generated docs are titled CallableExpressions.DynamicExpressions.DynamicExpression instead of CallableExpressions.DynamicExpression.

Also affects the docs of Julia itself, resulting in breakage for users: https://github.com/JuliaLang/julia/issues/54534

xref https://github.com/JuliaDocs/Documenter.jl/issues/2087?

fredrikekre commented 2 months ago
julia> CallableExpressions.DynamicExpression === CallableExpressions.DynamicExpressions.DynamicExpression
true
nsajko commented 2 months ago

Yes but the doc string is not defined in DynamicExpressions. If that counts for something.

nsajko commented 2 months ago

Also:

julia> Base.ispublic(CallableExpressions, :DynamicExpression)
true

julia> Base.ispublic(CallableExpressions, :DynamicExpressions)
false
fredrikekre commented 2 months ago

Yes but the doc string is not defined in DynamicExpressions. If that counts for something.

You attach the docstring to CallableExpressions.DynamicExpressions.DynamicExpression.

Also: [...]

Why does that matter?

nsajko commented 2 months ago

Why does that matter?

It matters because I want only the public API to be documented.

goerz commented 2 months ago

I sympathize with wanting to document functions under an aliased name. I have the same problem in QuantumControl: there are a lot of things defined in QuantumControlBase and re-exported in QuantumControl. Ideally, the QuantumControlBase should be more or less hidden from users. For example, I'd prefer if users used QuantumControl.ControlProblem, not QuantumControlBase.ControlProblem, even though the latter is the canonical name.

However, from Julia's perspective, each function/type has one and only one canonical fully specified name, and that's the one that Documenter shows. So basically, we'd have to tell Documenter to lie about the canonical name. I don't think there's any way to come up with an automatic way to do this. Certainly, Julia has no way to determine where the docstring for CallableExpressions.DynamicExpression was attached. It might work to define a mapping canonical path -> canonical alias for Documenter as a dict that gets passed as an argument to makedocs. Then, when Documenter renders docstrings, it would show the desired alias instead of canonical path. I'm not sure if this would be easy to implement. At first glance, it seems like it would, but one would have to be careful not to mess with the @ref-resolution, or anything like that.

goerz commented 2 months ago

By the way, how I (somewhat) get around this limitation is to have a script that generates an API overview with the "canonical aliases": https://juliaquantumcontrol.github.io/QuantumControl.jl/stable/api/quantum_control/.

That page describes the API as I'd like people to use it, but if you click on any of the actual items, you'll still see the canonical path, e.g. QuantumControl.ControlProblem or QuantumControls.Controls.evaluate, both of which have their canonical location in other packages.

mortenpi commented 2 months ago

Certainly, Julia has no way to determine where the docstring for CallableExpressions.DynamicExpression was attached.

Actually, technically, I think we do have that metadata (module and/or file). IIRC, we use it to determine if a docstring for e.g. a Base function/type comes from Base or from a package extending that function.

fredrikekre commented 2 months ago

You can also just document a new binding in the correct module that is const equal the "internal" one.

tecosaur commented 2 weeks ago

You can also just document a new binding in the correct module that is const equal the "internal" one.

This does seem to cause undesirable difference in printing though. Compare:


julia> module Foo
       module Internal
       struct Bar end
       end
       using .Internal
       const Bar = Internal.Bar
       export Bar
       end
Main.Foo

julia> using .Foo

julia> Bar
Main.Foo.Internal.Bar

vs.


julia> module Foo
       module Internal
       struct Bar end
       end
       using .Internal: Bar
       export Bar
       end
Main.Foo

julia> using .Foo

julia> Bar
Bar