Search code examples
pythonopencvdocumentationpython-sphinxautodoc

How to properly write cross-references to external documentation with intersphinx?


I'm trying to add cross-references to external API into my documentation but I'm facing three different behaviors.

I am using sphinx(1.3.1) with Python(2.7.3) and my intersphinx mapping is configured as:

{
'python': ('https://docs.python.org/2.7', None),
'numpy': ('http://docs.scipy.org/doc/numpy/', None),
'cv2' : ('http://docs.opencv.org/2.4/', None),
'h5py' : ('http://docs.h5py.org/en/latest/', None)
}

I have no trouble writing a cross-reference to numpy API with :class:`numpy.ndarray` or :func:`numpy.array` which gives me, as expected, something like numpy.ndarray.

However, with h5py, the only way I can have a link generated is if I omit the module name. For example, :class:`Group` (or :class:`h5py:Group`) gives me Group but :class:`h5py.Group` fails to generate a link.

Finally, I cannot find a way to write a working cross-reference to OpenCV API, none of these seems to work:

:func:`cv2.convertScaleAbs`
:func:`cv2:cv2.convertScaleAbs`
:func:`cv2:convertScaleAbs`
:func:`convertScaleAbs`

How to properly write cross-references to external API, or configure intersphinx, to have a generated link as in the numpy case?


Solution

  • I gave another try on trying to understand the content of an objects.inv file and hopefully this time I inspected numpy and h5py instead of only OpenCV's one.

    How to read an intersphinx inventory file

    Despite the fact that I couldn't find anything useful about reading the content of an object.inv file, it is actually very simple with the intersphinx module.

    from sphinx.ext import intersphinx
    import warnings
    
    
    def fetch_inventory(uri):
        """Read a Sphinx inventory file into a dictionary."""
        class MockConfig(object):
            intersphinx_timeout = None  # type: int
            tls_verify = False
    
        class MockApp(object):
            srcdir = ''
            config = MockConfig()
    
            def warn(self, msg):
                warnings.warn(msg)
    
        return intersphinx.fetch_inventory(MockApp(), '', uri)
    
    
    uri = 'http://docs.python.org/2.7/objects.inv'
    
    # Read inventory into a dictionary
    inv = fetch_inventory(uri)
    # Or just print it
    intersphinx.debug(['', uri])
    

    File structure (numpy)

    After inspecting numpy's one, you can see that keys are domains:

    [u'np-c:function',
     u'std:label',
     u'c:member',
     u'np:classmethod',
     u'np:data',
     u'py:class',
     u'np-c:member',
     u'c:var',
     u'np:class',
     u'np:function',
     u'py:module',
     u'np-c:macro',
     u'np:exception',
     u'py:method',
     u'np:method',
     u'np-c:var',
     u'py:exception',
     u'np:staticmethod',
     u'py:staticmethod',
     u'c:type',
     u'np-c:type',
     u'c:macro',
     u'c:function',
     u'np:module',
     u'py:data',
     u'np:attribute',
     u'std:term',
     u'py:function',
     u'py:classmethod',
     u'py:attribute']
    

    You can see how you can write your cross-reference when you look at the content of a specific domain. For example, py:class:

    {u'numpy.DataSource': (u'NumPy',
      u'1.9',
      u'http://docs.scipy.org/doc/numpy/reference/generated/numpy.DataSource.html#numpy.DataSource',
      u'-'),
     u'numpy.MachAr': (u'NumPy',
      u'1.9',
      u'http://docs.scipy.org/doc/numpy/reference/generated/numpy.MachAr.html#numpy.MachAr',
      u'-'),
     u'numpy.broadcast': (u'NumPy',
      u'1.9',
      u'http://docs.scipy.org/doc/numpy/reference/generated/numpy.broadcast.html#numpy.broadcast',
      u'-'),
      ...}
    

    So here, :class:`numpy.DataSource` will work as expected.

    h5py

    In the case of h5py, the domains are:

    [u'py:attribute', u'std:label', u'py:method', u'py:function', u'py:class']
    

    and if you look at the py:class domain:

    {u'AttributeManager': (u'h5py',
      u'2.5',
      u'http://docs.h5py.org/en/latest/high/attr.html#AttributeManager',
      u'-'),
     u'Dataset': (u'h5py',
      u'2.5',
      u'http://docs.h5py.org/en/latest/high/dataset.html#Dataset',
      u'-'),
     u'ExternalLink': (u'h5py',
      u'2.5',
      u'http://docs.h5py.org/en/latest/high/group.html#ExternalLink',
      u'-'),
     ...}
    

    That's why I couldn't make it work as numpy references. So a good way to format them would be :class:`h5py:Dataset`.

    OpenCV

    OpenCV's inventory object seems malformed. Where I would expect to find domains there is actually 902 function signatures:

    [u':',
     u'AdjusterAdapter::create(const',
     u'AdjusterAdapter::good()',
     u'AdjusterAdapter::tooFew(int',
     u'AdjusterAdapter::tooMany(int',
     u'Algorithm::create(const',
     u'Algorithm::getList(vector<string>&',
     u'Algorithm::name()',
     u'Algorithm::read(const',
     u'Algorithm::set(const'
     ...]
    

    and if we take the first one's value:

    {u'Ptr<AdjusterAdapter>': (u'OpenCV',
      u'2.4',
      u'http://docs.opencv.org/2.4/detectorType)',
      u'ocv:function 1 modules/features2d/doc/common_interfaces_of_feature_detectors.html#$ -')}
    

    I'm pretty sure it is then impossible to write OpenCV cross-references with this file...

    Conclusion

    I thought intersphinx generated the objects.inv based on the content of the documentation project in an standard way, which seems not to be the case. As a result, it seems that the proper way to write cross-references is API dependent and one should inspect a specific inventory object to actually see what's available.