diff --git a/Lib/test/test_clinic.py b/Lib/test/test_clinic.py index e3dd4a2be9e410..8293003f8ce925 100644 --- a/Lib/test/test_clinic.py +++ b/Lib/test/test_clinic.py @@ -858,10 +858,7 @@ class Foo "FooObject *" "&Foo_Type" def test_slot_with_parameters_and_return_type(self): # Slots which take arguments or return something else than an # object are not supported yet; check the machinery with them. - slots = { - '__contains__': SlotSpec(return_converter='int'), - '__bool__': SlotSpec(return_converter='int'), - } + slots = {'__bool__': SlotSpec(return_converter='int')} block = """ /*[clinic input] output everything block @@ -3356,6 +3353,20 @@ class Foo "FooObject *" "" self.assertEqual(function.return_converter.type, "PyObject *") self.assertEqual(list(function.parameters), ["self"]) + def test_slot_coexist_is_a_method(self): + # dict.__contains__ is a METH_COEXIST method, not the slot. + function = self.parse_function(""" + module foo + class Foo "FooObject *" "" + @coexist + Foo.__contains__ + key: object + / + """, signatures_in_block=3, function_index=2) + self.assertIs(function.kind, FunctionKind.CALLABLE) + self.assertIsNone(function.slot) + self.assertIn("METH_COEXIST", function.methoddef_flags) + def test_slot_parameters(self): # __getitem__ is not supported yet; its parameter gives the slot # (Py_ssize_t for sq_item, object for mp_subscript) and is not parsed. @@ -3396,8 +3407,6 @@ def test_slot_errors(self): "'__next__' must be a normal method"), ("@getter\nFoo.__next__", "'__next__' must be a normal method"), - ("@coexist\nFoo.__next__", - "@coexist cannot be applied to '__next__'"), ("@vectorcall\nFoo.__next__", "@vectorcall can only be used with __init__ and __new__"), ("Foo.__next__ -> int", diff --git a/Modules/_collectionsmodule.c b/Modules/_collectionsmodule.c index f62972ac8e4901..1d8181e9299f14 100644 --- a/Modules/_collectionsmodule.c +++ b/Modules/_collectionsmodule.c @@ -1190,8 +1190,17 @@ deque_count_impl(dequeobject *deque, PyObject *v) return PyLong_FromSsize_t(count); } +/*[clinic input] +@critical_section +_collections.deque.__contains__ as deque_contains + deque: dequeobject + v: object + / +[clinic start generated code]*/ + static int -deque_contains_lock_held(dequeobject *deque, PyObject *v) +deque_contains_impl(dequeobject *deque, PyObject *v) +/*[clinic end generated code: output=5e7718757d3063bf input=8dd115a2cdaf0f23]*/ { block *b = deque->leftblock; Py_ssize_t index = deque->leftindex; @@ -1222,17 +1231,6 @@ deque_contains_lock_held(dequeobject *deque, PyObject *v) return 0; } -static int -deque_contains(PyObject *self, PyObject *v) -{ - dequeobject *deque = dequeobject_CAST(self); - int result; - Py_BEGIN_CRITICAL_SECTION(deque); - result = deque_contains_lock_held(deque, v); - Py_END_CRITICAL_SECTION(); - return result; -} - static Py_ssize_t deque_len(PyObject *self) { diff --git a/Modules/_dbmmodule.c b/Modules/_dbmmodule.c index cfc8340d26b79c..7201973bf7f6ba 100644 --- a/Modules/_dbmmodule.c +++ b/Modules/_dbmmodule.c @@ -348,10 +348,18 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls) return v; } +/*[clinic input] +@critical_section +_dbm.dbm.__contains__ as dbm_contains + arg: object + / +[clinic start generated code]*/ + static int -dbm_contains_lock_held(PyObject *self, PyObject *arg) +dbm_contains_impl(dbmobject *self, PyObject *arg) +/*[clinic end generated code: output=6c42b797ada528bc input=94134e7abf3eeb6e]*/ { - dbmobject *dp = dbmobject_CAST(self); + dbmobject *dp = self; datum key, val; Py_ssize_t size; @@ -382,16 +390,6 @@ dbm_contains_lock_held(PyObject *self, PyObject *arg) return val.dptr != NULL; } -static int -dbm_contains(PyObject *self, PyObject *arg) -{ - int result; - Py_BEGIN_CRITICAL_SECTION(self); - result = dbm_contains_lock_held(self, arg); - Py_END_CRITICAL_SECTION(); - return result; -} - /*[clinic input] @critical_section _dbm.dbm.get diff --git a/Modules/_gdbmmodule.c b/Modules/_gdbmmodule.c index fefbb59848c638..69bad7e137e24a 100644 --- a/Modules/_gdbmmodule.c +++ b/Modules/_gdbmmodule.c @@ -466,10 +466,18 @@ _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls) return v; } +/*[clinic input] +@critical_section +_gdbm.gdbm.__contains__ as gdbm_contains + arg: object + / +[clinic start generated code]*/ + static int -gdbm_contains_lock_held(PyObject *self, PyObject *arg) +gdbm_contains_impl(gdbmobject *self, PyObject *arg) +/*[clinic end generated code: output=ad951f5a9f4194df input=411dc2039fdd7277]*/ { - gdbmobject *dp = (gdbmobject *)self; + gdbmobject *dp = self; datum key; Py_ssize_t size; _gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp)); @@ -498,16 +506,6 @@ gdbm_contains_lock_held(PyObject *self, PyObject *arg) return gdbm_exists(dp->di_dbm, key); } -static int -gdbm_contains(PyObject *self, PyObject *arg) -{ - int result; - Py_BEGIN_CRITICAL_SECTION(self); - result = gdbm_contains_lock_held(self, arg); - Py_END_CRITICAL_SECTION(); - return result; -} - /*[clinic input] @critical_section _gdbm.gdbm.firstkey diff --git a/Modules/clinic/_collectionsmodule.c.h b/Modules/clinic/_collectionsmodule.c.h index 7545d22b21e655..a5f4962ccf4cb8 100644 --- a/Modules/clinic/_collectionsmodule.c.h +++ b/Modules/clinic/_collectionsmodule.c.h @@ -349,6 +349,21 @@ deque_count(PyObject *deque, PyObject *v) return return_value; } +static int +deque_contains_impl(dequeobject *deque, PyObject *v); + +static int +deque_contains(PyObject *deque, PyObject *v) +{ + int return_value = -1; + + Py_BEGIN_CRITICAL_SECTION(deque); + return_value = deque_contains_impl((dequeobject *)deque, v); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(deque_index__doc__, "index($self, value, [start, [stop]])\n" "--\n" @@ -677,4 +692,4 @@ tuplegetter_new(PyTypeObject *type, PyObject *args, PyObject *kwargs) exit: return return_value; } -/*[clinic end generated code: output=761d85edf8291792 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=fca551851f7a658e input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_dbmmodule.c.h b/Modules/clinic/_dbmmodule.c.h index 1287254421a432..d71aa1362a4d34 100644 --- a/Modules/clinic/_dbmmodule.c.h +++ b/Modules/clinic/_dbmmodule.c.h @@ -89,6 +89,21 @@ _dbm_dbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssize return return_value; } +static int +dbm_contains_impl(dbmobject *self, PyObject *arg); + +static int +dbm_contains(PyObject *self, PyObject *arg) +{ + int return_value = -1; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = dbm_contains_impl((dbmobject *)self, arg); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_dbm_dbm_get__doc__, "get($self, key, default=None, /)\n" "--\n" @@ -279,4 +294,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=da6b5d201f8d3853 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=6ff4681a893c22ad input=a9049054013a1b77]*/ diff --git a/Modules/clinic/_gdbmmodule.c.h b/Modules/clinic/_gdbmmodule.c.h index 7549d3001d0f3e..062b6b0374664a 100644 --- a/Modules/clinic/_gdbmmodule.c.h +++ b/Modules/clinic/_gdbmmodule.c.h @@ -160,6 +160,21 @@ _gdbm_gdbm_keys(PyObject *self, PyTypeObject *cls, PyObject *const *args, Py_ssi return return_value; } +static int +gdbm_contains_impl(gdbmobject *self, PyObject *arg); + +static int +gdbm_contains(PyObject *self, PyObject *arg) +{ + int return_value = -1; + + Py_BEGIN_CRITICAL_SECTION(self); + return_value = gdbm_contains_impl((gdbmobject *)self, arg); + Py_END_CRITICAL_SECTION(); + + return return_value; +} + PyDoc_STRVAR(_gdbm_gdbm_firstkey__doc__, "firstkey($self, /)\n" "--\n" @@ -431,4 +446,4 @@ dbmopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs) exit: return return_value; } -/*[clinic end generated code: output=6f91f162b0f822d3 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=10c193b33298877a input=a9049054013a1b77]*/ diff --git a/Tools/clinic/libclinic/dsl_parser.py b/Tools/clinic/libclinic/dsl_parser.py index a3512b9522322a..c27a883de36a41 100644 --- a/Tools/clinic/libclinic/dsl_parser.py +++ b/Tools/clinic/libclinic/dsl_parser.py @@ -600,7 +600,7 @@ def parse_function_names(self, line: str) -> FunctionNames: fields = full_name.split(".") if fields[-1] == '__new__': fields.pop() - elif fields[-1] in SLOTS: + elif fields[-1] in SLOTS and not self.coexist: # list.__repr__ -> list_repr fields[-1] = fields[-1].strip('_') c_basename = "_".join(fields) @@ -627,14 +627,14 @@ def normalize_function_kind(self, fullname: str) -> None: fail("'__new__' must be a class method!") if self.kind in ACCESSORS and not cls: fail("@getter and @setter must be methods") - slot = SLOTS.get(name) + # A @coexist method named like a slot is a method which coexists + # with the slot (e.g. dict.__contains__), not the slot itself. + slot = None if self.coexist else SLOTS.get(name) if slot is not None: if self.kind is not CALLABLE: fail(f"{name!r} must be a normal method; got '{self.kind}'!") if not cls: fail(f"{name!r} must be a method!") - if self.coexist: - fail(f"@coexist cannot be applied to {name!r}") # Normalise self.kind. if name == '__new__': diff --git a/Tools/clinic/libclinic/slots.py b/Tools/clinic/libclinic/slots.py index c5158cc57fc940..2138a6e780b245 100644 --- a/Tools/clinic/libclinic/slots.py +++ b/Tools/clinic/libclinic/slots.py @@ -38,12 +38,13 @@ def make_return_converter(self) -> CReturnConverter: # Maps the name of a special method to its slot. A name listed here must # not be in dsl_parser.unsupported_special_methods. SLOTS: Final[dict[str, SlotSpec]] = { - '__next__': SlotSpec(return_converter='object'), - '__len__': SlotSpec(return_converter='Py_ssize_t'), '__add__': SlotSpec(return_converter='object'), + '__contains__': SlotSpec(return_converter='int'), '__iadd__': SlotSpec(return_converter='object'), - '__mul__': SlotSpec(return_converter='object'), '__imul__': SlotSpec(return_converter='object'), + '__len__': SlotSpec(return_converter='Py_ssize_t'), + '__mul__': SlotSpec(return_converter='object'), + '__next__': SlotSpec(return_converter='object'), '__repr__': SlotSpec(return_converter='object'), '__setitem__': SlotSpec(return_converter='int'), }