Reputation: 2692
I want to document Python object attributes with Sphinx. I understand I should use
:ivar varname: description
:ivar type varname: description
However I'm seeing a weird behaviour, that is Sphinx searches my project for the variable name and tries to create symlinks. E.g. this code:
class A(object):
"""
:ivar x: some description
"""
def __init__(self, x):
self.x = x
class B(object):
def x(self):
return 1
class C(object):
def x(self):
return 2
will cause this error:
module1.py:docstring of mylibrary.module1.A:None: WARNING: more than one target found for cross-reference u'x': mylibrary.module1.C.x, mylibrary.module1.B.x
Did I understand incorrectly the purpose or usage of :ivar?
Upvotes: 17
Views: 3293
Reputation: 3063
For those running older Sphinx versions than 4.0, here is a much simpler fix than the monkey-patch from @mzjn that was tested back to Sphinx 1.6.7 from Ubuntu 18.04. The fix basically just does the same thing that was done in PR #8638 that fixed the issue.
Put the following code into your conf.py:
from sphinx.domains.python import PyObject
PyObject.doc_field_types[map(lambda f: f.name == 'variable', PyObject.doc_field_types).index(True)].rolename = None
Upvotes: 0
Reputation: 6940
This is finally fixed in Sphinx 4.0.0.beta1 released on 2021-04-12.
The next stable release of Sphinx that contains it will be 4.0 or 4.1.
(Issue: #5977 , PR that fixed it: #8638, changelog entry for 4.0 beta 1: here)
Upvotes: 0
Reputation: 497
Here is a workaround provided by @acrisci
on github: prefix your variable name with ~.
. For example replace
:ivar float bandwidth: blah
with this:
:ivar float ~.bandwidth: blah
Source: https://github.com/sphinx-doc/sphinx/issues/2549#issuecomment-488896939
Upvotes: 3
Reputation: 50957
Here is a monkey patch (based on Sphinx 1.5.1) that disables ivar
cross-references. I'm not sure what the best solution is, so consider the patch an experimental suggestion. To try it out, add the code below to conf.py
.
from docutils import nodes
from sphinx.util.docfields import TypedField
from sphinx import addnodes
def patched_make_field(self, types, domain, items):
# type: (List, unicode, Tuple) -> nodes.field
def handle_item(fieldarg, content):
par = nodes.paragraph()
par += addnodes.literal_strong('', fieldarg) # Patch: this line added
#par.extend(self.make_xrefs(self.rolename, domain, fieldarg,
# addnodes.literal_strong))
if fieldarg in types:
par += nodes.Text(' (')
# NOTE: using .pop() here to prevent a single type node to be
# inserted twice into the doctree, which leads to
# inconsistencies later when references are resolved
fieldtype = types.pop(fieldarg)
if len(fieldtype) == 1 and isinstance(fieldtype[0], nodes.Text):
typename = u''.join(n.astext() for n in fieldtype)
par.extend(self.make_xrefs(self.typerolename, domain, typename,
addnodes.literal_emphasis))
else:
par += fieldtype
par += nodes.Text(')')
par += nodes.Text(' -- ')
par += content
return par
fieldname = nodes.field_name('', self.label)
if len(items) == 1 and self.can_collapse:
fieldarg, content = items[0]
bodynode = handle_item(fieldarg, content)
else:
bodynode = self.list_type()
for fieldarg, content in items:
bodynode += nodes.list_item('', handle_item(fieldarg, content))
fieldbody = nodes.field_body('', bodynode)
return nodes.field('', fieldname, fieldbody)
TypedField.make_field = patched_make_field
The original TypedField.make_field
method is here: https://github.com/sphinx-doc/sphinx/blob/master/sphinx/util/docfields.py.
Upvotes: 5
Reputation: 25244
There's an alternative with other advantages. Just define you member variables at class scope and document them with plain docstring. Later you can reference them with py:attr:
role. It's more readable, self-documented (yeah, I know this is under python-sphinx, but anyway) and introspection-friendly approach.
module.py
class A:
x = None
'''This way there's no issue. It is more readable and friendly
for class member introspection.'''
def __init__(self, x):
self.x = x
class B:
'''Something about :py:attr:`.A.x`'''
def x(self):
'''Method x of B'''
return 1
README.txt
****
Test
****
.. autoclass:: module.A
:members:
.. autoclass:: module.B
:members:
conf.py
extensions = ['sphinx.ext.autodoc']
source_suffix = '.txt'
master_doc = 'README'
project = 'Test'
pygments_style = 'sphinx'
html_theme = 'alabaster'
html_use_index = False
html_show_sourcelink = False
html_show_copyright = False
html_sidebars = {'**': ['localtoc.html']}
Build like PYTHONPATH=. sphinx-build . html
.
Upvotes: 1
Reputation: 5587
As mzjn referred there is an open issue for this SO post. In that thread there is also already a work-around for the issue posted. In sum, you use inline comments #:
instead of the docstring.
Take a look at the python.py
file in the commit referred by the user here. The docstring entries were removed (red lines), and he added inline comments in the constructor (green lines).
I have been looking for documentation on this but could not find it. For instance:
(...)
def __init__(self, function, fixtureinfo, config, cls=None, module=None):
#: access to the :class:`_pytest.config.Config` object for the test session
self.config = config
(...)
As noted by Nick Bastin this work-around renders completely differently from :ivar:
. There is no type support, and it always renders the default value.
Upvotes: 1