Skip to content

Commit

Permalink
pythongh-112205: Support @setter annotation from AC (pythongh-112922)
Browse files Browse the repository at this point in the history
---------

Co-authored-by: Erlend E. Aasland <erlend.aasland@protonmail.com>
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
  • Loading branch information
3 people authored and aisk committed Feb 11, 2024
1 parent d3f9166 commit 6ad31bb
Show file tree
Hide file tree
Showing 9 changed files with 262 additions and 64 deletions.
34 changes: 31 additions & 3 deletions Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
Expand Down
52 changes: 52 additions & 0 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2197,6 +2197,58 @@ class Foo "" ""
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 = """
Expand Down
18 changes: 9 additions & 9 deletions Modules/_io/bufferedio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

Expand Down Expand Up @@ -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}
};

Expand Down Expand Up @@ -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}
};

Expand Down
26 changes: 19 additions & 7 deletions Modules/_io/clinic/bufferedio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 19 additions & 7 deletions Modules/_io/clinic/stringio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 45 additions & 1 deletion Modules/_io/clinic/textio.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Modules/_io/stringio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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}
};

Expand Down
43 changes: 18 additions & 25 deletions Modules/_io/textio.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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
Expand Down Expand Up @@ -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}
};

Expand Down

0 comments on commit 6ad31bb

Please sign in to comment.