Skip to content

Commit

Permalink
gh-104683: clinic.py: refactor Parameter and Function as dataclas…
Browse files Browse the repository at this point in the history
…ses (#106477)
  • Loading branch information
AlexWaygood committed Jul 7, 2023
1 parent 3e5ce79 commit 363f4f9
Showing 1 changed file with 57 additions and 86 deletions.
143 changes: 57 additions & 86 deletions Tools/clinic/clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2439,6 +2439,8 @@ def __repr__(self) -> str:
ParamDict = dict[str, "Parameter"]
ReturnConverterType = Callable[..., "CReturnConverter"]


@dc.dataclass(repr=False)
class Function:
"""
Mutable duck type for inspect.Function.
Expand All @@ -2450,49 +2452,34 @@ class Function:
It will always be true that
(not docstring) or ((not docstring[0].isspace()) and (docstring.rstrip() == docstring))
"""
parameters: ParamDict = dc.field(default_factory=dict)
_: dc.KW_ONLY
name: str
module: Module
cls: Class | None = None
c_basename: str | None = None
full_name: str | None = None
return_converter: CReturnConverter
return_annotation: object = inspect.Signature.empty
docstring: str = ''
kind: str = CALLABLE
coexist: bool = False
# docstring_only means "don't generate a machine-readable
# signature, just a normal docstring". it's True for
# functions with optional groups because we can't represent
# those accurately with inspect.Signature in 3.4.
docstring_only: bool = False

def __init__(
self,
parameters: ParamDict | None = None,
*,
name: str,
module: Module,
cls: Class | None = None,
c_basename: str | None = None,
full_name: str | None = None,
return_converter: CReturnConverter,
return_annotation = inspect.Signature.empty,
docstring: str | None = None,
kind: str = CALLABLE,
coexist: bool = False,
docstring_only: bool = False
) -> None:
self.parameters = parameters or {}
self.return_annotation = return_annotation
self.name = name
self.full_name = full_name
self.module = module
self.cls = cls
self.parent = cls or module
self.c_basename = c_basename
self.return_converter = return_converter
self.docstring = docstring or ''
self.kind = kind
self.coexist = coexist
def __post_init__(self) -> None:
self.parent: Class | Module = self.cls or self.module
self.self_converter: self_converter | None = None
# docstring_only means "don't generate a machine-readable
# signature, just a normal docstring". it's True for
# functions with optional groups because we can't represent
# those accurately with inspect.Signature in 3.4.
self.docstring_only = docstring_only

self.rendered_parameters = None
self.__render_parameters__: list[Parameter] | None = None

__render_parameters__ = None
@property
def render_parameters(self):
def render_parameters(self) -> list[Parameter]:
if not self.__render_parameters__:
self.__render_parameters__ = l = []
l: list[Parameter] = []
self.__render_parameters__ = l
for p in self.parameters.values():
p = p.copy()
p.converter.pre_render()
Expand All @@ -2517,49 +2504,30 @@ def methoddef_flags(self) -> str | None:
def __repr__(self) -> str:
return '<clinic.Function ' + self.name + '>'

def copy(self, **overrides) -> "Function":
kwargs = {
'name': self.name, 'module': self.module, 'parameters': self.parameters,
'cls': self.cls, 'c_basename': self.c_basename,
'full_name': self.full_name,
'return_converter': self.return_converter, 'return_annotation': self.return_annotation,
'docstring': self.docstring, 'kind': self.kind, 'coexist': self.coexist,
'docstring_only': self.docstring_only,
}
kwargs.update(overrides)
f = Function(**kwargs)
def copy(self, **overrides: Any) -> Function:
f = dc.replace(self, **overrides)
f.parameters = {
name: value.copy(function=f)
for name, value in f.parameters.items()
}
return f


@dc.dataclass(repr=False, slots=True)
class Parameter:
"""
Mutable duck type of inspect.Parameter.
"""

def __init__(
self,
name: str,
kind: inspect._ParameterKind,
*,
default = inspect.Parameter.empty,
function: Function,
converter: "CConverter",
annotation = inspect.Parameter.empty,
docstring: str | None = None,
group: int = 0
) -> None:
self.name = name
self.kind = kind
self.default = default
self.function = function
self.converter = converter
self.annotation = annotation
self.docstring = docstring or ''
self.group = group
name: str
kind: inspect._ParameterKind
_: dc.KW_ONLY
default: object = inspect.Parameter.empty
function: Function
converter: CConverter
annotation: object = inspect.Parameter.empty
docstring: str = ''
group: int = 0
right_bracket_count: int = dc.field(init=False, default=0)

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

def copy(self, **overrides) -> "Parameter":
kwargs = {
'name': self.name, 'kind': self.kind, 'default':self.default,
'function': self.function, 'converter': self.converter, 'annotation': self.annotation,
'docstring': self.docstring, 'group': self.group,
}
kwargs.update(overrides)
if 'converter' not in overrides:
def copy(
self,
/,
*,
converter: CConverter | None = None,
function: Function | None = None,
**overrides: Any
) -> Parameter:
function = function or self.function
if not converter:
converter = copy.copy(self.converter)
converter.function = kwargs['function']
kwargs['converter'] = converter
return Parameter(**kwargs)
converter.function = function
return dc.replace(self, **overrides, function=function, converter=converter)

def get_displayname(self, i: int) -> str:
if i == 0:
Expand Down Expand Up @@ -2761,7 +2730,7 @@ def __init__(self,
# Positional args:
name: str,
py_name: str,
function,
function: Function,
default: object = unspecified,
*, # Keyword only args:
c_default: str | None = None,
Expand Down Expand Up @@ -2800,7 +2769,9 @@ def __init__(self,
# about the function in the init.
# (that breaks if we get cloned.)
# so after this change we will noisily fail.
self.function = LandMine("Don't access members of self.function inside converter_init!")
self.function: Function | LandMine = LandMine(
"Don't access members of self.function inside converter_init!"
)
self.converter_init(**kwargs)
self.function = function

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

def _render_self(self, parameter: str, data: CRenderData) -> None:
def _render_self(self, parameter: Parameter, data: CRenderData) -> None:
self.parameter = parameter
name = self.parser_name

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

def render(self, parameter: str, data: CRenderData) -> None:
def render(self, parameter: Parameter, data: CRenderData) -> None:
"""
parameter is a clinic.Parameter instance.
data is a CRenderData instance.
Expand Down Expand Up @@ -5246,7 +5217,7 @@ def format_docstring(self):
assert isinstance(parameters[0].converter, self_converter)
# self is always positional-only.
assert parameters[0].is_positional_only()
parameters[0].right_bracket_count = 0
assert parameters[0].right_bracket_count == 0
positional_only = True
for p in parameters[1:]:
if not p.is_positional_only():
Expand Down

0 comments on commit 363f4f9

Please sign in to comment.