From ae8e32b7231b6723f49a5e07150c2968d5a9df93 Mon Sep 17 00:00:00 2001 From: Takeshi KOMIYA Date: Sun, 31 Oct 2021 00:26:03 +0900 Subject: [PATCH] Fix #9781: autodoc_preserve_defaults does not support hexadecimal --- CHANGES | 2 + sphinx/ext/autodoc/preserve_defaults.py | 38 ++++++++++++++++--- .../target/preserve_defaults.py | 5 ++- tests/test_ext_autodoc_preserve_defaults.py | 4 +- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 8bf822c0dee..fb6f032e332 100644 --- a/CHANGES +++ b/CHANGES @@ -66,6 +66,8 @@ Bugs fixed * #9756: autodoc: Crashed if classmethod does not have __func__ attribute * #9757: autodoc: :confval:`autodoc_inherit_docstrings` does not effect to overriden classmethods +* #9781: autodoc: :confval:`autodoc_preserve_defaults` does not support + hexadecimal numeric * #9630: autosummary: Failed to build summary table if :confval:`primary_domain` is not 'py' * #9670: html: Fix download file with special characters diff --git a/sphinx/ext/autodoc/preserve_defaults.py b/sphinx/ext/autodoc/preserve_defaults.py index 3d859fe8eee..547e5a50acc 100644 --- a/sphinx/ext/autodoc/preserve_defaults.py +++ b/sphinx/ext/autodoc/preserve_defaults.py @@ -11,7 +11,8 @@ import ast import inspect -from typing import Any, Dict +import sys +from typing import Any, Dict, List, Optional from sphinx.application import Sphinx from sphinx.locale import __ @@ -49,11 +50,32 @@ def get_function_def(obj: Any) -> ast.FunctionDef: return None +def get_default_value(lines: List[str], position: ast.AST) -> Optional[str]: + try: + if sys.version_info < (3, 8): # only for py38+ + return None + elif position.lineno == position.end_lineno: # type: ignore + line = lines[position.lineno - 1] + return line[position.col_offset:position.end_col_offset] # type: ignore + else: + # multiline value is not supported now + return None + except (AttributeError, IndexError): + return None + + def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None: """Update defvalue info of *obj* using type_comments.""" if not app.config.autodoc_preserve_defaults: return + 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. + except OSError: + lines = [] + try: function = get_function_def(obj) if function.args.defaults or function.args.kw_defaults: @@ -64,11 +86,17 @@ def update_defvalue(app: Sphinx, obj: Any, bound_method: bool) -> None: for i, param in enumerate(parameters): if param.default is not param.empty: if param.kind in (param.POSITIONAL_ONLY, param.POSITIONAL_OR_KEYWORD): - value = DefaultValue(ast_unparse(defaults.pop(0))) # type: ignore - parameters[i] = param.replace(default=value) + default = defaults.pop(0) + value = get_default_value(lines, default) + if value is None: + value = ast_unparse(default) # type: ignore + parameters[i] = param.replace(default=DefaultValue(value)) else: - value = DefaultValue(ast_unparse(kw_defaults.pop(0))) # type: ignore - parameters[i] = param.replace(default=value) + default = kw_defaults.pop(0) + value = get_default_value(lines, default) + if value is None: + value = ast_unparse(default) # type: ignore + parameters[i] = param.replace(default=DefaultValue(value)) sig = sig.replace(parameters=parameters) obj.__signature__ = sig except (AttributeError, TypeError): diff --git a/tests/roots/test-ext-autodoc/target/preserve_defaults.py b/tests/roots/test-ext-autodoc/target/preserve_defaults.py index 79305349b40..422d41b953a 100644 --- a/tests/roots/test-ext-autodoc/target/preserve_defaults.py +++ b/tests/roots/test-ext-autodoc/target/preserve_defaults.py @@ -7,7 +7,8 @@ def foo(name: str = CONSTANT, sentinel: Any = SENTINEL, - now: datetime = datetime.now()) -> None: + now: datetime = datetime.now(), + color: int = 0xFFFFFF) -> None: """docstring""" @@ -15,5 +16,5 @@ class Class: """docstring""" def meth(self, name: str = CONSTANT, sentinel: Any = SENTINEL, - now: datetime = datetime.now()) -> None: + now: datetime = datetime.now(), color: int = 0xFFFFFF) -> None: """docstring""" diff --git a/tests/test_ext_autodoc_preserve_defaults.py b/tests/test_ext_autodoc_preserve_defaults.py index c0b5a9f294f..2dae6058779 100644 --- a/tests/test_ext_autodoc_preserve_defaults.py +++ b/tests/test_ext_autodoc_preserve_defaults.py @@ -30,14 +30,14 @@ def test_preserve_defaults(app): '', '', ' .. py:method:: Class.meth(name: str = CONSTANT, sentinel: Any = SENTINEL, ' - 'now: datetime.datetime = datetime.now()) -> None', + 'now: datetime.datetime = datetime.now(), color: int = 0xFFFFFF) -> None', ' :module: target.preserve_defaults', '', ' docstring', '', '', '.. py:function:: foo(name: str = CONSTANT, sentinel: Any = SENTINEL, now: ' - 'datetime.datetime = datetime.now()) -> None', + 'datetime.datetime = datetime.now(), color: int = 0xFFFFFF) -> None', ' :module: target.preserve_defaults', '', ' docstring',