I am documenting a project with Sphinx I want to create a specialized version of the autoclass:: directive that allows me to modify the documentation string for certain classes.
Here is one thing I have tried: searching the sphinx source, I found that the autoclass directive is created via the ClassDocumenter object. Following this, my idea was to register a subclass of ClassDocumenter for the classes of interest, and then override get_doc to modify the docstring.
Here's my attempt at such an extension:
from six import class_types
from sphinx.ext.autodoc import ClassDocumenter
from testmodule import Foo # the class that needs modified documentation
class MyClassDocumenter(ClassDocumenter):
objtype = 'myclass'
priority = 20 # higher priority than ClassDocumenter
#classmethod
def can_document_member(cls, member, membername, isattr, parent):
return isinstance(member, class_types) and issubclass(member, Foo)
def get_doc(self, encoding=None, ignore=1):
doc = super(MyClassDocumenter, self).get_doc(encoding, ignore)
# do something to modify the output documentation
doc[0].insert(0, "ADD SOMETHING TO THE DOC")
return doc
def setup(app):
app.add_autodocumenter(MyClassDocumenter)
The problem is, when I run this I get an error: ERROR: Unknown directive type "py:myclass". It seems that registering a documenter is not enough to register the associated directive, but I've not been able to find any clues in the sphinx source to tell me how such a registration is supposed to happen. It's not as simple as using the standard add_directive() methods, because I have no explicit directive to register.
How can I correctly accomplish such a specialization of an auto-documenter in sphinx?
(note: the full set of files to reproduce the error is available in this gist)
I have found something in Docutils markup API
Directives are handled by classes derived from docutils.parsers.rst.Directive. They have to be registered by an extension using Sphinx.add_directive() or Sphinx.add_directive_to_domain().
Is it what you are looking for?
Documenter classes require an existing directive to apply, when appropriate. This can either be a "built in" directive, or it can be a custom directive you create and register. Either way, the directive to use is specified by the directivetype class attribute. You can specify this value explicitly:
class MyClassDocumenter(ClassDocumenter):
directivetype = 'foo' # corresponds to :foo: in the doc source
objtype = 'bar'
priority = 20
Or, it looks like you can omit directivetype and it will use the same value as objtype by default. (This is an educated guess)
So then the question is: have you registered a new directive for :myclass:? If not I think that is the problem. Alternatively, if there is some other directive that you want to be used (whether built-in, or custom) the answer is probably to specify it explicitly by including a value for directivetype in your Documenter class definition.
Related
import util
class C():
save = util.save
setattr(C, 'load', util.load)
C.save is visible to the linter - but C.load isn't. There's thus some difference between assigning class methods from within the class itself, and from outside. Same deal for documentation builders; e.g. Sphinx won't acknowledge :meth:C.load - instead, need to do :func:util.load, which is misleading if load is meant to be C's method. An IDE (Spyder) also fails to "go to" method via self.load code.
The end-goal is to make linter (+docs & IDE) recognize load as C's method just like C.save is, but class method assignment needs to be dynamic (context). Can this be accomplished?
Note: the purpose of dynamic assignment is to automatically pull methods from modules (e.g. util) instead of having to manually update C upon method addition / removal.
Disclaimer: This solution does not work in all use cases, see comments. I leave it here, since it might still be useful under some circumstances.
I don't know about the linter and Sphinx, but an IDE like PyCharm will recognize the method if you declare it upfront using type hinting. In the code below, without the line load: Callable[[], None], I get the warning 'Unresolved attribute reference', but with the line there are no warnings in the file. Check the docs for more information about type hinting.
Notes:
Even with the more general load: Callable, the type checker is satisfied.
If you don't always declare a callable, a valid declaration is load: Optional[Callable] = None. This means that the default value is None. If you then call it without setting it, you will get an error, but you got that already anyway, that's unrelated to this typing.
p.s. I don't have your utils, so I defined some functions in the file itself.
from typing import Callable
def save():
pass
def load():
pass
class C:
load: Callable[[], None]
save = save
setattr(C, 'load', load)
C.load()
In one module, I have code such as:
class ConfigParsers:
def str(raw, key):
# parse value as a string
# other parsers
class Section(dict):
def add_parsers(name, parsers):
"""Add a parser
:param str name: the name of the object
...
...
When sphinx builds the documentation for this module using autodoc, "str" gets linked to the current pages' ConfigParsers.str rather than the python documention. This occurs with other built in types where the name collision is within a class, such as int and bool. I can understand the collision if the parsers were defined at the module level, however Sphinx is forming a collision even when the definition is within a class, like above.
Edit:
A work around I have just found entails using the new intersphinx mapping format, and then using identifierusedforstdlibcode:str, however this isn't ideal, and normally wouldn't be an issue as sphinx usually autolinks it already to an extent.
I want to include the docstrings for __init__() in my sphinx-generated documentation.
I was following the accepted answer to this stackoverflow question to add a handler for autodoc-skip-member and was still unable to see my __init__() documentation. Trace code inside the if name == "__init__": block shows I am hitting that code.
On a hunch I removed 'sphinx.ext.napoleon' from my extensions definition, leaving
extensions = [
'sphinx.ext.autodoc',
# 'sphinx.ext.napoleon',
]
and then I can see the __init__() documentation.
The only thing I see in the napoleon documentation that seems relevant is napoleon_include_special_with_doc, which it says defaults to True. Explicitly setting it to True in conf.py doesn't seem to change anything.
ETA: If I add the following method:
def __blah__(self):
'''blah blah blah'''
print self.__class__
I see __blah__() in my generated documentation.
If I change the name of __blah__ to __repr__ or __str__,
I see them in the generated documentation.
If I comment out the existing __init__ and change
__blah__ to __init__ I don't see it.
So it seems specific to __init__().
Is this a known issue, and is there another way to control this when using napoleon?
Napoleon defers to your autodoc configuration for how you want to handle the __init__ method.
Check your autodoc settings in conf.py. In particular, make sure autoclass_content is set to either init or both.
Per Rob's followon at https://github.com/sphinx-doc/sphinx/issues/2374, if you're using any extension that also sets a handler for the "autodoc-skip-member" event only one of the handlers will be used. This would seem to be the issue at hand. Thanks Rob!
The Sphinx documentation at http://www.sphinx-doc.org/en/stable/domains.html#cross-referencing-python-objects says,
:py:func: Reference a Python function; dotted names may be used. The role text needs not include trailing parentheses to enhance readability;
they will be added automatically by Sphinx if the
add_function_parentheses config value is True (the default).
:py:meth:
Reference a method of an object. The role text can include the type name and the method name; if it occurs within the description of
a type, the type name can be omitted. A dotted name may be used.
But I could not find any difference in the way they behave.
Here is my Python module for which I generated documentation.
"""foo module."""
def hello(name):
"""Print hello addressed to *name*.
Args:
name (str): Name to address.
"""
print('hello', name)
class Foo:
"""Foo class."""
def bye(self, name):
"""Print bye addressed to *name*.
Args:
name (str): Name to address.
"""
print('bye', name)
if __name__ == '__main__':
hello('world')
Foo().bye('python')
This is what I have in my index.rst file.
Foo Documentation
=================
See :func:`foo.hello` and :func:`foo.Foo.bye`.
Also, see :meth:`foo.hello` and :meth:`foo.Foo.bye`.
foo module
==========
.. automodule:: foo
:members:
After performing a make html, this is the output I see.
Both :func: and :meth: roles have generated valid cross-referencing hyperlinks to hello and Foo.bye regardless of whether the target is a function or a method.
What then is the difference between :func: and :meth: roles. Can you provide an example for which they behave differently?
I've looked at Sphinx code. The only difference I was able to discern is that each of the roles generate HTML elements whose HTML class includes the name of the role that created it. For instance, the code element for a :func: role will look like this:
<code class="xref py py-func docutils literal">
Whereas for a :meth: role, it would have py-meth instead of py-func. The stock CSS style included with Sphinx does not distinguish between py-meth and py-func but it would be possible to have a stylesheet that styles them differently.
For kicks I've tried other roles (e.g. class) and had them point to methods on objects. Sphinx had no problem with it even if it made no sense.
There is at least one difference, in terms of functionality.
Whenever you use the autoclass syntax (. in front of the class name), to automatically resolve the full class name:
:meth:`.myClass` limits the search scope to the current module.
:func:`.myClass` also resolves external classes.
It is semantic information that is used in the generated index for instance to label something as function or method. As Louis already mentioned it would be possible to style them differently in HTML via CSS.
I have a module, errors.py in which several global constants are defined (note: I understand that Python doesn't have constants, but I've defined them by convention using UPPERCASE).
"""Indicates some unknown error."""
API_ERROR = 1
"""Indicates that the request was bad in some way."""
BAD_REQUEST = 2
"""Indicates that the request is missing required parameters."""
MISSING_PARAMS = 3
Using reStructuredText how can I document these constants? As you can see I've listed a docstring above them, but I haven't found any documentation that indicates to do that, I've just done it as a guess.
Unfortunately, variables (and constants) do not have docstrings. After all, the variable is just a name for an integer, and you wouldn't want to attach a docstring to the number 1 the way you would to a function or class object.
If you look at almost any module in the stdlib, like pickle, you will see that the only documentation they use is comments. And yes, that means that help(pickle) only shows this:
DATA
APPEND = b'a'
APPENDS = b'e'
…
… completely ignoring the comments. If you want your docs to show up in the built-in help, you have to add them to the module's docstring, which is not exactly ideal.
But Sphinx can do more than the built-in help can. You can configure it to extract the comments on the constants, or use autodata to do it semi-automatically. For example:
#: Indicates some unknown error.
API_ERROR = 1
Multiple #: lines before any assignment statement, or a single #: comment to the right of the statement, work effectively the same as docstrings on objects picked up by autodoc. Which includes handling inline rST, and auto-generating an rST header for the variable name; there's nothing extra you have to do to make that work.
As a side note, you may want to consider using an enum instead of separate constants like this. If you're not using Python 3.4 (which you probably aren't yet…), there's a backport.enum package for 3.2+, or flufl.enum (which is not identical, but it is similar, as it was the main inspiration for the stdlib module) for 2.6+.
Enum instances (not flufl.enum, but the stdlib/backport version) can even have docstrings:
class MyErrors(enum.Enum):
"""Indicates some unknown error."""
API_ERROR = 1
"""Indicates that the request was bad in some way."""
BAD_REQUEST = 2
"""Indicates that the request is missing required parameters."""
MISSING_PARAMS = 3
Although they unfortunately don't show up in help(MyErrors.MISSING_PARAMS), they are docstrings that Sphinx autodoc can pick up.
If you put a string after the variable, then sphinx will pick it up as the variable's documentation. I know it works because I do it all over the place. Like this:
FOO = 1
"""
Constant signifying foo.
Blah blah blah...
""" # pylint: disable=W0105
The pylint directive tells pylint to avoid flagging the documentation as being a statement with no effect.
This is an older question, but I noted that a relevant answer was missing.
Or you can just include a description of the constants in the docstring of the module via .. py:data::. That way the documentation is also made available via the interactive help. Sphinx will render this nicely.
"""
Docstring for my module.
.. data:: API_ERROR
Indicates some unknown error.
.. data:: BAD_REQUEST
Indicates that the request was bad in some way.
.. data:: MISSING_PARAMS
Indicates that the request is missing required parameters.
"""
You can use hash + colon to document attributes (class or module level).
#: Use this content as input for moo to do bar
MY_CONSTANT = "foo"
This will be picked up by some document generators.
An example here, could not find a better one: Sphinx document module properties
the following worked for me with Sphinx 2.4.4:
in foo.py :
API_ERROR = 1
"""int: Indicates some unknown error."""
then to document it:
.. automodule:: foo.py
:members:
I think you're out of luck here.
Python don't support directly docstrings on variables: there is no attribute that can be attached to variables and retrieved interactively like the __doc__ attribute on modules, classes and functions.
Source.
The Sphinx Napoleon Python documentation extension allows to document module-level variables in an Attributes section.
Per https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_numpy.html :
Attributes
----------
module_level_variable1 : int
Module level variables may be documented in either the ``Attributes``
section of the module docstring, or in an inline docstring immediately
following the variable.
Either form is acceptable, but the two should not be mixed. Choose
one convention to document module level variables and be consistent
with it.
Writing only because I haven't seen this option in the answers so far:
You can also define your constants as functions that simply return the desired constant value when called, so for example:
def get_const_my_const() -> str:
"""Returns 'my_const'."""
return "my_const"
This way they'll be a bit "more constant" on one hand (less worrying about reassignment) and they'll also provide the opportunity for regular documentation, as with any other function.