sphinx-autodoc-typehints
sphinx-autodoc-typehints copied to clipboard
More compact and more correct property description.
Properties introspected by typehints are decorated with a "return type" clause. This can take up a lot of space and talking about "return type" for a property/attribute is not even that correct.
In psycopg3 documentation I have monkeypatched AttributeDocumenter.add_content() so that if an attribute has a :rtype: it is converted into a :type:, which is placed right after the directive. The difference can be seen in:

The code involved is something like what shown below, for reference. Of course the implementation in the module itself would be different.
orig_attr_add_content = AttributeDocumenter.add_content
def fixed_attr_add_content(self, more_content, no_docstring=False):
"""
Replace a docstring such as::
.. py:attribute:: ConnectionInfo.dbname
:module: psycopg3
The database name of the connection.
:rtype: :py:class:`str`
into:
.. py:attribute:: ConnectionInfo.dbname
:type: str
:module: psycopg3
The database name of the connection.
which creates a more compact representation of a property.
"""
orig_attr_add_content(self, more_content, no_docstring)
if not isinstance(self.object, property):
return
iret, mret = match_in_lines(r"\s*:rtype: (.*)", self.directive.result)
iatt, matt = match_in_lines(r"\.\.", self.directive.result)
if not (mret and matt):
return
self.directive.result.pop(iret)
self.directive.result.insert(
iatt + 1,
f"{self.indent}:type: {unrest(mret.group(1))}",
source=self.get_sourcename(),
)
AttributeDocumenter.add_content = fixed_attr_add_content
def match_in_lines(pattern, lines):
"""Match a regular expression against a list of strings.
Return the index of the first matched line and the match object.
None, None if nothing matched.
"""
for i, line in enumerate(lines):
m = re.match(pattern, line)
if m:
return i, m
else:
return None, None
def unrest(s):
r"""remove the reST markup from a string
e.g. :py:data:`~typing.Optional`\[:py:class:`int`] -> Optional[int]
required because :type: does the types lookup itself apparently.
"""
s = re.sub(r":[^`]*:`~?([^`]*)`", r"\1", s) # drop role
s = re.sub(r"\\(.)", r"\1", s) # drop escape
# note that ~psycopg3.pq.ConnStatus is converted to pq.ConnStatus
# which should be interpreted well if currentmodule is set ok.
s = re.sub(r"(?:typing|psycopg3)\.", "", s) # drop unneeded modules
s = re.sub(r"~", "", s) # drop the tilde
return s
A PR for this would be welcome.
Setting :type: for properties based on the return type of the property function is addressed in Sphinx 4.0 via sphinx-doc/sphinx#7383. It seems that the :rtype: annotation for properties can be removed now. However, the types added by sphinx don't get run through typehints_formatter(), so it might be a good idea to include that functionality (if possible).
I think this is resolved, partially by #287 and partially by the change to sphinx 4.0 mentioned above (probably some other changes were also involved, not sure).