Skip to content

Commit 6e1bd24

Browse files
authored
gh-114258: Update Argument Clinic for __contain__ slots (gh-160093)
1 parent ed144b7 commit 6e1bd24

9 files changed

Lines changed: 101 additions & 52 deletions

File tree

‎Lib/test/test_clinic.py‎

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -858,10 +858,7 @@ class Foo "FooObject *" "&Foo_Type"
858858
def test_slot_with_parameters_and_return_type(self):
859859
# Slots which take arguments or return something else than an
860860
# object are not supported yet; check the machinery with them.
861-
slots = {
862-
'__contains__': SlotSpec(return_converter='int'),
863-
'__bool__': SlotSpec(return_converter='int'),
864-
}
861+
slots = {'__bool__': SlotSpec(return_converter='int')}
865862
block = """
866863
/*[clinic input]
867864
output everything block
@@ -3356,6 +3353,20 @@ class Foo "FooObject *" ""
33563353
self.assertEqual(function.return_converter.type, "PyObject *")
33573354
self.assertEqual(list(function.parameters), ["self"])
33583355

3356+
def test_slot_coexist_is_a_method(self):
3357+
# dict.__contains__ is a METH_COEXIST method, not the slot.
3358+
function = self.parse_function("""
3359+
module foo
3360+
class Foo "FooObject *" ""
3361+
@coexist
3362+
Foo.__contains__
3363+
key: object
3364+
/
3365+
""", signatures_in_block=3, function_index=2)
3366+
self.assertIs(function.kind, FunctionKind.CALLABLE)
3367+
self.assertIsNone(function.slot)
3368+
self.assertIn("METH_COEXIST", function.methoddef_flags)
3369+
33593370
def test_slot_parameters(self):
33603371
# __getitem__ is not supported yet; its parameter gives the slot
33613372
# (Py_ssize_t for sq_item, object for mp_subscript) and is not parsed.
@@ -3396,8 +3407,6 @@ def test_slot_errors(self):
33963407
"'__next__' must be a normal method"),
33973408
("@getter\nFoo.__next__",
33983409
"'__next__' must be a normal method"),
3399-
("@coexist\nFoo.__next__",
3400-
"@coexist cannot be applied to '__next__'"),
34013410
("@vectorcall\nFoo.__next__",
34023411
"@vectorcall can only be used with __init__ and __new__"),
34033412
("Foo.__next__ -> int",

‎Modules/_collectionsmodule.c‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,8 +1190,17 @@ deque_count_impl(dequeobject *deque, PyObject *v)
11901190
return PyLong_FromSsize_t(count);
11911191
}
11921192

1193+
/*[clinic input]
1194+
@critical_section
1195+
_collections.deque.__contains__ as deque_contains
1196+
deque: dequeobject
1197+
v: object
1198+
/
1199+
[clinic start generated code]*/
1200+
11931201
static int
1194-
deque_contains_lock_held(dequeobject *deque, PyObject *v)
1202+
deque_contains_impl(dequeobject *deque, PyObject *v)
1203+
/*[clinic end generated code: output=5e7718757d3063bf input=8dd115a2cdaf0f23]*/
11951204
{
11961205
block *b = deque->leftblock;
11971206
Py_ssize_t index = deque->leftindex;
@@ -1222,17 +1231,6 @@ deque_contains_lock_held(dequeobject *deque, PyObject *v)
12221231
return 0;
12231232
}
12241233

1225-
static int
1226-
deque_contains(PyObject *self, PyObject *v)
1227-
{
1228-
dequeobject *deque = dequeobject_CAST(self);
1229-
int result;
1230-
Py_BEGIN_CRITICAL_SECTION(deque);
1231-
result = deque_contains_lock_held(deque, v);
1232-
Py_END_CRITICAL_SECTION();
1233-
return result;
1234-
}
1235-
12361234
static Py_ssize_t
12371235
deque_len(PyObject *self)
12381236
{

‎Modules/_dbmmodule.c‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -348,10 +348,18 @@ _dbm_dbm_keys_impl(dbmobject *self, PyTypeObject *cls)
348348
return v;
349349
}
350350

351+
/*[clinic input]
352+
@critical_section
353+
_dbm.dbm.__contains__ as dbm_contains
354+
arg: object
355+
/
356+
[clinic start generated code]*/
357+
351358
static int
352-
dbm_contains_lock_held(PyObject *self, PyObject *arg)
359+
dbm_contains_impl(dbmobject *self, PyObject *arg)
360+
/*[clinic end generated code: output=6c42b797ada528bc input=94134e7abf3eeb6e]*/
353361
{
354-
dbmobject *dp = dbmobject_CAST(self);
362+
dbmobject *dp = self;
355363
datum key, val;
356364
Py_ssize_t size;
357365

@@ -382,16 +390,6 @@ dbm_contains_lock_held(PyObject *self, PyObject *arg)
382390
return val.dptr != NULL;
383391
}
384392

385-
static int
386-
dbm_contains(PyObject *self, PyObject *arg)
387-
{
388-
int result;
389-
Py_BEGIN_CRITICAL_SECTION(self);
390-
result = dbm_contains_lock_held(self, arg);
391-
Py_END_CRITICAL_SECTION();
392-
return result;
393-
}
394-
395393
/*[clinic input]
396394
@critical_section
397395
_dbm.dbm.get

‎Modules/_gdbmmodule.c‎

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -466,10 +466,18 @@ _gdbm_gdbm_keys_impl(gdbmobject *self, PyTypeObject *cls)
466466
return v;
467467
}
468468

469+
/*[clinic input]
470+
@critical_section
471+
_gdbm.gdbm.__contains__ as gdbm_contains
472+
arg: object
473+
/
474+
[clinic start generated code]*/
475+
469476
static int
470-
gdbm_contains_lock_held(PyObject *self, PyObject *arg)
477+
gdbm_contains_impl(gdbmobject *self, PyObject *arg)
478+
/*[clinic end generated code: output=ad951f5a9f4194df input=411dc2039fdd7277]*/
471479
{
472-
gdbmobject *dp = (gdbmobject *)self;
480+
gdbmobject *dp = self;
473481
datum key;
474482
Py_ssize_t size;
475483
_gdbm_state *state = PyType_GetModuleState(Py_TYPE(dp));
@@ -498,16 +506,6 @@ gdbm_contains_lock_held(PyObject *self, PyObject *arg)
498506
return gdbm_exists(dp->di_dbm, key);
499507
}
500508

501-
static int
502-
gdbm_contains(PyObject *self, PyObject *arg)
503-
{
504-
int result;
505-
Py_BEGIN_CRITICAL_SECTION(self);
506-
result = gdbm_contains_lock_held(self, arg);
507-
Py_END_CRITICAL_SECTION();
508-
return result;
509-
}
510-
511509
/*[clinic input]
512510
@critical_section
513511
_gdbm.gdbm.firstkey

‎Modules/clinic/_collectionsmodule.c.h‎

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_dbmmodule.c.h‎

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Modules/clinic/_gdbmmodule.c.h‎

Lines changed: 16 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎Tools/clinic/libclinic/dsl_parser.py‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -600,7 +600,7 @@ def parse_function_names(self, line: str) -> FunctionNames:
600600
fields = full_name.split(".")
601601
if fields[-1] == '__new__':
602602
fields.pop()
603-
elif fields[-1] in SLOTS:
603+
elif fields[-1] in SLOTS and not self.coexist:
604604
# list.__repr__ -> list_repr
605605
fields[-1] = fields[-1].strip('_')
606606
c_basename = "_".join(fields)
@@ -627,14 +627,14 @@ def normalize_function_kind(self, fullname: str) -> None:
627627
fail("'__new__' must be a class method!")
628628
if self.kind in ACCESSORS and not cls:
629629
fail("@getter and @setter must be methods")
630-
slot = SLOTS.get(name)
630+
# A @coexist method named like a slot is a method which coexists
631+
# with the slot (e.g. dict.__contains__), not the slot itself.
632+
slot = None if self.coexist else SLOTS.get(name)
631633
if slot is not None:
632634
if self.kind is not CALLABLE:
633635
fail(f"{name!r} must be a normal method; got '{self.kind}'!")
634636
if not cls:
635637
fail(f"{name!r} must be a method!")
636-
if self.coexist:
637-
fail(f"@coexist cannot be applied to {name!r}")
638638

639639
# Normalise self.kind.
640640
if name == '__new__':

‎Tools/clinic/libclinic/slots.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ def make_return_converter(self) -> CReturnConverter:
3838
# Maps the name of a special method to its slot. A name listed here must
3939
# not be in dsl_parser.unsupported_special_methods.
4040
SLOTS: Final[dict[str, SlotSpec]] = {
41-
'__next__': SlotSpec(return_converter='object'),
42-
'__len__': SlotSpec(return_converter='Py_ssize_t'),
4341
'__add__': SlotSpec(return_converter='object'),
42+
'__contains__': SlotSpec(return_converter='int'),
4443
'__iadd__': SlotSpec(return_converter='object'),
45-
'__mul__': SlotSpec(return_converter='object'),
4644
'__imul__': SlotSpec(return_converter='object'),
45+
'__len__': SlotSpec(return_converter='Py_ssize_t'),
46+
'__mul__': SlotSpec(return_converter='object'),
47+
'__next__': SlotSpec(return_converter='object'),
4748
'__repr__': SlotSpec(return_converter='object'),
4849
'__setitem__': SlotSpec(return_converter='int'),
4950
}

0 commit comments

Comments
 (0)