summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/clinic.test.c34
-rw-r--r--Lib/test/test_clinic.py52
-rw-r--r--Modules/_io/bufferedio.c18
-rw-r--r--Modules/_io/clinic/bufferedio.c.h26
-rw-r--r--Modules/_io/clinic/stringio.c.h26
-rw-r--r--Modules/_io/clinic/textio.c.h46
-rw-r--r--Modules/_io/stringio.c6
-rw-r--r--Modules/_io/textio.c43
-rwxr-xr-xTools/clinic/clinic.py75
9 files changed, 262 insertions, 64 deletions
diff --git a/Lib/test/clinic.test.c b/Lib/test/clinic.test.c
index ee4a422..a6a2166 100644
--- a/Lib/test/clinic.test.c
+++ b/Lib/test/clinic.test.c
@@ -4956,8 +4956,12 @@ Test_meth_coexist_impl(TestObj *self)
Test.property
[clinic start generated code]*/
-#define TEST_PROPERTY_GETTERDEF \
- {"property", (getter)Test_property_get, NULL, NULL},
+#if defined(TEST_PROPERTY_GETSETDEF)
+# undef TEST_PROPERTY_GETSETDEF
+# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, (setter)Test_property_set, NULL},
+#else
+# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, NULL, NULL},
+#endif
static PyObject *
Test_property_get_impl(TestObj *self);
@@ -4970,8 +4974,32 @@ Test_property_get(TestObj *self, void *Py_UNUSED(context))
static PyObject *
Test_property_get_impl(TestObj *self)
-/*[clinic end generated code: output=892b6fb351ff85fd input=2d92b3449fbc7d2b]*/
+/*[clinic end generated code: output=af8140b692e0e2f1 input=2d92b3449fbc7d2b]*/
+
+/*[clinic input]
+@setter
+Test.property
+[clinic start generated code]*/
+
+#if defined(TEST_PROPERTY_GETSETDEF)
+# undef TEST_PROPERTY_GETSETDEF
+# define TEST_PROPERTY_GETSETDEF {"property", (getter)Test_property_get, (setter)Test_property_set, NULL},
+#else
+# define TEST_PROPERTY_GETSETDEF {"property", NULL, (setter)Test_property_set, NULL},
+#endif
+
+static int
+Test_property_set_impl(TestObj *self, PyObject *value);
+
+static int
+Test_property_set(TestObj *self, PyObject *value, void *Py_UNUSED(context))
+{
+ return Test_property_set_impl(self, value);
+}
+static int
+Test_property_set_impl(TestObj *self, PyObject *value)
+/*[clinic end generated code: output=f3eba6487d7550e2 input=3bc3f46a23c83a88]*/
/*[clinic input]
output push
diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py
index f53e948..d3dbde8 100644
--- a/Lib/test/test_clinic.py
+++ b/Lib/test/test_clinic.py
@@ -2197,6 +2197,58 @@ class ClinicParserTest(TestCase):
expected_error = err_template.format(invalid_kind)
self.expect_failure(block, expected_error, lineno=3)
+ def test_invalid_getset(self):
+ annotations = ["@getter", "@setter"]
+ for annotation in annotations:
+ with self.subTest(annotation=annotation):
+ block = f"""
+ module foo
+ class Foo "" ""
+ {annotation}
+ Foo.property -> int
+ """
+ expected_error = f"{annotation} method cannot define a return type"
+ self.expect_failure(block, expected_error, lineno=3)
+
+ block = f"""
+ module foo
+ class Foo "" ""
+ {annotation}
+ Foo.property
+ obj: int
+ /
+ """
+ expected_error = f"{annotation} method cannot define parameters"
+ self.expect_failure(block, expected_error)
+
+ def test_duplicate_getset(self):
+ annotations = ["@getter", "@setter"]
+ for annotation in annotations:
+ with self.subTest(annotation=annotation):
+ block = f"""
+ module foo
+ class Foo "" ""
+ {annotation}
+ {annotation}
+ Foo.property -> int
+ """
+ expected_error = f"Cannot apply {annotation} twice to the same function!"
+ self.expect_failure(block, expected_error, lineno=3)
+
+ def test_getter_and_setter_disallowed_on_same_function(self):
+ dup_annotations = [("@getter", "@setter"), ("@setter", "@getter")]
+ for dup in dup_annotations:
+ with self.subTest(dup=dup):
+ block = f"""
+ module foo
+ class Foo "" ""
+ {dup[0]}
+ {dup[1]}
+ Foo.property -> int
+ """
+ expected_error = "Cannot apply both @getter and @setter to the same function!"
+ self.expect_failure(block, expected_error, lineno=3)
+
def test_duplicate_coexist(self):
err = "Called @coexist twice"
block = """
diff --git a/Modules/_io/bufferedio.c b/Modules/_io/bufferedio.c
index 6796268..f02207a 100644
--- a/Modules/_io/bufferedio.c
+++ b/Modules/_io/bufferedio.c
@@ -2526,9 +2526,9 @@ static PyMemberDef bufferedreader_members[] = {
};
static PyGetSetDef bufferedreader_getset[] = {
- _IO__BUFFERED_CLOSED_GETTERDEF
- _IO__BUFFERED_NAME_GETTERDEF
- _IO__BUFFERED_MODE_GETTERDEF
+ _IO__BUFFERED_CLOSED_GETSETDEF
+ _IO__BUFFERED_NAME_GETSETDEF
+ _IO__BUFFERED_MODE_GETSETDEF
{NULL}
};
@@ -2586,9 +2586,9 @@ static PyMemberDef bufferedwriter_members[] = {
};
static PyGetSetDef bufferedwriter_getset[] = {
- _IO__BUFFERED_CLOSED_GETTERDEF
- _IO__BUFFERED_NAME_GETTERDEF
- _IO__BUFFERED_MODE_GETTERDEF
+ _IO__BUFFERED_CLOSED_GETSETDEF
+ _IO__BUFFERED_NAME_GETSETDEF
+ _IO__BUFFERED_MODE_GETSETDEF
{NULL}
};
@@ -2704,9 +2704,9 @@ static PyMemberDef bufferedrandom_members[] = {
};
static PyGetSetDef bufferedrandom_getset[] = {
- _IO__BUFFERED_CLOSED_GETTERDEF
- _IO__BUFFERED_NAME_GETTERDEF
- _IO__BUFFERED_MODE_GETTERDEF
+ _IO__BUFFERED_CLOSED_GETSETDEF
+ _IO__BUFFERED_NAME_GETSETDEF
+ _IO__BUFFERED_MODE_GETSETDEF
{NULL}
};
diff --git a/Modules/_io/clinic/bufferedio.c.h b/Modules/_io/clinic/bufferedio.c.h
index 69d28ad..ec46d54 100644
--- a/Modules/_io/clinic/bufferedio.c.h
+++ b/Modules/_io/clinic/bufferedio.c.h
@@ -327,8 +327,12 @@ _io__Buffered_simple_flush(buffered *self, PyObject *Py_UNUSED(ignored))
return return_value;
}
-#define _IO__BUFFERED_CLOSED_GETTERDEF \
- {"closed", (getter)_io__Buffered_closed_get, NULL, NULL},
+#if defined(_IO__BUFFERED_CLOSED_GETSETDEF)
+# undef _IO__BUFFERED_CLOSED_GETSETDEF
+# define _IO__BUFFERED_CLOSED_GETSETDEF {"closed", (getter)_io__Buffered_closed_get, (setter)_io__Buffered_closed_set, NULL},
+#else
+# define _IO__BUFFERED_CLOSED_GETSETDEF {"closed", (getter)_io__Buffered_closed_get, NULL, NULL},
+#endif
static PyObject *
_io__Buffered_closed_get_impl(buffered *self);
@@ -460,8 +464,12 @@ _io__Buffered_writable(buffered *self, PyObject *Py_UNUSED(ignored))
return return_value;
}
-#define _IO__BUFFERED_NAME_GETTERDEF \
- {"name", (getter)_io__Buffered_name_get, NULL, NULL},
+#if defined(_IO__BUFFERED_NAME_GETSETDEF)
+# undef _IO__BUFFERED_NAME_GETSETDEF
+# define _IO__BUFFERED_NAME_GETSETDEF {"name", (getter)_io__Buffered_name_get, (setter)_io__Buffered_name_set, NULL},
+#else
+# define _IO__BUFFERED_NAME_GETSETDEF {"name", (getter)_io__Buffered_name_get, NULL, NULL},
+#endif
static PyObject *
_io__Buffered_name_get_impl(buffered *self);
@@ -478,8 +486,12 @@ _io__Buffered_name_get(buffered *self, void *Py_UNUSED(context))
return return_value;
}
-#define _IO__BUFFERED_MODE_GETTERDEF \
- {"mode", (getter)_io__Buffered_mode_get, NULL, NULL},
+#if defined(_IO__BUFFERED_MODE_GETSETDEF)
+# undef _IO__BUFFERED_MODE_GETSETDEF
+# define _IO__BUFFERED_MODE_GETSETDEF {"mode", (getter)_io__Buffered_mode_get, (setter)_io__Buffered_mode_set, NULL},
+#else
+# define _IO__BUFFERED_MODE_GETSETDEF {"mode", (getter)_io__Buffered_mode_get, NULL, NULL},
+#endif
static PyObject *
_io__Buffered_mode_get_impl(buffered *self);
@@ -1218,4 +1230,4 @@ skip_optional_pos:
exit:
return return_value;
}
-/*[clinic end generated code: output=f21ed03255032b43 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=0999c33f666dc692 input=a9049054013a1b77]*/
diff --git a/Modules/_io/clinic/stringio.c.h b/Modules/_io/clinic/stringio.c.h
index ed505ae..fc2962d 100644
--- a/Modules/_io/clinic/stringio.c.h
+++ b/Modules/_io/clinic/stringio.c.h
@@ -475,8 +475,12 @@ _io_StringIO___setstate__(stringio *self, PyObject *state)
return return_value;
}
-#define _IO_STRINGIO_CLOSED_GETTERDEF \
- {"closed", (getter)_io_StringIO_closed_get, NULL, NULL},
+#if defined(_IO_STRINGIO_CLOSED_GETSETDEF)
+# undef _IO_STRINGIO_CLOSED_GETSETDEF
+# define _IO_STRINGIO_CLOSED_GETSETDEF {"closed", (getter)_io_StringIO_closed_get, (setter)_io_StringIO_closed_set, NULL},
+#else
+# define _IO_STRINGIO_CLOSED_GETSETDEF {"closed", (getter)_io_StringIO_closed_get, NULL, NULL},
+#endif
static PyObject *
_io_StringIO_closed_get_impl(stringio *self);
@@ -493,8 +497,12 @@ _io_StringIO_closed_get(stringio *self, void *Py_UNUSED(context))
return return_value;
}
-#define _IO_STRINGIO_LINE_BUFFERING_GETTERDEF \
- {"line_buffering", (getter)_io_StringIO_line_buffering_get, NULL, NULL},
+#if defined(_IO_STRINGIO_LINE_BUFFERING_GETSETDEF)
+# undef _IO_STRINGIO_LINE_BUFFERING_GETSETDEF
+# define _IO_STRINGIO_LINE_BUFFERING_GETSETDEF {"line_buffering", (getter)_io_StringIO_line_buffering_get, (setter)_io_StringIO_line_buffering_set, NULL},
+#else
+# define _IO_STRINGIO_LINE_BUFFERING_GETSETDEF {"line_buffering", (getter)_io_StringIO_line_buffering_get, NULL, NULL},
+#endif
static PyObject *
_io_StringIO_line_buffering_get_impl(stringio *self);
@@ -511,8 +519,12 @@ _io_StringIO_line_buffering_get(stringio *self, void *Py_UNUSED(context))
return return_value;
}
-#define _IO_STRINGIO_NEWLINES_GETTERDEF \
- {"newlines", (getter)_io_StringIO_newlines_get, NULL, NULL},
+#if defined(_IO_STRINGIO_NEWLINES_GETSETDEF)
+# undef _IO_STRINGIO_NEWLINES_GETSETDEF
+# define _IO_STRINGIO_NEWLINES_GETSETDEF {"newlines", (getter)_io_StringIO_newlines_get, (setter)_io_StringIO_newlines_set, NULL},
+#else
+# define _IO_STRINGIO_NEWLINES_GETSETDEF {"newlines", (getter)_io_StringIO_newlines_get, NULL, NULL},
+#endif
static PyObject *
_io_StringIO_newlines_get_impl(stringio *self);
@@ -528,4 +540,4 @@ _io_StringIO_newlines_get(stringio *self, void *Py_UNUSED(context))
return return_value;
}
-/*[clinic end generated code: output=3a92e8b6c322f61b input=a9049054013a1b77]*/
+/*[clinic end generated code: output=27726751d98ab617 input=a9049054013a1b77]*/
diff --git a/Modules/_io/clinic/textio.c.h b/Modules/_io/clinic/textio.c.h
index 675e0ed..a492f34 100644
--- a/Modules/_io/clinic/textio.c.h
+++ b/Modules/_io/clinic/textio.c.h
@@ -1047,4 +1047,48 @@ _io_TextIOWrapper_close(textio *self, PyObject *Py_UNUSED(ignored))
return return_value;
}
-/*[clinic end generated code: output=8781a91be6d99e2c input=a9049054013a1b77]*/
+
+#if defined(_IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF)
+# undef _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF
+# define _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF {"_CHUNK_SIZE", (getter)_io_TextIOWrapper__CHUNK_SIZE_get, (setter)_io_TextIOWrapper__CHUNK_SIZE_set, NULL},
+#else
+# define _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF {"_CHUNK_SIZE", (getter)_io_TextIOWrapper__CHUNK_SIZE_get, NULL, NULL},
+#endif
+
+static PyObject *
+_io_TextIOWrapper__CHUNK_SIZE_get_impl(textio *self);
+
+static PyObject *
+_io_TextIOWrapper__CHUNK_SIZE_get(textio *self, void *Py_UNUSED(context))
+{
+ PyObject *return_value = NULL;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_TextIOWrapper__CHUNK_SIZE_get_impl(self);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+
+#if defined(_IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF)
+# undef _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF
+# define _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF {"_CHUNK_SIZE", (getter)_io_TextIOWrapper__CHUNK_SIZE_get, (setter)_io_TextIOWrapper__CHUNK_SIZE_set, NULL},
+#else
+# define _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF {"_CHUNK_SIZE", NULL, (setter)_io_TextIOWrapper__CHUNK_SIZE_set, NULL},
+#endif
+
+static int
+_io_TextIOWrapper__CHUNK_SIZE_set_impl(textio *self, PyObject *value);
+
+static int
+_io_TextIOWrapper__CHUNK_SIZE_set(textio *self, PyObject *value, void *Py_UNUSED(context))
+{
+ int return_value;
+
+ Py_BEGIN_CRITICAL_SECTION(self);
+ return_value = _io_TextIOWrapper__CHUNK_SIZE_set_impl(self, value);
+ Py_END_CRITICAL_SECTION();
+
+ return return_value;
+}
+/*[clinic end generated code: output=b312f2d2e2221580 input=a9049054013a1b77]*/
diff --git a/Modules/_io/stringio.c b/Modules/_io/stringio.c
index 74dcee2..06bc267 100644
--- a/Modules/_io/stringio.c
+++ b/Modules/_io/stringio.c
@@ -1037,15 +1037,15 @@ static struct PyMethodDef stringio_methods[] = {
};
static PyGetSetDef stringio_getset[] = {
- _IO_STRINGIO_CLOSED_GETTERDEF
- _IO_STRINGIO_NEWLINES_GETTERDEF
+ _IO_STRINGIO_CLOSED_GETSETDEF
+ _IO_STRINGIO_NEWLINES_GETSETDEF
/* (following comments straight off of the original Python wrapper:)
XXX Cruft to support the TextIOWrapper API. This would only
be meaningful if StringIO supported the buffer attribute.
Hopefully, a better solution, than adding these pseudo-attributes,
will be found.
*/
- _IO_STRINGIO_LINE_BUFFERING_GETTERDEF
+ _IO_STRINGIO_LINE_BUFFERING_GETSETDEF
{NULL}
};
diff --git a/Modules/_io/textio.c b/Modules/_io/textio.c
index 545f467..c76d92c 100644
--- a/Modules/_io/textio.c
+++ b/Modules/_io/textio.c
@@ -3238,33 +3238,37 @@ textiowrapper_errors_get(textio *self, void *context)
return result;
}
+/*[clinic input]
+@critical_section
+@getter
+_io.TextIOWrapper._CHUNK_SIZE
+[clinic start generated code]*/
+
static PyObject *
-textiowrapper_chunk_size_get_impl(textio *self, void *context)
+_io_TextIOWrapper__CHUNK_SIZE_get_impl(textio *self)
+/*[clinic end generated code: output=039925cd2df375bc input=e9715b0e06ff0fa6]*/
{
CHECK_ATTACHED(self);
return PyLong_FromSsize_t(self->chunk_size);
}
-static PyObject *
-textiowrapper_chunk_size_get(textio *self, void *context)
-{
- PyObject *result = NULL;
- Py_BEGIN_CRITICAL_SECTION(self);
- result = textiowrapper_chunk_size_get_impl(self, context);
- Py_END_CRITICAL_SECTION();
- return result;
-}
+/*[clinic input]
+@critical_section
+@setter
+_io.TextIOWrapper._CHUNK_SIZE
+[clinic start generated code]*/
static int
-textiowrapper_chunk_size_set_impl(textio *self, PyObject *arg, void *context)
+_io_TextIOWrapper__CHUNK_SIZE_set_impl(textio *self, PyObject *value)
+/*[clinic end generated code: output=edb86d2db660a5ab input=32fc99861db02a0a]*/
{
Py_ssize_t n;
CHECK_ATTACHED_INT(self);
- if (arg == NULL) {
+ if (value == NULL) {
PyErr_SetString(PyExc_AttributeError, "cannot delete attribute");
return -1;
}
- n = PyNumber_AsSsize_t(arg, PyExc_ValueError);
+ n = PyNumber_AsSsize_t(value, PyExc_ValueError);
if (n == -1 && PyErr_Occurred())
return -1;
if (n <= 0) {
@@ -3276,16 +3280,6 @@ textiowrapper_chunk_size_set_impl(textio *self, PyObject *arg, void *context)
return 0;
}
-static int
-textiowrapper_chunk_size_set(textio *self, PyObject *arg, void *context)
-{
- int result = 0;
- Py_BEGIN_CRITICAL_SECTION(self);
- result = textiowrapper_chunk_size_set_impl(self, arg, context);
- Py_END_CRITICAL_SECTION();
- return result;
-}
-
static PyMethodDef incrementalnewlinedecoder_methods[] = {
_IO_INCREMENTALNEWLINEDECODER_DECODE_METHODDEF
_IO_INCREMENTALNEWLINEDECODER_GETSTATE_METHODDEF
@@ -3361,8 +3355,7 @@ static PyGetSetDef textiowrapper_getset[] = {
*/
{"newlines", (getter)textiowrapper_newlines_get, NULL, NULL},
{"errors", (getter)textiowrapper_errors_get, NULL, NULL},
- {"_CHUNK_SIZE", (getter)textiowrapper_chunk_size_get,
- (setter)textiowrapper_chunk_size_set, NULL},
+ _IO_TEXTIOWRAPPER__CHUNK_SIZE_GETSETDEF
{NULL}
};
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)