Documenting class-level variables in Python - python-sphinx

I am trying to document a Python class that has some class-level member variables, but I could not get it appropriately documented using reST/Sphinx.
The code is this:
class OSM:
"""Some blah and examples"""
url = 'http://overpass-api.de/api/interpreter' # URL of the Overpass API
sleep_time = 10 # pause between successive queries when assembling OSM dataset
But I get this output (see the green circled area, where I would like to have some text describing both variables, as above).
I apologize for the blurring, but part of the example is somewhat sensitive

You have several options to document class level variables.
Put a comment that starts with #: before the variable, or on the same line. (Uses only autodoc.)
Perhaps the easiest choice. If needed you can customize the value using :annotation: option. If you want to type-hint the value use #: type:.
Put a docstring after the variable.
Useful if the variable requires extensive documentation.
For module data members and class attributes, documentation can either be put into a comment with special formatting (using a #: to start the comment instead of just #), or in a docstring after the definition. Comments need to be either on a line of their own before the definition, or immediately after the assignment on the same line. The latter form is restricted to one line only.
Document the variable in the class docstring. (Uses sphinx-napoleon extension, shown in the example.)
This has the drawback that the variable's value will be omitted. Since it's a class level variable your IDE's static type checker may complain if you don't prefix the variable with cls. or class_name.. The distinction is however convenient because instance variables can also be documented in the class docstring.
The following example shows all three options. The .rst has additional complexity to illustrate the neededautodoc functionalities. Type hints were included in all cases but can also be omitted.
class OSM:
"""Some blah and examples"""
#: str: URL of the Overpass API.
url = 'http://overpass-api.de/api/interpreter'
#: int: pause between successive queries when assembling OSM dataset.
sleep_time = 10
class OSM2:
"""Some blah and examples.
Attributes:
cls.url (str): URL of the Overpass API.
"""
url = 'http://overpass-api.de/api/interpreter'
sleep_time = 10
"""str: Docstring of sleep_time after the variable."""
Corresponding .rst
OSM module
==========
.. automodule:: OSM_module
:members:
:exclude-members: OSM2
.. autoclass:: OSM2
:no-undoc-members:
:exclude-members: sleep_time
.. autoattribute:: sleep_time
:annotation: = "If you want to specify a different value from the source code."
The result:

This also works, if you're ok with suppressing a silly linter rule.
class OSM2:
sleep_time = 10; """str: Inline docstring of sleep_time after the variable."""

Related

Bullet list style for single parameter functions in RTD theme using autodoc and Sphinx?

I've noticed that when I use autodoc with the ReadTheDoc theme, if I have multiple arguments in my functions they are listed in a bullet list style:
arg1
arg2
...
but if there is only 1 argument then it is not using the bullet list style which is a bit silly to me since it breaks the continuity of the design.
I've found how to remove the disc via CSS to make things more uniform but I actually want to do the opposite and have the disk for the single argument functions.
At this point, I'm not sure it is a CSS change and I do not know how to do that.
I've also noticed the same thing in different docs.
Here is the rendered html:
Here are the 2 methods:
def add_attribute(self, name, index):
"""
:param name: The name attached to the attribute.
:param index: The position of the attribute within the list of attributes. """
print("")
def delete_attribute(self, name):
"""
:param name: The name of the attribute to delete."""
print("")
Here is the my .rst:
API
----------------
.. automodule:: my_module
:members:
Here is the conf.py
extensions = [
'sphinx_rtd_theme',
'sphinx.ext.autodoc',
'sphinx.ext.napoleon',
'sphinx.ext.coverage',
'sphinx.ext.autosummary',
]
templates_path = ['_templates']
language = 'python'
exclude_patterns = []
html_theme = "sphinx_rtd_theme"
html_static_path = ['_static']
autosummary_generate = True
Any idea?
Cheers!
After a lot of digging, I've found a partial workaround for this.
My solution involves manually editing the produced HTML files to insert the missing bullet points.
Required conf.py changes:
# Register hook to run when build is complete
def setup(app):
app.connect('build-finished', on_build_finished)
# Hook implementation
def on_build_finished(app, exception):
add_single_param_bullets("_build/html/index.html")
# Function to actually add the bullet points by overwriting the given HTML file
def add_single_param_bullets(file_path):
print('Add single parameter bullets in {:s}'.format(file_path))
if not os.path.exists(file_path):
print(' File not found, skipping...')
return
lines_enc = []
with open(file_path, 'rb') as f:
for l in f.readlines():
# Check for html that indicates single parameter function
if b'<dd class="field-odd"><p><strong>' in l:
# Work out the encoding if not defined
enc = None
if enc is None:
import chardet
enc = chardet.detect(l)['encoding']
# Decode html and get the parameter information that needs adding
l_dec = l.decode(enc)
l_insert = l_dec.replace('<dd class="field-odd">', '').replace('\r\n', '')
# Add new encoded lines to output
lines_enc.append('<dd class="field-odd"><ul class="simple">'.encode('utf=8'))
lines_enc.append('<li>{:s}</li>'.format(l_insert).encode(enc))
lines_enc.append('</ul>'.encode('utf=8'))
else:
lines_enc.append(l)
# Overwrite the original file with the new changes
with open(file_path, 'wb') as f:
for l in lines_enc:
f.write(l)
In my case, I only have single argument functions in index.html. However, you can register additional files in on_build_finished.
A few things to note:
This only edits the produced HTML files, and doesn't actually solve the underlying problem. I dug through the source for a bit but couldn't find why the bullet points aren't added for single parameter function.
The problem is not just for the RTD theme. It seems to occur with the basic theme as well. So I suspect it's a deeper problem with Sphinx rather than the RTD theme.
The code above somewhat deals with different encodings in the original HTML.
This does not work on the RTD website. As the HTML files are edited in place, and the RTD build outputs the HTML files to a different directory, this solution doesn't seem to work on the RTD website. This is quite annoying. A solution would be to somehow change the RTD build process, or tell RTD to use pre-built HTML sources rather than building its own, but I don't know how to do so.
After spending a few hours working all this out, I actually think it looks better without the bullet points...

Generate structured documentation from commented code

How do i get Asciidoc(tor) to generate eg. a nice overall function description out of several code comments and some code, including the function signature, without butchering my code with tags?
AFAIK Asciidoc only supports external includes in its Asciidoc file via surrounding tags in the code like
# tag::mytag[]
<CODE TO INCLUDE HERE>
# end::mytag[]
which would be quite noisy around every describing comment within a single function body and around every function signature.
Maybe there is an exotic, less verbose way like marking the single line comments like #! and single line tags that tells Asciidoctor to read only a single line relative to these tags.
Consider this tiny example.
def uber_func(to_uber: str) -> str:
"""
This is an overall description. Delivers some context.
"""
# Trivial code here
# To uber means <include code below>
result = to_uber + " IS SOOO " + to_uber + "!!!"
# Trivial code here
# Function only returns upper case.
return result.upper()
My naive Asciidoc approach to include all meaningfull comments, the docstring and the function signature from the code above would look awefull, plus, Asciidoc doesn't recognize and remove comment marks, so the resulting documentation might not be so pretty too.
Instead of this very ugly
# tag::uber_func[]
def uber_func(to_uber: str) -> str:
"""
This is an overall description. Delivers some context.
"""
# end::uber_func[]
# Trivial code here
# tag::uber_func[]
# To uber means
result = to_uber + " IS SOOO " + to_uber + "!!!"
# end::uber_func[]
# Trivial code here
# tag::uber_func[]
# Function only returns upper case.
# end::uber_func[]
return result.upper()
I would like to use some thing like (pseudo):
def uber_func(to_uber: str) -> str:
# tag::uber_func[readline:-1,ignore-comment-marks,doc-comment:#!]
#! This is an overall description. Delivers some context.
# Trivial code here
#! To uber means
# tag::uber_func[readline:+1]
result = to_uber + " IS SOOO " + to_uber + "!!!"
# Trivial code here
#! Function only returns upper case.
return result.upper()
# end::uber_func[]
I think the general issue is, that Asciidoc is merely a text formatting tool, which means, if i want it to generate a structured documentation mostly from my code, i would need to provide this structe in my code and in my .adoc file.
Documentation generators like Doxygen on the other side recognize this structure and the documenting comments automatically.
I value this feature very much, that some generators allow you to write code and pretty documentation side by side, which lowers the overall effort alot.
If Asciidoc doesn't allow me to do this in a reasonable way, i will have look for something else.
I think you would have to write a scraper that puts the comments into a structure, then pull that structure into your AsciiDoc. This way the comments can be internally formatted with AsciiDoc markup, and you can output it in Asciidoctor-generated documents, but you won't need Asciidoctor to read the source files directly.
I would try a system of using one # for non-publishing comments and ## for ones you wish to publish, or vice versa, or append a # to the ones that are for docs publishing. As well as those denoted by the """ notation. Then your scraper can read the block name (uber_func or whatever portion is important) and then scrape the keeper comments and all the literal code, arranging them all in a file. The below file has seen most comments tagged as text, non-keeper comments dropped, and non-comment content as code:
# tag::function__uber_func[]
# tag::function__uber_func_form[]
uber_func(to_uber: str) -> str:
# end::function__uber_func_form[]
# tag::function__uber_func_desc[]
This is an overall description. Delivers some context.
# end::function__uber_func_desc[]
# tag::function__uber_func_body[]
# tag::function__uber_func_text[]
To uber means
# end::function__uber_func_text[]
# tag::function__uber_func_code[]
----
result = to_uber + " IS SOOO " + to_uber + "!!!"
----
# end::function__uber_func_code[]
# tag::function__uber_func_text[]
Function only returns upper case.
# end::function__uber_func_text[]
# tag::function__uber_func_code[]
----
return result.upper()
----
# end::function__uber_func_code[]
# end::function__uber_func[]
I know this looks hideous, but it is super useful to an AsciiDoc template. For instance, use just:
uber_func::
include::includes/api-stuff.adoc[tags="function__uber_func_form"]
+
include::includes/api-stuff.adoc[tags="function__uber_func_desc"]
+
include::includes/api-stuff.adoc[tags="function__uber_func_body"]
This would be even better if you parse it to a data format (like JSON or YAML) and then press it into AsciiDoc template dynamically. But you could maintain something like the above if it was not too massive. At a certain size (20+ such records?) you need an intermediary datasource (an ephemeral data file produced by the scraping), and at a certain larger scale (> 100 code blocks/endpoints?), you likely need a system that specializes in API documentation, such as Doxygen, et al.

Module variable documentation error

I get the following error while documenting a module variable json_class_index (See source), which does not have a docstring.
The generated documentation seems to be fine. What is a good fix?
reading sources... [100%] sanskrit_data_schema_common
/home/vvasuki/sanskrit_data/sanskrit_data/schema/common.py:docstring of sanskrit_data.schema.common.json_class_index:3: WARNING: Unexpected indentation.
/home/vvasuki/sanskrit_data/sanskrit_data/schema/common.py:docstring of sanskrit_data.schema.common.json_class_index:4: WARNING: Block quote ends without a blank line; unexpected unindent.
/home/vvasuki/sanskrit_data/sanskrit_data/schema/common.py:docstring of sanskrit_data.schema.common.json_class_index:7: WARNING: Unexpected indentation.
/home/vvasuki/sanskrit_data/sanskrit_data/schema/common.py:docstring of sanskrit_data.schema.common.json_class_index:8: WARNING: Inline strong start-string without end-string.
Edit:
PS: Note that removing the below docstring makes the error disappear, so it seems to be the thing to fix.
.. autodata:: json_class_index
:annotation: Maps jsonClass values to Python object names. Useful for (de)serialization. Updated using update_json_class_index() calls at the end of each module file (such as this one) whose classes may be serialized.
The warning messages indicate that the reStructuredText syntax of your docstrings is not valid and needs to be corrected.
Additionally your source code does not comply with PEP 8. Indentation should be 4 spaces, but your code uses 2, which might cause problems with Sphinx.
First make your code compliant with PEP 8 indentation.
Second, you must have two lines separating whatever precedes info field lists and the info field lists themselves.
Third, if the warnings persist, then look at the line numbers in the warnings—3, 4, 7, and 8—and the warnings themselves. It appears that the warnings correspond to this block of code:
#classmethod
def make_from_dict(cls, input_dict):
"""Defines *our* canonical way of constructing a JSON object from a dict.
All other deserialization methods should use this.
Note that this assumes that json_class_index is populated properly!
- ``from sanskrit_data.schema import *`` before using this should take care of it.
:param input_dict:
:return: A subclass of JsonObject
"""
Try this instead, post-PEP-8-ification, which should correct most of the warnings caused by faulty white space in your docstring:
#classmethod
def make_from_dict(cls, input_dict):
"""
Defines *our* canonical way of constructing a JSON object from a dict.
All other deserialization methods should use this.
Note that this assumes that json_class_index is populated properly!
- ``from sanskrit_data.schema import *`` before using this should take care of it.
:param input_dict:
:return: A subclass of JsonObject
"""
This style is acceptable according to PEP 257. The indentation is visually and vertically consistent, where the triple quotes vertically align with the left indentation. I think it's easier to read.
The fix was to add a docstring for the variable as follows:
#: Maps jsonClass values to Python object names. Useful for (de)serialization. Updated using update_json_class_index() calls at the end of each module file (such as this one) whose classes may be serialized.
json_class_index = {}

sphinx:No content update in html content from docstring

I have been working on a project on to interface with Senna which is tool used in NLP processing using Python. For easy generation of documentation I followed reStructuredText documentation style which is pretty easy one.
On calling make html, few time(and sometime no warning) there has been shown warning like
docstring of pntl.tools.Annotator.test:2: WARNING: Field list ends without a blank line; unexpected unindent and one more thing what is the use of this number 2 displayed in the working.
def test(senna_path="/media/jawahar/jon/ubuntu/senna", sent="", dep_model="", batch=False,
jar_path="/media/jawahar/jon/ubuntu/practNLPTools-lite/pntl"):
"""please replace the path of yours environment(accouding to OS path)
:parama str senna_path: path for senna location
:parama str dep_model: stanford dependency parser model location
:parama str or list sent: the sentense to process with Senna
:parama bool batch: makeing as batch process with one or more sentense passing
:parama str jar_path: location of stanford-parser.jar file
"""
and Image of the built result is been attach to show error in the content of html. For detail view of my project follow this link
The error indicates that you have incorrect syntax, specifically no blank lines around the description and the field list, and indentation is not correct. White space matters.
Spelling matters, too. You probably meant :param blah blah: thing not :parama blah blah: thing:
See Info field lists for more information.
Edit
The following example should fix the issue. Note the correct spelling of "param", and the necessary line break separating the parameter list from the description in the docstring. Additionally, to avoid PEP8 warnings in your code (reStructuredText does not really care in this case), you should wrap long lines as noted in the method definition. There is another new line wrapping in the parameter list so that Sphinx will render it correctly as well as avoid the PEP8 warning.
def test(senna_path="/media/jawahar/jon/ubuntu/senna", sent="", dep_model="",
batch=False,
jar_path="/media/jawahar/jon/ubuntu/practNLPTools-lite/pntl"):
"""
please replace the path of yours environment(accouding to OS path)
:param str senna_path: path for senna location
:param str dep_model: stanford dependency parser model location
:param str or list sent: the sentense to process with Senna
:param bool batch: makeing as batch process with one or more sentense
passing
:param str jar_path: location of stanford-parser.jar file
"""

How can I document a constant (module level variable) with sphinx (docstring without data)?

I want to document a constant by its docstring. I don't want to let the value to show up. If I have a long list, Sphinx will include it completely which I don't want. It would be okay for me if only an excerpt would be shown (with ellipsis or whatever). But not thousands of list elements.
module_level_variable2 = 98765
"""int: Module level variable documented inline.
The docstring may span multiple lines. The type may optionally be specified
on the first line, separated by a colon.
"""
I've tried the following:
.. autoclass:: module_level_variable2
does not show the docstring at all.
.. autodata:: module_level_variable2
does show docstring and the data.
.. automethod:: module_level_variable2
and
.. autoattribute:: module_level_variable2
do not show anything in the documentation (not even the name of the variable).
.. autofunction:: module_level_variable2
does show the docstring only. This is almost what I want. However, it considers a constant as a function() by adding parentheses to it - which is wrong (why a warning is raised on make html!
I went through http://www.sphinx-doc.org/en/1.4.8/ext/autodoc.html but could not find a switch to tell .. autodata:: not to show the data itself (but the docstring only).
SOLUTION
.. autodata:: module_level_variable2
:annotation:

Resources