summaryrefslogtreecommitdiffstats
path: root/Tools
diff options
context:
space:
mode:
authorDonghee Na <donghee.na@python.org>2023-12-13 14:00:34 (GMT)
committerGitHub <noreply@github.com>2023-12-13 14:00:34 (GMT)
commit498a096a51a215cd3084845131e619222b906b3e (patch)
tree9c14ead533097de8f8fbae449de4f6cbe91c9729 /Tools
parent9263173280d7ce949911965efa5b745287c581b2 (diff)
downloadcpython-498a096a51a215cd3084845131e619222b906b3e.zip
cpython-498a096a51a215cd3084845131e619222b906b3e.tar.gz
cpython-498a096a51a215cd3084845131e619222b906b3e.tar.bz2
gh-112205: Support `@setter` annotation from AC (gh-112922)
--------- Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com> Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
Diffstat (limited to 'Tools')
-rwxr-xr-xTools/clinic/clinic.py75
1 files changed, 66 insertions, 9 deletions
diff --git a/Tools/clinic/clinic.py b/Tools/clinic/clinic.py
index 816ce0e..5ec0887 100755
--- a/Tools/clinic/clinic.py
+++ b/Tools/clinic/clinic.py
@@ -850,6 +850,10 @@ class CLanguage(Language):
static PyObject *
{c_basename}({self_type}{self_name}, void *Py_UNUSED(context))
""")
+ PARSER_PROTOTYPE_SETTER: Final[str] = normalize_snippet("""
+ static int
+ {c_basename}({self_type}{self_name}, PyObject *value, void *Py_UNUSED(context))
+ """)
METH_O_PROTOTYPE: Final[str] = normalize_snippet("""
static PyObject *
{c_basename}({impl_parameters})
@@ -870,8 +874,20 @@ class CLanguage(Language):
{{"{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}},
+ #if defined({getset_name}_GETSETDEF)
+ # undef {getset_name}_GETSETDEF
+ # define {getset_name}_GETSETDEF {{"{name}", (getter){getset_basename}_get, (setter){getset_basename}_set, NULL}},
+ #else
+ # define {getset_name}_GETSETDEF {{"{name}", (getter){getset_basename}_get, NULL, NULL}},
+ #endif
+ """)
+ SETTERDEF_PROTOTYPE_DEFINE: Final[str] = normalize_snippet(r"""
+ #if defined({getset_name}_GETSETDEF)
+ # undef {getset_name}_GETSETDEF
+ # define {getset_name}_GETSETDEF {{"{name}", (getter){getset_basename}_get, (setter){getset_basename}_set, NULL}},
+ #else
+ # define {getset_name}_GETSETDEF {{"{name}", NULL, (setter){getset_basename}_set, NULL}},
+ #endif
""")
METHODDEF_PROTOTYPE_IFNDEF: Final[str] = normalize_snippet("""
#ifndef {methoddef_name}
@@ -1172,6 +1188,10 @@ class CLanguage(Language):
elif f.kind is GETTER:
methoddef_define = self.GETTERDEF_PROTOTYPE_DEFINE
docstring_prototype = docstring_definition = ''
+ elif f.kind is SETTER:
+ return_value_declaration = "int {return_value};"
+ methoddef_define = self.SETTERDEF_PROTOTYPE_DEFINE
+ docstring_prototype = docstring_prototype = docstring_definition = ''
else:
docstring_prototype = self.DOCSTRING_PROTOTYPE_VAR
docstring_definition = self.DOCSTRING_PROTOTYPE_STRVAR
@@ -1226,12 +1246,19 @@ class CLanguage(Language):
limited_capi = False
parsearg: str | None
+ if f.kind in {GETTER, SETTER} and parameters:
+ fail(f"@{f.kind.name.lower()} method cannot define parameters")
+
if not parameters:
parser_code: list[str] | None
if f.kind is GETTER:
flags = "" # This should end up unused
parser_prototype = self.PARSER_PROTOTYPE_GETTER
parser_code = []
+ elif f.kind is SETTER:
+ flags = ""
+ parser_prototype = self.PARSER_PROTOTYPE_SETTER
+ parser_code = []
elif not requires_defining_class:
# no parameters, METH_NOARGS
flags = "METH_NOARGS"
@@ -1944,9 +1971,16 @@ class CLanguage(Language):
full_name = f.full_name
template_dict = {'full_name': full_name}
template_dict['name'] = f.displayname
- if f.kind is GETTER:
- template_dict['getter_name'] = f.c_basename.upper() + "_GETTERDEF"
- template_dict['c_basename'] = f.c_basename + "_get"
+ if f.kind in {GETTER, SETTER}:
+ template_dict['getset_name'] = f.c_basename.upper()
+ template_dict['getset_basename'] = f.c_basename
+ if f.kind is GETTER:
+ template_dict['c_basename'] = f.c_basename + "_get"
+ elif f.kind is SETTER:
+ template_dict['c_basename'] = f.c_basename + "_set"
+ # Implicitly add the setter value parameter.
+ data.impl_parameters.append("PyObject *value")
+ data.impl_arguments.append("value")
else:
template_dict['methoddef_name'] = f.c_basename.upper() + "_METHODDEF"
template_dict['c_basename'] = f.c_basename
@@ -1959,7 +1993,11 @@ class CLanguage(Language):
converter.set_template_dict(template_dict)
f.return_converter.render(f, data)
- template_dict['impl_return_type'] = f.return_converter.type
+ if f.kind is SETTER:
+ # All setters return an int.
+ template_dict['impl_return_type'] = 'int'
+ else:
+ template_dict['impl_return_type'] = f.return_converter.type
template_dict['declarations'] = format_escape("\n".join(data.declarations))
template_dict['initializers'] = "\n\n".join(data.initializers)
@@ -2954,6 +2992,7 @@ class FunctionKind(enum.Enum):
METHOD_INIT = enum.auto()
METHOD_NEW = enum.auto()
GETTER = enum.auto()
+ SETTER = enum.auto()
@functools.cached_property
def new_or_init(self) -> bool:
@@ -2970,6 +3009,7 @@ CLASS_METHOD: Final = FunctionKind.CLASS_METHOD
METHOD_INIT: Final = FunctionKind.METHOD_INIT
METHOD_NEW: Final = FunctionKind.METHOD_NEW
GETTER: Final = FunctionKind.GETTER
+SETTER: Final = FunctionKind.SETTER
ParamDict = dict[str, "Parameter"]
ReturnConverterType = Callable[..., "CReturnConverter"]
@@ -3056,7 +3096,7 @@ class Function:
case FunctionKind.STATIC_METHOD:
flags.append('METH_STATIC')
case _ as kind:
- acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER}
+ acceptable_kinds = {FunctionKind.CALLABLE, FunctionKind.GETTER, FunctionKind.SETTER}
assert kind in acceptable_kinds, f"unknown kind: {kind!r}"
if self.coexist:
flags.append('METH_COEXIST')
@@ -4702,7 +4742,7 @@ class Py_buffer_converter(CConverter):
def correct_name_for_self(
f: Function
) -> tuple[str, str]:
- if f.kind in {CALLABLE, METHOD_INIT, GETTER}:
+ if f.kind in {CALLABLE, METHOD_INIT, GETTER, SETTER}:
if f.cls:
return "PyObject *", "self"
return "PyObject *", "module"
@@ -5335,7 +5375,22 @@ class DSLParser:
self.critical_section = True
def at_getter(self) -> None:
- self.kind = GETTER
+ match self.kind:
+ case FunctionKind.GETTER:
+ fail("Cannot apply @getter twice to the same function!")
+ case FunctionKind.SETTER:
+ fail("Cannot apply both @getter and @setter to the same function!")
+ case _:
+ self.kind = FunctionKind.GETTER
+
+ def at_setter(self) -> None:
+ match self.kind:
+ case FunctionKind.SETTER:
+ fail("Cannot apply @setter twice to the same function!")
+ case FunctionKind.GETTER:
+ fail("Cannot apply both @getter and @setter to the same function!")
+ case _:
+ self.kind = FunctionKind.SETTER
def at_staticmethod(self) -> None:
if self.kind is not CALLABLE:
@@ -5536,6 +5591,8 @@ class DSLParser:
return_converter = None
if returns:
+ if self.kind in {GETTER, SETTER}:
+ fail(f"@{self.kind.name.lower()} method cannot define a return type")
ast_input = f"def x() -> {returns}: pass"
try:
module_node = ast.parse(ast_input)