Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 15 additions & 6 deletions Lib/test/test_clinic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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",
Expand Down
22 changes: 10 additions & 12 deletions Modules/_collectionsmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)
{
Expand Down
22 changes: 10 additions & 12 deletions Modules/_dbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
22 changes: 10 additions & 12 deletions Modules/_gdbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down Expand Up @@ -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
Expand Down
17 changes: 16 additions & 1 deletion Modules/clinic/_collectionsmodule.c.h

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

17 changes: 16 additions & 1 deletion Modules/clinic/_dbmmodule.c.h

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

17 changes: 16 additions & 1 deletion Modules/clinic/_gdbmmodule.c.h

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

8 changes: 4 additions & 4 deletions Tools/clinic/libclinic/dsl_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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__':
Expand Down
7 changes: 4 additions & 3 deletions Tools/clinic/libclinic/slots.py
Original file line number Diff line number Diff line change
Expand Up @@ -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'),
}
Loading