diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index 38eabd2f7f21dc..c80c5d0c70cc92 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -3507,7 +3507,7 @@ def test_Function_and_Parameter_reprs(self): c_basename=None, full_name='foofoo', return_converter=clinic.init_return_converter(), - kind=clinic.FunctionKind.METHOD_INIT, + kind=clinic.FunctionKind.CALLABLE, coexist=False ) self.assertEqual(repr(function), "") diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py index 9f7c47430772f7..150f95594300d9 100755 --- a/Tools/clinic/clinic.py +++ b/Tools/clinic/clinic.py @@ -998,7 +998,7 @@ def output_templates( has_option_groups = parameters and (parameters[0].group or parameters[-1].group) default_return_converter = f.return_converter.type == 'PyObject *' - new_or_init = f.kind.new_or_init + new_or_init = f.new_or_init vararg: int | str = NO_VARARG pos_only = min_pos = max_pos = min_kw_only = pseudo_args = 0 @@ -1405,7 +1405,7 @@ def parser_body( if new_or_init: methoddef_define = '' - if f.kind is METHOD_NEW: + if f.name == "__new__": parser_prototype = self.PARSER_PROTOTYPE_KEYWORD else: return_value_declaration = "int return_value = -1;" @@ -1629,7 +1629,7 @@ def render_function( last_group = 0 first_optional = len(selfless) positional = selfless and selfless[-1].is_positional_only() - new_or_init = f.kind.new_or_init + new_or_init = f.new_or_init has_option_groups = False # offset i by -1 because first_optional needs to ignore self @@ -2604,12 +2604,6 @@ class FunctionKind(enum.Enum): CALLABLE = enum.auto() STATIC_METHOD = enum.auto() CLASS_METHOD = enum.auto() - METHOD_INIT = enum.auto() - METHOD_NEW = enum.auto() - - @functools.cached_property - def new_or_init(self) -> bool: - return self in {FunctionKind.METHOD_INIT, FunctionKind.METHOD_NEW} def __repr__(self) -> str: return f"" @@ -2619,8 +2613,6 @@ def __repr__(self) -> str: CALLABLE: Final = FunctionKind.CALLABLE STATIC_METHOD: Final = FunctionKind.STATIC_METHOD CLASS_METHOD: Final = FunctionKind.CLASS_METHOD -METHOD_INIT: Final = FunctionKind.METHOD_INIT -METHOD_NEW: Final = FunctionKind.METHOD_NEW ParamDict = dict[str, "Parameter"] ReturnConverterType = Callable[..., "CReturnConverter"] @@ -2661,10 +2653,14 @@ def __post_init__(self) -> None: self.self_converter: self_converter | None = None self.__render_parameters__: list[Parameter] | None = None + @functools.cached_property + def new_or_init(self) -> bool: + return self.name in ("__new__", "__init__") + @functools.cached_property def displayname(self) -> str: """Pretty-printable name.""" - if self.kind.new_or_init: + if self.new_or_init: assert isinstance(self.cls, Class) return self.cls.name else: @@ -2673,7 +2669,7 @@ def displayname(self) -> str: @functools.cached_property def fulldisplayname(self) -> str: parent: Class | Module | Clinic | None - if self.kind.new_or_init: + if self.new_or_init: parent = getattr(self.cls, "parent", None) else: parent = self.parent @@ -2696,7 +2692,7 @@ def render_parameters(self) -> list[Parameter]: @property def methoddef_flags(self) -> str | None: - if self.kind.new_or_init: + if self.new_or_init: return None flags = [] match self.kind: @@ -4127,13 +4123,13 @@ def parse_arg(self, argname: str, displayname: str) -> str | None: def correct_name_for_self( f: Function ) -> tuple[str, str]: - if f.kind in (CALLABLE, METHOD_INIT): + if f.kind is CALLABLE or f.name == "__init__": if f.cls: return "PyObject *", "self" return "PyObject *", "module" if f.kind is STATIC_METHOD: return "void *", "null" - if f.kind in (CLASS_METHOD, METHOD_NEW): + if f.kind is CLASS_METHOD: return "PyTypeObject *", "type" raise AssertionError(f"Unhandled type of function f: {f.kind!r}") @@ -4141,7 +4137,7 @@ def required_type_for_self_for_parser( f: Function ) -> str | None: type, _ = correct_name_for_self(f) - if f.kind in (METHOD_INIT, METHOD_NEW, STATIC_METHOD, CLASS_METHOD): + if f.kind in (STATIC_METHOD, CLASS_METHOD) or f.name == "__init__": return type return None @@ -4165,13 +4161,13 @@ def pre_render(self) -> None: kind = self.function.kind - if kind is STATIC_METHOD or kind.new_or_init: + if kind is STATIC_METHOD or f.new_or_init: self.show_in_signature = False - # tp_new (METHOD_NEW) functions are of type newfunc: + # tp_new functions are of type newfunc: # typedef PyObject *(*newfunc)(PyTypeObject *, PyObject *, PyObject *); # - # tp_init (METHOD_INIT) functions are of type initproc: + # tp_init functions are of type initproc: # typedef int (*initproc)(PyObject *, PyObject *, PyObject *); # # All other functions generated by Argument Clinic are stored in @@ -4189,18 +4185,18 @@ def pre_render(self) -> None: # # * The type of the first parameter to the impl will always be of self.type. # - # * If the function is neither tp_new (METHOD_NEW) nor tp_init (METHOD_INIT): + # * If the function is neither tp_new nor tp_init: # * The type of the first parameter to the parsing function is also self.type. # This means that if you step into the parsing function, your "self" parameter # is of the correct type, which may make debugging more pleasant. # - # * Else if the function is tp_new (METHOD_NEW): + # * Else if the function is tp_new: # * The type of the first parameter to the parsing function is "PyTypeObject *", # so the type signature of the function call is an exact match. # * If self.type != "PyTypeObject *", we cast the first parameter to self.type # in the impl call. # - # * Else if the function is tp_init (METHOD_INIT): + # * Else if the function is tp_init: # * The type of the first parameter to the parsing function is "PyObject *", # so the type signature of the function call is an exact match. # * If self.type != "PyObject *", we cast the first parameter to self.type @@ -4233,11 +4229,10 @@ def render(self, parameter: Parameter, data: CRenderData) -> None: def set_template_dict(self, template_dict: TemplateDict) -> None: template_dict['self_name'] = self.name template_dict['self_type'] = self.parser_type - kind = self.function.kind cls = self.function.cls - if kind.new_or_init and cls and cls.typedef: - if kind is METHOD_NEW: + if self.function.new_or_init and cls and cls.typedef: + if self.function.name == "__new__": type_check = ( '({0} == base_tp || {0}->tp_init == base_tp->tp_init)' ).format(self.name) @@ -4862,11 +4857,9 @@ def update_function_kind(self, fullname: str) -> None: if name == '__new__': if (self.kind is not CLASS_METHOD) or (not cls): fail("'__new__' must be a class method!") - self.kind = METHOD_NEW elif name == '__init__': if (self.kind is not CALLABLE) or (not cls): fail("'__init__' must be a normal method, not a class or static method!") - self.kind = METHOD_INIT def state_modulename_name(self, line: str) -> None: # looking for declaration, which establishes the leftmost column @@ -4920,16 +4913,15 @@ def state_modulename_name(self, line: str) -> None: "c_basename": c_basename, "docstring": "", } - if not (existing_function.kind is self.kind and + # Allow __new__ or __init__ methods. + if existing_function.new_or_init: + overrides["kind"] = self.kind + # Future enhancement: allow custom return converters + overrides["return_converter"] = CReturnConverter() + elif not (existing_function.kind is self.kind and existing_function.coexist == self.coexist): - # Allow __new__ or __init__ methods. - if existing_function.kind.new_or_init: - overrides["kind"] = self.kind - # Future enhancement: allow custom return converters - overrides["return_converter"] = CReturnConverter() - else: - fail("'kind' of function and cloned function don't match! " - "(@classmethod/@staticmethod/@coexist)") + fail("'kind' of function and cloned function don't match! " + "(@classmethod/@staticmethod/@coexist)") function = existing_function.copy(**overrides) self.function = function self.block.signatures.append(function) @@ -4965,7 +4957,7 @@ def state_modulename_name(self, line: str) -> None: module, cls = self.clinic._module_and_class(fields) self.update_function_kind(full_name) - if self.kind is METHOD_INIT and not return_converter: + if function_name == "__init__" and not return_converter: return_converter = init_return_converter() if not return_converter: @@ -5729,7 +5721,7 @@ def format_docstring_parameters(params: list[Parameter]) -> str: def format_docstring(self) -> str: assert self.function is not None f = self.function - if f.kind.new_or_init and not f.docstring: + if f.new_or_init and not f.docstring: # don't render a docstring at all, no signature, nothing. return f.docstring