Compatibility with Sphinx

Info

This section goes into some fairly technical details. You do not need to read it unless you use both Documenter and Sphinx and plan to link both ways between Julia and Python projects.

DocumenterInterLinks is interoperable with Sphinx and Intersphinx: The InterLinks object can refer to the objects.inv file that Sphinx automatically writes out for every project. This makes it possible to easily link to virtually every Python project (as well as any other C/C++/Fortran project that uses Sphinx for its documentation).

The possible specification :domain:role:`name` in an @extref link mimics the cross-referencing syntax in Sphinx. However, Sphinx reStructuredText is much more explicit than Documenter's markdown syntax. In particular, the domain and role are required for every reference (although projects can set up a default domain, usually py, which can then be omitted). This is not the case in the @extref syntax defined by DocumenterInterLinks, where domain and role are for disambiguation only and can (and usually should) be omitted.

Moreover, domains and roles must be formally defined in Sphinx. In fact, Sphinx makes a distinction between "type" and "role". Strictly speaking, the objects.inv file records an "object type", like function or module, which DocInventories.InventoryItem reads in as role. A Sphinx domain then defines "roles" on top of that which are used for referencing object. The formal definition of the domain includes a mapping between an object type and one or more roles. Consider for example the code of the PythonDomain, which defines an object type function with associated roles func and obj. In contrast, DocumenterInterLinks has no formally defined domains and makes no distinction between object types and roles. Thus, the inventory item links["matplotlib"][":py:function:`matplotlib.get_backend`"] would be referenced as :py:func:`matplotlib.get_backend` (using :func:, not :function:!) or :py:obj:`matplotlib.get_backend` in Sphinx, but as [`get_backend`](@extref :py:function:`matplotlib.get_backend`) in DocumenterInterLinks, or more simply without any domain or role as [`matplotlib.get_backend`](@extref).

Note that Sphinx has a "fallback" feature similar to the one implemented for Documenter ≥ v1.3.0 via ExternalFallbacks, see Fallback Resolution. In fact, that fallback is used extensively in Sphinx, preferring shorter references like :py:class:`zipfile.ZipFile` in lieu of the proper :external: role. This approach is not recommended in DocumenterInterLinks. Never use an @ref link when the link target is known to be an @extref! When this is not possible, use explicit mappings in the ExternalFallbacks plugin.

Referencing the Julia domain in Sphinx

The formal nature of Sphinx domains also has consequences for referencing Julia objects from within a Sphinx project. Linking from a project using Sphinx as a documentation generator to a Julia project using Documenter and the automatic inventory generation provided by DocumenterInterLinks will not work out of the box. This is because Sphinx does not know about the jl domain. In this sense, the jl domain is considered "ad-hoc".

There is a Sphinx-Julia package, but that package is currently not functional, and only partially supports the object types / roles used here in The Julia Domain.

Thus, any Sphinx project that wants to link to inventory items in the jl domain must first formally specify that domain. This could be done by adding the following code to the Sphinx conf.py file (or an extension):

from sphinx.domains import Domain, ObjType
from sphinx.roles import XRefRole


class JuliaDomain(Domain):
    """A minimal Julia language domain."""

    name = 'jl'
    label = 'Julia'
    object_types = {
        # name => (localized name, *roles)
        'macro': ObjType('macro', 'macro', 'obj'),
        'keyword': ObjType('keyword', 'keyword', 'obj'),
        'function': ObjType('function', 'func', 'function', 'obj'),
        'method': ObjType('method', 'meth', 'method', 'obj'),
        'type': ObjType('type', 'type', 'obj'),
        'module': ObjType('module', 'mod', 'module', 'obj'),
        'constant': ObjType('constant', 'const', 'constant', 'obj'),
    }

    roles = {
        'macro': XRefRole(),
        'keyword': XRefRole(),
        'function': XRefRole(fix_parens=True),
        'func': XRefRole(fix_parens=True),
        'method': XRefRole(),
        'meth': XRefRole(),
        'type': XRefRole(),
        'module': XRefRole(),
        'mod': XRefRole(),
        'constant': XRefRole(),
        'const': XRefRole(),
        'obj': XRefRole(),
    }


def setup(app):
    app.add_domain(JuliaDomain)

We have used Sphinx' Domain API here to define the object types matching our Julia Domain. For each object type, we define a role of the same name, as well as abbreviated roles in line with Sphinx' usual conventions, such as :func: as a shorthand for :function: and obj for any type.

See sphinx-to-documenter-links for a full example of a Python project with documentation linking to the documentation of Julia projects.