Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sphinx.ext.autodoc.preserve_defaults extension. #11550

Merged
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
d89b859
Fix ``sphinx.ext.autodoc.preserve_defaults`` extension.
picnixz Aug 3, 2023
fbd834f
Merge branch 'sphinx-doc:master' into fix/11543-patch-update-default-…
picnixz Aug 3, 2023
835254a
Fix ``sphinx.ext.autodoc.preserve_defaults``.
picnixz Aug 3, 2023
36d6593
update CHANGES
picnixz Aug 3, 2023
dfac4ab
update comment
picnixz Aug 4, 2023
4f95c4b
Merge branch 'master' into fix/11543-patch-update-default-values
picnixz Aug 5, 2023
1153e03
Merge branch 'fix/11543-patch-update-default-values' of github.com:pi…
picnixz Aug 7, 2023
3be4413
Merge branch 'master' into fix/11543-patch-update-default-values
picnixz Aug 8, 2023
b463f86
Merge branch 'sphinx-doc:master' into fix/11543-patch-update-default-…
picnixz Aug 9, 2023
f9844b4
Merge remote-tracking branch 'upstream/master' into fix/11543-patch-u…
picnixz Aug 10, 2023
b674fa7
Merge remote-tracking branch 'upstream/master' into fix/11543-patch-u…
picnixz Aug 10, 2023
41a63f0
Merge remote-tracking branch 'upstream/master' into fix/11543-patch-u…
picnixz Aug 12, 2023
da9afeb
Merge branch 'master' into fix/11543-patch-update-default-values
AA-Turner Aug 13, 2023
614fac0
Merge branch 'master' into fix/11543-patch-update-default-values
picnixz Aug 14, 2023
ee79aaa
fix whitespace
picnixz Aug 14, 2023
1506dcf
update CHANGES
picnixz Aug 14, 2023
4d5404e
add deprecation warnings
picnixz Aug 14, 2023
71ca5d4
add deprecation warnings
picnixz Aug 14, 2023
4d66b78
fix doc
picnixz Aug 14, 2023
18da1e5
update tests
picnixz Aug 14, 2023
d90dfe4
fix lint
picnixz Aug 14, 2023
6bf3011
fix lint
picnixz Aug 14, 2023
3962e6a
Merge remote-tracking branch 'upstream/master' into fix/11543-patch-u…
picnixz Aug 15, 2023
c4e5741
Merge remote-tracking branch 'upstream/master' into fix/11543-patch-u…
picnixz Aug 16, 2023
d4ddf39
Merge branch 'master' into fix/11543-patch-update-default-values
AA-Turner Aug 16, 2023
9cefefa
Implementation updates
AA-Turner Aug 16, 2023
5f38027
Merge branch 'master' into fix/11543-patch-update-default-values
AA-Turner Aug 16, 2023
724c246
Merge branch 'master' into fix/11543-patch-update-default-values
AA-Turner Aug 17, 2023
536d2a4
Merge branch 'master' into fix/11543-patch-update-default-values
AA-Turner Aug 17, 2023
88f57e1
whitespace
AA-Turner Aug 17, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGES
Expand Up @@ -10,6 +10,12 @@ Dependencies
Incompatible changes
--------------------

* #11459: ``sphinx.ext.autodoc.preserve_defaults.get_function_def`` is
renamed ``sphinx.ext.autodoc.preserve_defaults.get_arguments`` and its
signature is updated accordingly. It now extracts the ``ast.arguments`` from
a function or a lambda function object.
Patch by Bénédikt Tran.
picnixz marked this conversation as resolved.
Show resolved Hide resolved

Deprecated
----------

Expand Down Expand Up @@ -48,12 +54,18 @@ Features added
* #11572: Improve ``debug`` logging of reasons why files are detected as out of
date.
Patch by Eric Larson.
* #11459: ``sphinx.ext.autodoc.preserve_defaults``: Add support for
``async def`` functions and lambda functions.
Patch by Bénédikt Tran.

Bugs fixed
----------

* #11077: graphviz: Fix relative links from within the graph.
Patch by Ralf Grubenmann.
* #11459: ``sphinx.ext.autodoc.preserve_defaults``: fix lambda functions
used inside ``@property`` should be ignored in a multi-line context.
Patch by Bénédikt Tran.
* #11529: Line Block in LaTeX builder outputs spurious empty token.
Patch by Adrian Vollmer.
* #11196: autosummary: Summary line extraction failed with "e.g."
Expand Down
56 changes: 46 additions & 10 deletions sphinx/ext/autodoc/preserve_defaults.py
Expand Up @@ -8,6 +8,7 @@

import ast
import inspect
from types import LambdaType
from typing import Any

import sphinx
Expand All @@ -27,8 +28,15 @@ def __repr__(self) -> str:
return self.name


