8000 gh-113317: Change how Argument Clinic lists converters (#116853) · python/cpython@7aa89bc · GitHub
[go: up one dir, main page]

Skip to content

Commit 7aa89bc

Browse files
gh-113317: Change how Argument Clinic lists converters (#116853)
* Add a new create_parser_namespace() function for PythonParser to pass objects to executed code. * In run_clinic(), list converters using 'converters' and 'return_converters' dictionarties. * test_clinic: add 'object()' return converter. * Use also create_parser_namespace() in eval_ast_expr(). Co-authored-by: Erlend E. Aasland <erlend@python.org>
1 parent 669ef49 commit 7aa89bc

File tree

2 files changed

+55
-32
lines changed

2 files changed

+55
-32
lines changed

Lib/test/test_clinic.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2657,6 +2657,7 @@ def test_cli_converters(self):
26572657
float()
26582658
int()
26592659
long()
2660+
object()
26602661
Py_ssize_t()
26612662
size_t()
26622663
unsigned_int()

Tools/clinic/clinic.py

Lines changed: 54 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@
5656
from libclinic.block_parser import Block, BlockParser
5757
from libclinic.crenderdata import CRenderData, Include, TemplateDict
5858
from libclinic.converter import (
59-
CConverter, CConverterClassT,
59+
CConverter, CConverterClassT, ConverterType,
6060
converters, legacy_converters)
6161

6262

@@ -1988,13 +1988,38 @@ def parse_file(
19881988
libclinic.write_file(output, cooked)
19891989

19901990

1991+
@functools.cache
1992+
def _create_parser_base_namespace() -> dict[str, Any]:
1993+
ns = dict(
1994+
CConverter=CConverter,
1995+
CReturnConverter=CReturnConverter,
1996+
buffer=buffer,
1997+
robuffer=robuffer,
1998+
rwbuffer=rwbuffer,
1999+
unspecified=unspecified,
2000+
NoneType=NoneType,
2001+
)
2002+
for name, converter in converters.items():
2003+
ns[f'{name}_converter'] = converter
2004+
for name, return_converter in return_converters.items():
2005+
ns[f'{name}_return_converter'] = return_converter
2006+
return ns
2007+
2008+
2009+
def create_parser_namespace() -> dict[str, Any]:
2010+
base_namespace = _create_parser_base_namespace()
2011+
return base_namespace.copy()
2012+
2013+
2014+
19912015
class PythonParser:
19922016
def __init__(self, clinic: Clinic) -> None:
19932017
pass
19942018

19952019
def parse(self, block: Block) -> None:
2020+
namespace = create_parser_namespace()
19962021
with contextlib.redirect_stdout(io.StringIO()) as s:
1997-
exec(block.input)
2022+
exec(block.input, namespace)
19982023
block.output = s.getvalue()
19992024

20002025

@@ -3443,7 +3468,6 @@ class float_return_converter(double_return_converter):
34433468

34443469
def eval_ast_expr(
34453470
node: ast.expr,
3446-
globals: dict[str, Any],
34473471
*,
34483472
filename: str = '-'
34493473
) -> Any:
@@ -3460,8 +3484,9 @@ def eval_ast_expr(
34603484
node = node.value
34613485

34623486
expr = ast.Expression(node)
3487+
namespace = create_parser_namespace()
34633488
co = compile(expr, filename, 'eval')
3464-
fn = FunctionType(co, globals)
3489+
fn = FunctionType(co, namespace)
34653490
return fn()
34663491

34673492

@@ -4463,12 +4488,11 @@ def parse_converter(
44634488
case ast.Name(name):
44644489
return name, False, {}
44654490
case ast.Call(func=ast.Name(name)):
4466-
symbols = globals()
44674491
kwargs: ConverterArgs = {}
44684492
for node in annotation.keywords:
44694493
if not isinstance(node.arg, str):
44704494
fail("Cannot use a kwarg splat in a function-call annotation")
4471-
kwargs[node.arg] = eval_ast_expr(node.value, symbols)
4495+
kwargs[node.arg] = eval_ast_expr(node.value)
44724496
return name, False, kwargs
44734497
case _:
44744498
fail(
@@ -4984,25 +5008,21 @@ def run_clinic(parser: argparse.ArgumentParser, ns: argparse.Namespace) -> None:
49845008
parser.error(
49855009
"can't specify --converters and a filename at the same time"
49865010
)
4987-
converters: list[tuple[str, str]] = []
4988-
return_converters: list[tuple[str, str]] = []
4989-
ignored = set("""
4990-
add_c_converter
4991-
add_c_return_converter
4992-
add_default_legacy_c_converter
4993-
add_legacy_c_converter
4994-
""".strip().split())
4995-
module = globals()
4996-
for name in module:
4997-
for suffix, ids in (
4998-
("_return_converter", return_converters),
4999-
("_converter", converters),
5000-
):
5001-
if name in ignored:
5002-
continue
5003-
if name.endswith(suffix):
5004-
ids.append((name, name.removesuffix(suffix)))
5005-
break
< 2D0F /code>
5011+
AnyConverterType = ConverterType | ReturnConverterType
5012+
converter_list: list[tuple[str, AnyConverterType]] = []
5013+
return_converter_list: list[tuple[str, AnyConverterType]] = []
5014+
5015+
for name, converter in converters.items():
5016+
converter_list.append((
5017+
name,
5018+
converter,
5019+
))
5020+
for name, return_converter in return_converters.items():
5021+
return_converter_list.append((
5022+
name,
5023+
return_converter
5024+
))
5025+
50065026
print()
50075027

50085028
print("Legacy converters:")
@@ -5012,15 +5032,17 @@ def run_clinic(parser: argparse.ArgumentParser, ns: argparse.Namespace) -> None:
50125032
print()
50135033

50145034
for title, attribute, ids in (
5015-
("Converters", 'converter_init', converters),
5016-
("Return converters", 'return_converter_init', return_converters),
5035+
("Converters", 'converter_init', converter_list),
5036+
("Return converters", 'return_converter_init', return_converter_list),
50175037
):
50185038
print(title + ":")
5039+
5040+
ids.sort(key=lambda item: item[0].lower())
50195041
longest = -1
5020-
for name, short_name in ids:
5021-
longest = max(longest, len(short_name))
5022-
for name, short_name in sorted(ids, key=lambda x: x[1].lower()):
5023-
cls = module[name]
5042+
for name, _ in ids:
5043+
longest = max(longest, len(name))
5044+
5045+
for name, cls in ids:
50245046
callable = getattr(cls, attribute, None)
50255047
if not callable:
50265048
continue
@@ -5033,7 +5055,7 @@ def run_clinic(parser: argparse.ArgumentParser, ns: argparse.Namespace) -> None:
50335055
else:
50345056
s = parameter_name
50355057
parameters.append(s)
5036-
print(' {}({})'.format(short_name, ', '.join(parameters)))
5058+
print(' {}({})'.format(name, ', '.join(parameters)))
50375059
print()
50385060
print("All converters also accept (c_default=None, py_default=None, annotation=None).")
50395061
print("All return converters also accept (py_default=None).")

0 commit comments

Comments
 (0)
0