Skip to content

Commit 363f4f9

Browse files
authored
gh-104683: clinic.py: refactor Parameter and Function as dataclasses (#106477)
1 parent 3e5ce79 commit 363f4f9

File tree

1 file changed

+57
-86
lines changed

1 file changed

+57
-86
lines changed

Diff for: Tools/clinic/clinic.py

+57-86
Original file line numberDiff line numberDiff line change
@@ -2439,6 +2439,8 @@ def __repr__(self) -> str:
24392439
ParamDict = dict[str, "Parameter"]
24402440
ReturnConverterType = Callable[..., "CReturnConverter"]
24412441

2442+
2443+
@dc.dataclass(repr=False)
24422444
class Function:
24432445
"""
24442446
Mutable duck type for inspect.Function.
@@ -2450,49 +2452,34 @@ class Function:
24502452
It will always be true that
24512453
(not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring))
24522454
"""
2455+
parameters: ParamDict = dc.field(default_factory=dict)
2456+
_: dc.KW_ONLY
2457+
name: str
2458+
module: Module
2459+
cls: Class | None = None
2460+
c_basename: str | None = None
2461+
full_name: str | None = None
2462+
return_converter: CReturnConverter
2463+
return_annotation: object = inspect.Signature.empty
2464+
docstring: str = ''
2465+
kind: str = CALLABLE
2466+
coexist: bool = False
2467+
# docstring_only means "don't generate a machine-readable
2468+
# signature, just a normal docstring". it's True for
2469+
# functions with optional groups because we can't represent
2470+
# those accurately with inspect.Signature in 3.4.
2471+
docstring_only: bool = False
24532472

2454-
def __init__(
2455-
self,
2456-
parameters: ParamDict | None = None,
2457-
*,
2458-
name: str,
2459-
module: Module,
2460-
cls: Class | None = None,
2461-
c_basename: str | None = None,
2462-
full_name: str | None = None,
2463-
return_converter: CReturnConverter,
2464-
return_annotation = inspect.Signature.empty,
2465-
docstring: str | None = None,
2466-
kind: str = CALLABLE,
2467-
coexist: bool = False,
2468-
docstring_only: bool = False
2469-
) -> None:
2470-
self.parameters = parameters or {}
2471-
self.return_annotation = return_annotation
2472-
self.name = name
2473-
self.full_name = full_name
2474-
self.module = module
2475-
self.cls = cls
2476-
self.parent = cls or module
2477-
self.c_basename = c_basename
2478-
self.return_converter = return_converter
2479-
self.docstring = docstring or ''
2480-
self.kind = kind
2481-
self.coexist = coexist
2473+
def __post_init__(self) -> None:
2474+
self.parent: Class | Module = self.cls or self.module
24822475
self.self_converter: self_converter | None = None
2483-
# docstring_only means "don't generate a machine-readable
2484-
# signature, just a normal docstring". it's True for
2485-
# functions with optional groups because we can't represent
2486-
# those accurately with inspect.Signature in 3.4.
2487-
self.docstring_only = docstring_only
2488-
2489-
self.rendered_parameters = None
2476+
self.__render_parameters__: list[Parameter] | None = None
24902477

2491-
__render_parameters__ = None
24922478
@property
2493-
def render_parameters(self):
2479+
def render_parameters(self) -> list[Parameter]:
24942480
if not self.__render_parameters__:
2495-
self.__render_parameters__ = l = []
2481+
l: list[Parameter] = []
2482+
self.__render_parameters__ = l
24962483
for p in self.parameters.values():
24972484
p = p.copy()
24982485
p.converter.pre_render()
@@ -2517,49 +2504,30 @@ def methoddef_flags(self) -> str | None:
25172504
def __repr__(self) -> str:
25182505
return '<clinic.Function ' + self.name + '>'
25192506

2520-
def copy(self, **overrides) -> "Function":
2521-
kwargs = {
2522-
'name': self.name, 'module': self.module, 'parameters': self.parameters,
2523-
'cls': self.cls, 'c_basename': self.c_basename,
2524-
'full_name': self.full_name,
2525-
'return_converter': self.return_converter, 'return_annotation': self.return_annotation,
2526-
'docstring': self.docstring, 'kind': self.kind, 'coexist': self.coexist,
2527-
'docstring_only': self.docstring_only,
2528-
}
2529-
kwargs.update(overrides)
2530-
f = Function(**kwargs)
2507+
def copy(self, **overrides: Any) -> Function:
2508+
f = dc.replace(self, **overrides)
25312509
f.parameters = {
25322510
name: value.copy(function=f)
25332511
for name, value in f.parameters.items()
25342512
}
25352513
return f
25362514

25372515

2516+
@dc.dataclass(repr=False, slots=True)
25382517
class Parameter:
25392518
"""
25402519
Mutable duck type of inspect.Parameter.
25412520
"""
2542-
2543-
def __init__(
2544-
self,
2545-
name: str,
2546-
kind: inspect._ParameterKind,
2547-
*,
2548-
default = inspect.Parameter.empty,
2549-
function: Function,
2550-
converter: "CConverter",
2551-
annotation = inspect.Parameter.empty,
2552-
docstring: str | None = None,
2553-
group: int = 0
2554-
) -> None:
2555-
self.name = name
2556-
self.kind = kind
2557-
self.default = default
2558-
self.function = function
2559-
self.converter = converter
2560-
self.annotation = annotation
2561-
self.docstring = docstring or ''
2562-
self.group = group
2521+
name: str
2522+
kind: inspect._ParameterKind
2523+
_: dc.KW_ONLY
2524+
default: object = inspect.Parameter.empty
2525+
function: Function
2526+
converter: CConverter
2527+
annotation: object = inspect.Parameter.empty
2528+
docstring: str = ''
2529+
group: int = 0
2530+
right_bracket_count: int = dc.field(init=False, default=0)
25632531

25642532
def __repr__(self) -> str:
25652533
return '<clinic.Parameter ' + self.name + '>'
@@ -2576,18 +2544,19 @@ def is_vararg(self) -> bool:
25762544
def is_optional(self) -> bool:
25772545
return not self.is_vararg() and (self.default is not unspecified)
25782546

2579-
def copy(self, **overrides) -> "Parameter":
2580-
kwargs = {
2581-
'name': self.name, 'kind': self.kind, 'default':self.default,
2582-
'function': self.function, 'converter': self.converter, 'annotation': self.annotation,
2583-
'docstring': self.docstring, 'group': self.group,
2584-
}
2585-
kwargs.update(overrides)
2586-
if 'converter' not in overrides:
2547+
def copy(
2548+
self,
2549+
/,
2550+
*,
2551+
converter: CConverter | None = None,
2552+
function: Function | None = None,
2553+
**overrides: Any
2554+
) -> Parameter:
2555+
function = function or self.function
2556+
if not converter:
25872557
converter = copy.copy(self.converter)
2588-
converter.function = kwargs['function']
2589-
kwargs['converter'] = converter
2590-
return Parameter(**kwargs)
2558+
converter.function = function
2559+
return dc.replace(self, **overrides, function=function, converter=converter)
25912560

25922561
def get_displayname(self, i: int) -> str:
25932562
if i == 0:
@@ -2761,7 +2730,7 @@ def __init__(self,
27612730
# Positional args:
27622731
name: str,
27632732
py_name: str,
2764-
function,
2733+
function: Function,
27652734
default: object = unspecified,
27662735
*, # Keyword only args:
27672736
c_default: str | None = None,
@@ -2800,7 +2769,9 @@ def __init__(self,
28002769
# about the function in the init.
28012770
# (that breaks if we get cloned.)
28022771
# so after this change we will noisily fail.
2803-
self.function = LandMine("Don't access members of self.function inside converter_init!")
2772+
self.function: Function | LandMine = LandMine(
2773+
"Don't access members of self.function inside converter_init!"
2774+
)
28042775
self.converter_init(**kwargs)
28052776
self.function = function
28062777

@@ -2810,7 +2781,7 @@ def converter_init(self):
28102781
def is_optional(self) -> bool:
28112782
return (self.default is not unspecified)
28122783

2813-
def _render_self(self, parameter: str, data: CRenderData) -> None:
2784+
def _render_self(self, parameter: Parameter, data: CRenderData) -> None:
28142785
self.parameter = parameter
28152786
name = self.parser_name
28162787

@@ -2870,7 +2841,7 @@ def _render_non_self(self, parameter, data):
28702841
if cleanup:
28712842
data.cleanup.append('/* Cleanup for ' + name + ' */\n' + cleanup.rstrip() + "\n")
28722843

2873-
def render(self, parameter: str, data: CRenderData) -> None:
2844+
def render(self, parameter: Parameter, data: CRenderData) -> None:
28742845
"""
28752846
parameter is a clinic.Parameter instance.
28762847
data is a CRenderData instance.
@@ -5246,7 +5217,7 @@ def format_docstring(self):
52465217
assert isinstance(parameters[0].converter, self_converter)
52475218
# self is always positional-only.
52485219
assert parameters[0].is_positional_only()
5249-
parameters[0].right_bracket_count = 0
5220+
assert parameters[0].right_bracket_count == 0
52505221
positional_only = True
52515222
for p in parameters[1:]:
52525223
if not p.is_positional_only():

0 commit comments

Comments
 (0)