def get_function_def(obj: Any) -> ast.FunctionDef | None:
"""Get FunctionDef object from living object.
_LAMBDA_NAME = (lambda: None).__name__


def _islambda(v):
return isinstance(v, LambdaType) and v.__name__ == _LAMBDA_NAME


def get_arguments(obj: Any) -> ast.arguments | None:
"""Get ast.arguments object from living object.
This tries to parse original code for living object and returns
AST node for given *obj*.
"""
Expand All @@ -38,12 +46,35 @@ def get_function_def(obj: Any) -> ast.FunctionDef | None:
# subject is placed inside class or block. To read its docstring,
# this adds if-block before the declaration.
module = ast.parse('if True:\n' + source)
return module.body[0].body[0] # type: ignore
subject = module.body[0].body[0] # type: ignore[attr-defined]
else:
module = ast.parse(source)
return module.body[0] # type: ignore
subject = module.body[0]
except (OSError, TypeError): # failed to load source code
return None
except SyntaxError:
if _islambda(obj):
# most likely a multi-line arising from detecting a lambda, e.g.:
#
# class Foo:
# x = property(
# lambda self: 1, doc="...")
return None

# Other syntax errors that are not due to the fact that we are
# documenting a lambda function are propagated (in particular,
# if a lambda function is renamed by the user, the SyntaxError is
# propagated).
raise

def _get_arguments(x: Any) -> ast.arguments | None:
if isinstance(x, (ast.AsyncFunctionDef, ast.FunctionDef, ast.Lambda)):
return x.args
if isinstance(x, (ast.Assign, ast.AnnAssign)):
return _get_arguments(x.value)
return None

return _get_arguments(subject)


def get_default_value(lines: list[str], position: ast.AST) -> str | None:
Expand All @@ -66,17 +97,22 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None:
try:
lines = inspect.getsource(obj).splitlines()
if lines[0].startswith((' ', r'\t')):
lines.insert(0, '') # insert a dummy line to follow what get_function_def() does.
lines.insert(0, '') # insert a dummy line to follow what get_arguments() does.
except (OSError, TypeError):
lines = []

try:
function = get_function_def(obj)
assert function is not None # for mypy
if function.args.defaults or function.args.kw_defaults:
args = get_arguments(obj)
if args is None:
# If the object is a built-in, we won't be always able to recover
# the function definition and its arguments. This happens if *obj*
# is the `__init__` method generated automatically for dataclasses.
return

if args.defaults or args.kw_defaults:
sig = inspect.signature(obj)
defaults = list(function.args.defaults)
kw_defaults = list(function.args.kw_defaults)
defaults = list(args.defaults)
kw_defaults = list(args.kw_defaults)
parameters = list(sig.parameters.values())
for i, param in enumerate(parameters):
if param.default is param.empty:
Expand Down
28 changes: 28 additions & 0 deletions tests/roots/test-ext-autodoc/target/preserve_defaults.py
Expand Up @@ -30,3 +30,31 @@ def clsmeth(cls, name: str = CONSTANT, sentinel: Any = SENTINEL,
now: datetime = datetime.now(), color: int = 0xFFFFFF,
*, kwarg1, kwarg2 = 0xFFFFFF) -> None:
"""docstring"""


get_sentinel = lambda custom=SENTINEL: custom
"""docstring"""


class MultiLine:
"""docstring"""

# The properties will raise a silent SyntaxError because "lambda self: 1"
# will be detected as a function to update the default values of. However,
# only prop3 will not fail because it's on a single line whereas the others
# will fail to parse.

prop1 = property(
lambda self: 1, doc="docstring")

prop2 = property(
lambda self: 2, doc="docstring"
)

prop3 = property(lambda self: 3, doc="docstring")

prop4 = (property
(lambda self: 4, doc="docstring"))

prop5 = property\
(lambda self: 5, doc="docstring")
42 changes: 42 additions & 0 deletions tests/test_ext_autodoc_preserve_defaults.py
Expand Up @@ -40,11 +40,53 @@ def test_preserve_defaults(app):
' docstring',
'',
'',
'.. py:class:: MultiLine()',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
' .. py:property:: MultiLine.prop1',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
' .. py:property:: MultiLine.prop2',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
' .. py:property:: MultiLine.prop3',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
' .. py:property:: MultiLine.prop4',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
' .. py:property:: MultiLine.prop5',
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
'.. py:function:: foo(name: str = CONSTANT, sentinel: ~typing.Any = SENTINEL, '
'now: ~datetime.datetime = datetime.now(), color: int = %s, *, kwarg1, '
'kwarg2=%s) -> None' % (color, color),
' :module: target.preserve_defaults',
'',
' docstring',
'',
'',
'.. py:function:: get_sentinel(custom=SENTINEL)',
' :module: target.preserve_defaults',
'',
' docstring',
'',
]