summaryrefslogtreecommitdiffstats
path: root/Tools/clinic
diff options
context:
space:
mode:
authorDonghee Na <donghee.na@python.org>2023-11-30 10:40:53 (GMT)
committerGitHub <noreply@github.com>2023-11-30 10:40:53 (GMT)
commit7eeea13403882af63a71226433c9a13b80c22564 (patch)
tree976e1a1589211c9731fc6fec94934d415991837a /Tools/clinic
parent0785c685599aaa052f85d6163872bdecb9c66486 (diff)
downloadcpython-7eeea13403882af63a71226433c9a13b80c22564.zip
cpython-7eeea13403882af63a71226433c9a13b80c22564.tar.gz
cpython-7eeea13403882af63a71226433c9a13b80c22564.tar.bz2
gh-112205: Support @getter annotation from AC (gh-112396)
Diffstat (limited to 'Tools/clinic')
-rwxr-xr-xTools/clinic/clinic.py53
1 files changed, 43 insertions, 10 deletions
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index c083086..54962c9 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -846,6 +846,10 @@ class CLanguage(Language):
static PyObject *
{c_basename}({self_type}{self_name}, PyObject *Py_UNUSED(ignored))
""")
+ PARSER_PROTOTYPE_GETTER: Final[str] = normalize_snippet("""
+ static PyObject *
+ {c_basename}({self_type}{self_name}, void *Py_UNUSED(context))
+ """)
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({impl_parameters})
@@ -865,6 +869,10 @@ class CLanguage(Language):
#define {methoddef_name} \
{{"{name}", {methoddef_cast}{c_basename}{methoddef_cast_end}, {methoddef_flags}, {c_basename}__doc__}},
""")
+ GETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
+ #define {getter_name} \
+ {{"{name}", (getter){c_basename}, NULL, NULL}},
+ """)
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
#ifndef {methoddef_name}
#define {methoddef_name}
@@ -1161,6 +1169,9 @@ class CLanguage(Language):
methoddef_define = self.METHODDEF_PROTOTYPE_DEFINE
if new_or_init and not f.docstring:
docstring_prototype = docstring_definition = ''
+ elif f.kind is GETTER:
+ methoddef_define = self.GETTERDEF_PROTOTYPE_DEFINE
+ docstring_prototype = docstring_definition = ''
else:
docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR
docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR
@@ -1217,7 +1228,11 @@ class CLanguage(Language):
parsearg: str | None
if not parameters:
parser_code: list[str] | None
- if not requires_defining_class:
+ if f.kind is GETTER:
+ flags = "" # This should end up unused
+ parser_prototype = self.PARSER_PROTOTYPE_GETTER
+ parser_code = []
+ elif not requires_defining_class:
# no parameters, METH_NOARGS
flags = "METH_NOARGS"
parser_prototype = self.PARSER_PROTOTYPE_NOARGS
@@ -1670,6 +1685,8 @@ class CLanguage(Language):
methoddef_cast_end = ""
if flags in ('METH_NOARGS', 'METH_O', 'METH_VARARGS'):
methoddef_cast = "(PyCFunction)"
+ elif f.kind is GETTER:
+ methoddef_cast = "" # This should end up unused
elif limited_capi:
methoddef_cast = "(PyCFunction)(void(*)(void))"
else:
@@ -1927,8 +1944,12 @@ class CLanguage(Language):
full_name = f.full_name
template_dict = {'full_name': full_name}
template_dict['name'] = f.displayname
- template_dict['c_basename'] = f.c_basename
- template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF"
+ if f.kind is GETTER:
+ template_dict['getter_name'] = f.c_basename.upper() + "_GETTERDEF"
+ template_dict['c_basename'] = f.c_basename + "_get"
+ else:
+ template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF"
+ template_dict['c_basename'] = f.c_basename
template_dict['docstring'] = self.docstring_for_c_string(f)
@@ -2932,6 +2953,7 @@ class FunctionKind(enum.Enum):
CLASS_METHOD = enum.auto()
METHOD_INIT = enum.auto()
METHOD_NEW = enum.auto()
+ GETTER = enum.auto()
@functools.cached_property
def new_or_init(self) -> bool:
@@ -2947,6 +2969,7 @@ STATIC_METHOD: Final = FunctionKind.STATIC_METHOD
CLASS_METHOD: Final = FunctionKind.CLASS_METHOD
METHOD_INIT: Final = FunctionKind.METHOD_INIT
METHOD_NEW: Final = FunctionKind.METHOD_NEW
+GETTER: Final = FunctionKind.GETTER
ParamDict = dict[str, "Parameter"]
ReturnConverterType = Callable[..., "CReturnConverter"]
@@ -3033,7 +3056,8 @@ class Function:
case FunctionKind.STATIC_METHOD:
flags.append('METH_STATIC')
case _ as kind:
- assert kind is FunctionKind.CALLABLE, f"unknown kind: {kind!r}"
+ acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER}
+ assert kind in acceptable_kinds, f"unknown kind: {kind!r}"
if self.coexist:
flags.append('METH_COEXIST')
return '|'.join(flags)
@@ -4678,7 +4702,7 @@ class Py_buffer_converter(CConverter):
def correct_name_for_self(
f: Function
) -> tuple[str, str]:
- if f.kind in (CALLABLE, METHOD_INIT):
+ if f.kind in {CALLABLE, METHOD_INIT, GETTER}:
if f.cls:
return "PyObject *", "self"
return "PyObject *", "module"
@@ -5310,6 +5334,9 @@ class DSLParser:
self.target_critical_section.extend(args)
self.critical_section = True
+ def at_getter(self) -> None:
+ self.kind = GETTER
+
def at_staticmethod(self) -> None:
if self.kind is not CALLABLE:
fail("Can't set @staticmethod, function is not a normal callable")
@@ -5419,14 +5446,20 @@ class DSLParser:
_, cls = self.clinic._module_and_class(fields)
if name in unsupported_special_methods:
fail(f"{name!r} is a special method and cannot be converted to Argument Clinic!")
+
if name == '__new__':
- if (self.kind is not CLASS_METHOD) or (not cls):
+ if (self.kind is CLASS_METHOD) and cls:
+ self.kind = METHOD_NEW
+ else:
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
+ if (self.kind is CALLABLE) and cls:
+ self.kind = METHOD_INIT
+ else:
+ fail(
+ "'__init__' must be a normal method; "
+ f"got '{self.kind}'!"
+ )
def state_modulename_name(self, line: str) -> None:
# looking for declaration, which establishes the leftmost column