Fix #8255: py domain: number in defarg is changed to decimal

Number literals in default argument value is converted to decimal form
unexpectedly by AST module.  This fixes the signature parsing code to
recosntruct it correctly.

Note: This is only available in Python 3.8+.
This commit is contained in:
Takeshi KOMIYA 2020-10-04 10:31:02 +09:00
parent 0b32e72635
commit cc941db40b
3 changed files with 28 additions and 12 deletions

View File

@ -49,6 +49,8 @@ Bugs fixed
* #8277: sphinx-build: missing and redundant spacing (and etc) for console
output on building
* #7973: imgconverter: Check availability of imagemagick many times
* #8255: py domain: number in default argument value is changed from hexadecimal
to decimal
* #8093: The highlight warning has wrong location in some builders (LaTeX,
singlehtml and so on)
* #8239: Failed to refer a token in productionlist if it is indented

View File

@ -600,13 +600,14 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
def signature_from_str(signature: str) -> inspect.Signature:
"""Create a Signature object from string."""
module = ast.parse('def func' + signature + ': pass')
code = 'def func' + signature + ': pass'
module = ast.parse(code)
function = cast(ast.FunctionDef, module.body[0]) # type: ignore
return signature_from_ast(function)
return signature_from_ast(function, code)
def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
def signature_from_ast(node: ast.FunctionDef, code: str = '') -> inspect.Signature:
"""Create a Signature object from AST *node*."""
args = node.args
defaults = list(args.defaults)
@ -626,9 +627,9 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i] is Parameter.empty:
default = Parameter.empty
else:
default = ast_unparse(defaults[i])
default = ast_unparse(defaults[i], code)
annotation = ast_unparse(arg.annotation) or Parameter.empty
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
default=default, annotation=annotation))
@ -636,29 +637,29 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i + posonlyargs] is Parameter.empty:
default = Parameter.empty
else:
default = ast_unparse(defaults[i + posonlyargs])
default = ast_unparse(defaults[i + posonlyargs], code)
annotation = ast_unparse(arg.annotation) or Parameter.empty
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
default=default, annotation=annotation))
if args.vararg:
annotation = ast_unparse(args.vararg.annotation) or Parameter.empty
annotation = ast_unparse(args.vararg.annotation, code) or Parameter.empty
params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
annotation=annotation))
for i, arg in enumerate(args.kwonlyargs):
default = ast_unparse(args.kw_defaults[i]) or Parameter.empty
annotation = ast_unparse(arg.annotation) or Parameter.empty
default = ast_unparse(args.kw_defaults[i], code) or Parameter.empty
annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
annotation=annotation))
if args.kwarg:
annotation = ast_unparse(args.kwarg.annotation) or Parameter.empty
annotation = ast_unparse(args.kwarg.annotation, code) or Parameter.empty
params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD,
annotation=annotation))
return_annotation = ast_unparse(node.returns) or Parameter.empty
return_annotation = ast_unparse(node.returns, code) or Parameter.empty
return inspect.Signature(params, return_annotation=return_annotation)

View File

@ -386,6 +386,19 @@ def test_pyfunction_signature_full_py38(app):
[desc_parameter, desc_sig_operator, "/"])])
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
def test_pyfunction_with_number_literals(app):
text = ".. py:function:: hello(age=0x10, height=1_6_0)"
doctree = restructuredtext.parse(app, text)
assert_node(doctree[1][0][1],
[desc_parameterlist, ([desc_parameter, ([desc_sig_name, "age"],
[desc_sig_operator, "="],
[nodes.inline, "0x10"])],
[desc_parameter, ([desc_sig_name, "height"],
[desc_sig_operator, "="],
[nodes.inline, "1_6_0"])])])
def test_optional_pyfunction_signature(app):
text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object"
doctree = restructuredtext.parse(app, text)