Merge pull request #8278 from tk0miya/8255_hexadecimal_default_value

Fix #8255: py domain: number in defarg is changed to decimal
This commit is contained in:
Takeshi KOMIYA 2020-10-06 00:47:22 +09:00 committed by GitHub
commit 408ebe4105
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 46 additions and 19 deletions

View File

@ -49,6 +49,8 @@ Bugs fixed
* #8277: sphinx-build: missing and redundant spacing (and etc) for console * #8277: sphinx-build: missing and redundant spacing (and etc) for console
output on building output on building
* #7973: imgconverter: Check availability of imagemagick many times * #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, * #8093: The highlight warning has wrong location in some builders (LaTeX,
singlehtml and so on) singlehtml and so on)
* #8239: Failed to refer a token in productionlist if it is indented * #8239: Failed to refer a token in productionlist if it is indented

View File

@ -58,17 +58,19 @@ def parse(code: str, mode: str = 'exec') -> "ast.AST":
return ast.parse(code, mode=mode) return ast.parse(code, mode=mode)
def unparse(node: Optional[ast.AST]) -> Optional[str]: def unparse(node: Optional[ast.AST], code: str = '') -> Optional[str]:
"""Unparse an AST to string.""" """Unparse an AST to string."""
if node is None: if node is None:
return None return None
elif isinstance(node, str): elif isinstance(node, str):
return node return node
return _UnparseVisitor().visit(node) return _UnparseVisitor(code).visit(node)
# a greatly cut-down version of `ast._Unparser` # a greatly cut-down version of `ast._Unparser`
class _UnparseVisitor(ast.NodeVisitor): class _UnparseVisitor(ast.NodeVisitor):
def __init__(self, code: str = '') -> None:
self.code = code
def _visit_op(self, node: ast.AST) -> str: def _visit_op(self, node: ast.AST) -> str:
return OPERATORS[node.__class__] return OPERATORS[node.__class__]
@ -195,6 +197,11 @@ class _UnparseVisitor(ast.NodeVisitor):
def visit_Constant(self, node: ast.Constant) -> str: def visit_Constant(self, node: ast.Constant) -> str:
if node.value is Ellipsis: if node.value is Ellipsis:
return "..." return "..."
elif isinstance(node.value, (int, float, complex)):
if self.code and sys.version_info > (3, 8):
return ast.get_source_segment(self.code, node)
else:
return repr(node.value)
else: else:
return repr(node.value) return repr(node.value)

View File

@ -600,13 +600,14 @@ def stringify_signature(sig: inspect.Signature, show_annotation: bool = True,
def signature_from_str(signature: str) -> inspect.Signature: def signature_from_str(signature: str) -> inspect.Signature:
"""Create a Signature object from string.""" """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 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*.""" """Create a Signature object from AST *node*."""
args = node.args args = node.args
defaults = list(args.defaults) defaults = list(args.defaults)
@ -626,9 +627,9 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i] is Parameter.empty: if defaults[i] is Parameter.empty:
default = Parameter.empty default = Parameter.empty
else: 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, params.append(Parameter(arg.arg, Parameter.POSITIONAL_ONLY,
default=default, annotation=annotation)) default=default, annotation=annotation))
@ -636,29 +637,29 @@ def signature_from_ast(node: ast.FunctionDef) -> inspect.Signature:
if defaults[i + posonlyargs] is Parameter.empty: if defaults[i + posonlyargs] is Parameter.empty:
default = Parameter.empty default = Parameter.empty
else: 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, params.append(Parameter(arg.arg, Parameter.POSITIONAL_OR_KEYWORD,
default=default, annotation=annotation)) default=default, annotation=annotation))
if args.vararg: 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, params.append(Parameter(args.vararg.arg, Parameter.VAR_POSITIONAL,
annotation=annotation)) annotation=annotation))
for i, arg in enumerate(args.kwonlyargs): for i, arg in enumerate(args.kwonlyargs):
default = ast_unparse(args.kw_defaults[i]) or Parameter.empty default = ast_unparse(args.kw_defaults[i], code) or Parameter.empty
annotation = ast_unparse(arg.annotation) or Parameter.empty annotation = ast_unparse(arg.annotation, code) or Parameter.empty
params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default, params.append(Parameter(arg.arg, Parameter.KEYWORD_ONLY, default=default,
annotation=annotation)) annotation=annotation))
if args.kwarg: 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, params.append(Parameter(args.kwarg.arg, Parameter.VAR_KEYWORD,
annotation=annotation)) 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) 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, "/"])]) [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): def test_optional_pyfunction_signature(app):
text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object" text = ".. py:function:: compile(source [, filename [, symbol]]) -> ast object"
doctree = restructuredtext.parse(app, text) doctree = restructuredtext.parse(app, text)

View File

@ -58,7 +58,7 @@ from sphinx.pycode import ast
]) ])
def test_unparse(source, expected): def test_unparse(source, expected):
module = ast.parse(source) module = ast.parse(source)
assert ast.unparse(module.body[0].value) == expected assert ast.unparse(module.body[0].value, source) == expected
def test_unparse_None(): def test_unparse_None():
@ -66,8 +66,12 @@ def test_unparse_None():
@pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.') @pytest.mark.skipif(sys.version_info < (3, 8), reason='python 3.8+ is required.')
def test_unparse_py38(): @pytest.mark.parametrize('source,expected', [
source = "lambda x=0, /, y=1, *args, z, **kwargs: x + y + z" ("lambda x=0, /, y=1, *args, z, **kwargs: x + y + z",
expected = "lambda x=0, /, y=1, *args, z, **kwargs: ..." "lambda x=0, /, y=1, *args, z, **kwargs: ..."), # posonlyargs
("0x1234", "0x1234"), # Constant
("1_000_000", "1_000_000"), # Constant
])
def test_unparse_py38(source, expected):
module = ast.parse(source) module = ast.parse(source)
assert ast.unparse(module.body[0].value) == expected assert ast.unparse(module.body[0].value, source) == expected