mirror of
https://github.com/python/cpython.git
synced 2026-10-28 10:25:05 +08:00
gh-114258: Update AC to support @critical_section for tp_iternext (gh-159962)
This commit is contained in:
1 parent
e07817d029
commit
f9c0e1ea27
23 files changed
+715
-205
No files matched your search
+182
-1
@@ -15,6 +15,7 @@ import os.path
|
||||
import re
|
||||
import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
import warnings
|
||||
|
||||
test_tools.skip_if_missing('clinic')
|
||||
@@ -22,7 +23,8 @@ with test_tools.imports_under_tool('clinic'):
|
||||
import libclinic
|
||||
from libclinic import ClinicError, unspecified, NULL, fail
|
||||
from libclinic.converters import (
|
||||
int_converter, object_converter, str_converter, self_converter)
|
||||
int_converter, object_converter, str_converter, self_converter,
|
||||
Py_ssize_t_converter)
|
||||
from libclinic.function import (
|
||||
Module, Class, Function, FunctionKind, Parameter,
|
||||
permute_optional_groups, permute_right_option_groups,
|
||||
@@ -34,6 +36,7 @@ with test_tools.imports_under_tool('clinic'):
|
||||
from libclinic.block_parser import Block, BlockParser
|
||||
from libclinic.codegen import BlockPrinter, Destination
|
||||
from libclinic.dsl_parser import DSLParser
|
||||
from libclinic.slots import SlotSpec, SLOTS
|
||||
from libclinic.cli import parse_file, Clinic
|
||||
|
||||
|
||||
@@ -807,6 +810,112 @@ class ClinicWholeFileTest(TestCase):
|
||||
"""
|
||||
self.expect_failure(block, err, lineno=4)
|
||||
|
||||
def test_slot_next(self):
|
||||
block = """
|
||||
/*[clinic input]
|
||||
output everything block
|
||||
class Foo "FooObject *" "&Foo_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
Foo.__next__
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
Foo.__next__ as foo_iternext
|
||||
[clinic start generated code]*/
|
||||
#ifdef CONDITION
|
||||
/*[clinic input]
|
||||
Foo.__next__ as foo_iternext_cond
|
||||
[clinic start generated code]*/
|
||||
#endif
|
||||
"""
|
||||
generated = self.clinic.parse(dedent(block))
|
||||
self.assertIn(dedent("""
|
||||
static PyObject *
|
||||
Foo_next(PyObject *self)
|
||||
{
|
||||
return Foo_next_impl((FooObject *)self);
|
||||
}
|
||||
"""), generated)
|
||||
self.assertIn(dedent("""
|
||||
static PyObject *
|
||||
foo_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = foo_iternext_impl((FooObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
"""), generated)
|
||||
self.assertIn("#if defined(CONDITION)\n\nstatic PyObject *\n"
|
||||
"foo_iternext_cond_impl(FooObject *self);", generated)
|
||||
# A slot function is not stored in a PyMethodDef.
|
||||
for text in "METHODDEF", "PyDoc_", "#ifndef":
|
||||
self.assertNotIn(text, generated)
|
||||
|
||||
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 fake ones.
|
||||
slots = {
|
||||
'__contains__': SlotSpec('__contains__', 'int', 'int',
|
||||
{(object_converter,): 'sq_contains'}),
|
||||
'__bool__': SlotSpec('__bool__', 'int', 'int', {(): 'nb_bool'}),
|
||||
}
|
||||
block = """
|
||||
/*[clinic input]
|
||||
output everything block
|
||||
class Foo "FooObject *" "&Foo_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
Foo.__contains__
|
||||
value: object
|
||||
/
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
Foo.__bool__
|
||||
[clinic start generated code]*/
|
||||
"""
|
||||
with mock.patch.dict(SLOTS, slots):
|
||||
generated = self.clinic.parse(dedent(block))
|
||||
self.assertIn(dedent("""
|
||||
static int
|
||||
Foo_contains(PyObject *self, PyObject *value)
|
||||
{
|
||||
return Foo_contains_impl((FooObject *)self, value);
|
||||
}
|
||||
"""), generated)
|
||||
self.assertIn(dedent("""
|
||||
static int
|
||||
Foo_bool(PyObject *self)
|
||||
{
|
||||
int return_value = -1;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = Foo_bool_impl((FooObject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
"""), generated)
|
||||
|
||||
def test_slot_cannot_be_cloned(self):
|
||||
block = """
|
||||
/*[clinic input]
|
||||
class Foo "FooObject *" "&Foo_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
Foo.__next__
|
||||
[clinic start generated code]*/
|
||||
/*[clinic input]
|
||||
Foo.other = Foo.__next__
|
||||
[clinic start generated code]*/
|
||||
"""
|
||||
self.expect_failure(block, "Type slot methods cannot be cloned", lineno=8)
|
||||
|
||||
def test_getset_in_ifdef(self):
|
||||
block = """
|
||||
/*[clinic input]
|
||||
@@ -3237,6 +3346,78 @@ class ClinicParserTest(TestCase):
|
||||
f"a normal callable")
|
||||
self.expect_failure(block, expected_error, lineno=3)
|
||||
|
||||
def test_slot_next(self):
|
||||
function = self.parse_function("""
|
||||
module foo
|
||||
class Foo "FooObject *" ""
|
||||
Foo.__next__
|
||||
""", signatures_in_block=3, function_index=2)
|
||||
self.assertIs(function.kind, FunctionKind.SLOT)
|
||||
self.assertEqual(function.slot.name, "__next__")
|
||||
self.assertEqual(function.c_basename, "Foo_next")
|
||||
self.assertEqual(function.return_converter.type, "PyObject *")
|
||||
self.assertEqual(list(function.parameters), ["self"])
|
||||
|
||||
def test_slot_parameters(self):
|
||||
slot = SlotSpec('__contains__', 'int', 'int', {
|
||||
(Py_ssize_t_converter,): 'sq_fake',
|
||||
(object_converter,): 'sq_contains'})
|
||||
block = """
|
||||
module foo
|
||||
class Foo "FooObject *" ""
|
||||
Foo.__contains__
|
||||
value: {converter}
|
||||
/
|
||||
"""
|
||||
errors = [
|
||||
("Foo.__contains__",
|
||||
"'__contains__' must declare (Py_ssize_t) for sq_fake or "
|
||||
"(object) for sq_contains; got ()"),
|
||||
("Foo.__contains__\n value: int\n /",
|
||||
"'__contains__' must declare"),
|
||||
("Foo.__contains__\n value: object",
|
||||
"The parameters of '__contains__' must be positional-only"),
|
||||
("Foo.__contains__\n value: object = None\n /",
|
||||
"The parameters of '__contains__' cannot have a default value"),
|
||||
]
|
||||
with mock.patch.dict(SLOTS, {'__contains__': slot}):
|
||||
for converter in "Py_ssize_t", "object":
|
||||
with self.subTest(converter=converter):
|
||||
function = self.parse_function(
|
||||
block.format(converter=converter),
|
||||
signatures_in_block=3, function_index=2)
|
||||
self.assertIs(function.kind, FunctionKind.SLOT)
|
||||
self.assertEqual(function.return_converter.type, "int")
|
||||
self.assertEqual(list(function.parameters), ["self", "value"])
|
||||
for body, err in errors:
|
||||
with self.subTest(block=body):
|
||||
body = 'class Foo "FooObject *" "&Foo_Type"\n' + body
|
||||
self.expect_failure(body, err)
|
||||
|
||||
def test_slot_errors(self):
|
||||
cases = [
|
||||
("module foo\nfoo.__next__",
|
||||
"'__next__' must be a method!"),
|
||||
("@classmethod\nFoo.__next__",
|
||||
"'__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__\n a: int",
|
||||
"'__next__' methods cannot define parameters"),
|
||||
("Foo.__next__ -> int",
|
||||
"'Foo.__next__' cannot define a return type"),
|
||||
("Foo.__next__\n\nReturn the next item.",
|
||||
"'__next__' methods cannot have a docstring"),
|
||||
]
|
||||
for block, err in cases:
|
||||
with self.subTest(block=block):
|
||||
block = 'class Foo "FooObject *" "&Foo_Type"\n' + block
|
||||
self.expect_failure(block, err)
|
||||
|
||||
def test_deleter_without_setter(self):
|
||||
block = """
|
||||
module foo
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
Argument Clinic now supports the ``__next__`` type slot (``tp_iternext``),
|
||||
including ``@critical_section``.
|
||||
|
||||
+12
-17
@@ -22,8 +22,9 @@ module instead.
|
||||
/*[clinic input]
|
||||
module _csv
|
||||
class _csv.Dialect "DialectObj *" "clinic_state()->dialect_type"
|
||||
class _csv.Reader "ReaderObj *" "clinic_state()->reader_type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=042fcc14c7d541cc]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=7bdf66dc7d68ed90]*/
|
||||
#define NOT_SET ((Py_UCS4)-1)
|
||||
#define EOL ((Py_UCS4)-2)
|
||||
|
||||
@@ -124,8 +125,6 @@ typedef struct {
|
||||
bool nonascii_special;
|
||||
} DialectObj;
|
||||
|
||||
#include "clinic/_csv.c.h"
|
||||
|
||||
typedef struct {
|
||||
PyObject_HEAD
|
||||
|
||||
@@ -161,6 +160,8 @@ typedef struct {
|
||||
#define _ReaderObj_CAST(op) ((ReaderObj *)(op))
|
||||
#define _WriterObj_CAST(op) ((WriterObj *)(op))
|
||||
|
||||
#include "clinic/_csv.c.h"
|
||||
|
||||
/*
|
||||
* DIALECT class
|
||||
*/
|
||||
@@ -1020,11 +1021,15 @@ parse_reset(ReaderObj *self)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Reader_iternext_lock_held(PyObject *op)
|
||||
{
|
||||
ReaderObj *self = _ReaderObj_CAST(op);
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_csv.Reader.__next__ as Reader_iternext
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
Reader_iternext_impl(ReaderObj *self)
|
||||
/*[clinic end generated code: output=3a02b0254a76e1fc input=97c70a4e2fe621e4]*/
|
||||
{
|
||||
PyObject *fields = NULL;
|
||||
Py_UCS4 c;
|
||||
Py_ssize_t pos, linelen;
|
||||
@@ -1094,16 +1099,6 @@ err:
|
||||
return fields;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Reader_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = Reader_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return result;
|
||||
}
|
||||
|
||||
static void
|
||||
Reader_dealloc(PyObject *op)
|
||||
{
|
||||
|
||||
+8
-13
@@ -768,13 +768,18 @@ _io_BytesIO_truncate_impl(bytesio *self, PyObject *size)
|
||||
return PyLong_FromSsize_t(new_size);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_io.BytesIO.__next__ as bytesio_iternext
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
bytesio_iternext_lock_held(PyObject *op)
|
||||
bytesio_iternext_impl(bytesio *self)
|
||||
/*[clinic end generated code: output=f530bdee3a720d48 input=7fb3a763efe11a21]*/
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
|
||||
|
||||
Py_ssize_t n;
|
||||
bytesio *self = bytesio_CAST(op);
|
||||
|
||||
CHECK_CLOSED(self);
|
||||
|
||||
@@ -786,16 +791,6 @@ bytesio_iternext_lock_held(PyObject *op)
|
||||
return read_bytes_lock_held(self, n);
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
bytesio_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *ret;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
ret = bytesio_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_io.BytesIO.seek
|
||||
|
||||
Generated
+16
-1
@@ -511,6 +511,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
bytesio_iternext_impl(bytesio *self);
|
||||
|
||||
static PyObject *
|
||||
bytesio_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = bytesio_iternext_impl((bytesio *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_io_BytesIO_seek__doc__,
|
||||
"seek($self, pos, whence=0, /)\n"
|
||||
"--\n"
|
||||
@@ -780,4 +795,4 @@ _io_BytesIO___sizeof__(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
}
|
||||
#define _IO_BYTESIO_CLOSED_GETSETDEF {"closed", (getter)_io_BytesIO_closed_get, (setter)NULL, _io_BytesIO_closed__doc__},
|
||||
|
||||
/*[clinic end generated code: output=a3dc7a416dae6ae9 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=a2bc96c358d6e240 input=a9049054013a1b77]*/
|
||||
Generated
+16
-1
@@ -135,6 +135,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_iternext_impl(stringio *self);
|
||||
|
||||
static PyObject *
|
||||
stringio_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = stringio_iternext_impl((stringio *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_io_StringIO_truncate__doc__,
|
||||
"truncate($self, pos=None, /)\n"
|
||||
"--\n"
|
||||
@@ -539,4 +554,4 @@ _io_StringIO_newlines_get(PyObject *self, void *Py_UNUSED(context))
|
||||
|
||||
#define _IO_STRINGIO_NEWLINES_GETSETDEF {"newlines", (getter)_io_StringIO_newlines_get, (setter)NULL, NULL},
|
||||
|
||||
/*[clinic end generated code: output=4100afa5e4f295d7 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=38a0385529d9cbef input=a9049054013a1b77]*/
|
||||
Generated
+16
-1
@@ -1096,6 +1096,21 @@ _io_TextIOWrapper_close(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
textiowrapper_iternext_impl(textio *self);
|
||||
|
||||
static PyObject *
|
||||
textiowrapper_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = textiowrapper_iternext_impl((textio *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
_io_TextIOWrapper_name_get_impl(textio *self);
|
||||
|
||||
@@ -1246,4 +1261,4 @@ _io_TextIOWrapper_buffer_get(PyObject *self, void *Py_UNUSED(context))
|
||||
|
||||
#define _IO_TEXTIOWRAPPER_BUFFER_GETSETDEF {"buffer", (getter)_io_TextIOWrapper_buffer_get, (setter)NULL, NULL},
|
||||
|
||||
/*[clinic end generated code: output=89de9ab341df9f66 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=f3dd3fff36cd2142 input=a9049054013a1b77]*/
|
||||
+9
-14
@@ -420,13 +420,18 @@ _io_StringIO_readline_impl(stringio *self, Py_ssize_t size)
|
||||
return _stringio_readline(self, size);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_io.StringIO.__next__ as stringio_iternext
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
stringio_iternext_lock_held(PyObject *op)
|
||||
stringio_iternext_impl(stringio *self)
|
||||
/*[clinic end generated code: output=51eb3991b1c79c41 input=b6cf3e73fab6b42e]*/
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
|
||||
|
||||
PyObject *line;
|
||||
stringio *self = stringio_CAST(op);
|
||||
|
||||
CHECK_INITIALIZED(self);
|
||||
CHECK_CLOSED(self);
|
||||
@@ -438,7 +443,7 @@ stringio_iternext_lock_held(PyObject *op)
|
||||
}
|
||||
else {
|
||||
/* XXX is subclassing StringIO really supported? */
|
||||
line = PyObject_CallMethodNoArgs(op, &_Py_ID(readline));
|
||||
line = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(readline));
|
||||
if (line && !PyUnicode_Check(line)) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"readline() should have returned a str object, "
|
||||
@@ -460,16 +465,6 @@ stringio_iternext_lock_held(PyObject *op)
|
||||
return line;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
stringio_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *ret;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
ret = stringio_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_io.StringIO.truncate
|
||||
|
||||
+9
-14
@@ -3287,12 +3287,17 @@ _io_TextIOWrapper_close_impl(textio *self)
|
||||
}
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
_io.TextIOWrapper.__next__ as textiowrapper_iternext
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
textiowrapper_iternext_lock_held(PyObject *op)
|
||||
textiowrapper_iternext_impl(textio *self)
|
||||
/*[clinic end generated code: output=f365c4287cd43a1f input=c5b4c79faf0c98cb]*/
|
||||
{
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op);
|
||||
_Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(self);
|
||||
PyObject *line;
|
||||
textio *self = textio_CAST(op);
|
||||
|
||||
CHECK_ATTACHED(self);
|
||||
|
||||
@@ -3302,7 +3307,7 @@ textiowrapper_iternext_lock_held(PyObject *op)
|
||||
line = _textiowrapper_readline(self, -1);
|
||||
}
|
||||
else {
|
||||
line = PyObject_CallMethodNoArgs(op, &_Py_ID(readline));
|
||||
line = PyObject_CallMethodNoArgs((PyObject *)self, &_Py_ID(readline));
|
||||
if (line && !PyUnicode_Check(line)) {
|
||||
PyErr_Format(PyExc_OSError,
|
||||
"readline() should have returned a str object, "
|
||||
@@ -3326,16 +3331,6 @@ textiowrapper_iternext_lock_held(PyObject *op)
|
||||
return line;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
textiowrapper_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = textiowrapper_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return result;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
@getter
|
||||
|
||||
Generated
+17
-1
@@ -6,6 +6,7 @@ preserve
|
||||
# include "pycore_gc.h" // PyGC_Head
|
||||
# include "pycore_runtime.h" // _Py_ID()
|
||||
#endif
|
||||
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
||||
#include "pycore_modsupport.h" // _PyArg_NoPositional()
|
||||
|
||||
PyDoc_STRVAR(_csv_Dialect___reduce____doc__,
|
||||
@@ -87,6 +88,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
Reader_iternext_impl(ReaderObj *self);
|
||||
|
||||
static PyObject *
|
||||
Reader_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = Reader_iternext_impl((ReaderObj *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(_csv_reader__doc__,
|
||||
"reader($module, iterable, dialect=\'excel\', /, **fmtparams)\n"
|
||||
"--\n"
|
||||
@@ -449,4 +465,4 @@ skip_optional_pos:
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=1a90b8a8ed82497d input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=7e3a92d524d88023 input=a9049054013a1b77]*/
|
||||
Generated
+107
-1
@@ -7,6 +7,7 @@ preserve
|
||||
# include "pycore_runtime.h" // _Py_ID()
|
||||
#endif
|
||||
#include "pycore_abstract.h" // _PyNumber_Index()
|
||||
#include "pycore_critical_section.h"// Py_BEGIN_CRITICAL_SECTION()
|
||||
#include "pycore_modsupport.h" // _PyArg_UnpackKeywords()
|
||||
|
||||
PyDoc_STRVAR(batched_new__doc__,
|
||||
@@ -508,6 +509,36 @@ itertools_chain_from_iterable(PyObject *type, PyObject *arg)
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
chain_next_impl(chainobject *self);
|
||||
|
||||
static PyObject *
|
||||
chain_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = chain_next_impl((chainobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
product_next_impl(productobject *self);
|
||||
|
||||
static PyObject *
|
||||
product_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = product_next_impl((productobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(itertools_combinations__doc__,
|
||||
"combinations(iterable, r)\n"
|
||||
"--\n"
|
||||
@@ -586,6 +617,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
combinations_next_impl(combinationsobject *self);
|
||||
|
||||
static PyObject *
|
||||
combinations_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = combinations_next_impl((combinationsobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(itertools_combinations_with_replacement__doc__,
|
||||
"combinations_with_replacement(iterable, r)\n"
|
||||
"--\n"
|
||||
@@ -665,6 +711,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
cwr_next_impl(cwrobject *self);
|
||||
|
||||
static PyObject *
|
||||
cwr_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = cwr_next_impl((cwrobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(itertools_permutations__doc__,
|
||||
"permutations(iterable, r=None)\n"
|
||||
"--\n"
|
||||
@@ -732,6 +793,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
permutations_next_impl(permutationsobject *self);
|
||||
|
||||
static PyObject *
|
||||
permutations_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = permutations_next_impl((permutationsobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(itertools_accumulate__doc__,
|
||||
"accumulate(iterable, func=None, *, initial=None)\n"
|
||||
"--\n"
|
||||
@@ -808,6 +884,21 @@ exit:
|
||||
return return_value;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
accumulate_next_impl(accumulateobject *self);
|
||||
|
||||
static PyObject *
|
||||
accumulate_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = accumulate_next_impl((accumulateobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(itertools_compress__doc__,
|
||||
"compress(data, selectors)\n"
|
||||
"--\n"
|
||||
@@ -980,4 +1071,19 @@ skip_optional_pos:
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=a34a31f60100e0ff input=a9049054013a1b77]*/
|
||||
|
||||
static PyObject *
|
||||
zip_longest_next_impl(ziplongestobject *self);
|
||||
|
||||
static PyObject *
|
||||
zip_longest_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = zip_longest_next_impl((ziplongestobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=4b55088ac24582b7 input=a9049054013a1b77]*/
|
||||
+85
-101
@@ -65,6 +65,16 @@ find_state_by_type(PyTypeObject *tp)
|
||||
return get_module_state(mod);
|
||||
}
|
||||
|
||||
// The generated code (clinic/itertoolsmodule.c.h) needs the object types
|
||||
// of the type slots implemented with Argument Clinic.
|
||||
typedef struct chainobject chainobject;
|
||||
typedef struct productobject productobject;
|
||||
typedef struct combinationsobject combinationsobject;
|
||||
typedef struct cwrobject cwrobject;
|
||||
typedef struct permutationsobject permutationsobject;
|
||||
typedef struct accumulateobject accumulateobject;
|
||||
typedef struct ziplongestobject ziplongestobject;
|
||||
|
||||
/*[clinic input]
|
||||
module itertools
|
||||
class itertools.groupby "groupbyobject *" "clinic_state()->groupby_type"
|
||||
@@ -77,16 +87,18 @@ class itertools.dropwhile "dropwhileobject *" "clinic_state()->dropwhile_type"
|
||||
class itertools.takewhile "takewhileobject *" "clinic_state()->takewhile_type"
|
||||
class itertools.starmap "starmapobject *" "clinic_state()->starmap_type"
|
||||
class itertools.chain "chainobject *" "clinic_state()->chain_type"
|
||||
class itertools.product "productobject *" "clinic_state()->product_type"
|
||||
class itertools.combinations "combinationsobject *" "clinic_state()->combinations_type"
|
||||
class itertools.combinations_with_replacement "cwr_object *" "clinic_state()->cwr_type"
|
||||
class itertools.combinations_with_replacement "cwrobject *" "clinic_state()->cwr_type"
|
||||
class itertools.permutations "permutationsobject *" "clinic_state()->permutations_type"
|
||||
class itertools.accumulate "accumulateobject *" "clinic_state()->accumulate_type"
|
||||
class itertools.zip_longest "ziplongestobject *" "clinic_state()->ziplongest_type"
|
||||
class itertools.compress "compressobject *" "clinic_state()->compress_type"
|
||||
class itertools.filterfalse "filterfalseobject *" "clinic_state()->filterfalse_type"
|
||||
class itertools.count "countobject *" "clinic_state()->count_type"
|
||||
class itertools.pairwise "pairwiseobject *" "clinic_state()->pairwise_type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=aa48fe4de9d4080f]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=1086fb2d308d75d2]*/
|
||||
|
||||
#define clinic_state() (find_state_by_type(type))
|
||||
#define clinic_state_by_cls() (get_module_state_by_cls(base_tp))
|
||||
@@ -1877,11 +1889,11 @@ static PyType_Spec starmap_spec = {
|
||||
|
||||
/* chain object **************************************************************/
|
||||
|
||||
typedef struct {
|
||||
struct chainobject {
|
||||
PyObject_HEAD
|
||||
PyObject *source; /* Iterator over input iterables */
|
||||
PyObject *active; /* Currently running input iterator */
|
||||
} chainobject;
|
||||
};
|
||||
|
||||
#define chainobject_CAST(op) ((chainobject *)(op))
|
||||
|
||||
@@ -1963,10 +1975,16 @@ chain_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline PyObject *
|
||||
chain_next_lock_held(PyObject *op)
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.chain.__next__ as chain_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
chain_next_impl(chainobject *self)
|
||||
/*[clinic end generated code: output=91e716f45def7fa9 input=e17098e9dc88706b]*/
|
||||
{
|
||||
chainobject *lz = chainobject_CAST(op);
|
||||
chainobject *lz = self;
|
||||
PyObject *item;
|
||||
|
||||
/* lz->source is the iterator of iterables. If it's NULL, we've already
|
||||
@@ -2002,16 +2020,6 @@ chain_next_lock_held(PyObject *op)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
chain_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = chain_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(chain_doc,
|
||||
"chain(*iterables)\n\
|
||||
--\n\
|
||||
@@ -2055,13 +2063,13 @@ static PyType_Spec chain_spec = {
|
||||
|
||||
/* product object ************************************************************/
|
||||
|
||||
typedef struct {
|
||||
struct productobject {
|
||||
PyObject_HEAD
|
||||
PyObject *pools; /* tuple of pool tuples */
|
||||
Py_ssize_t *indices; /* one index per pool */
|
||||
PyObject *result; /* most recently returned result tuple */
|
||||
int stopped; /* set to 1 when the iterator is exhausted */
|
||||
} productobject;
|
||||
};
|
||||
|
||||
#define productobject_CAST(op) ((productobject *)(op))
|
||||
|
||||
@@ -2182,10 +2190,16 @@ product_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.product.__next__ as product_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
product_next_lock_held(PyObject *op)
|
||||
product_next_impl(productobject *self)
|
||||
/*[clinic end generated code: output=d20b8571608c2043 input=54266fe7ed4a857a]*/
|
||||
{
|
||||
productobject *lz = productobject_CAST(op);
|
||||
productobject *lz = self;
|
||||
PyObject *pool;
|
||||
PyObject *elem;
|
||||
PyObject *oldelem;
|
||||
@@ -2269,16 +2283,6 @@ empty:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
product_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = product_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef product_methods[] = {
|
||||
{"__sizeof__", product_sizeof, METH_NOARGS, sizeof_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
@@ -2323,14 +2327,14 @@ static PyType_Spec product_spec = {
|
||||
|
||||
/* combinations object *******************************************************/
|
||||
|
||||
typedef struct {
|
||||
struct combinationsobject {
|
||||
PyObject_HEAD
|
||||
PyObject *pool; /* input converted to a tuple */
|
||||
Py_ssize_t *indices; /* one index per result element */
|
||||
PyObject *result; /* most recently returned result tuple */
|
||||
Py_ssize_t r; /* size of result tuple */
|
||||
int stopped; /* set to 1 when the iterator is exhausted */
|
||||
} combinationsobject;
|
||||
};
|
||||
|
||||
#define combinationsobject_CAST(op) ((combinationsobject *)(op))
|
||||
|
||||
@@ -2421,10 +2425,16 @@ combinations_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.combinations.__next__ as combinations_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
combinations_next_lock_held(PyObject *op)
|
||||
combinations_next_impl(combinationsobject *self)
|
||||
/*[clinic end generated code: output=19965c1a1f4b8f2a input=93000f6ec48380d7]*/
|
||||
{
|
||||
combinationsobject *co = combinationsobject_CAST(op);
|
||||
combinationsobject *co = self;
|
||||
PyObject *elem;
|
||||
PyObject *oldelem;
|
||||
PyObject *pool = co->pool;
|
||||
@@ -2507,16 +2517,6 @@ empty:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
combinations_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = combinations_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef combinations_methods[] = {
|
||||
{"__sizeof__", combinations_sizeof, METH_NOARGS, sizeof_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
@@ -2572,14 +2572,14 @@ static PyType_Spec combinations_spec = {
|
||||
if sorted(indices) == list(indices):
|
||||
yield tuple(pool[i] for i in indices)
|
||||
*/
|
||||
typedef struct {
|
||||
struct cwrobject {
|
||||
PyObject_HEAD
|
||||
PyObject *pool; /* input converted to a tuple */
|
||||
Py_ssize_t *indices; /* one index per result element */
|
||||
PyObject *result; /* most recently returned result tuple */
|
||||
Py_ssize_t r; /* size of result tuple */
|
||||
int stopped; /* set to 1 when the cwr iterator is exhausted */
|
||||
} cwrobject;
|
||||
};
|
||||
|
||||
#define cwrobject_CAST(op) ((cwrobject *)(op))
|
||||
|
||||
@@ -2673,10 +2673,16 @@ cwr_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.combinations_with_replacement.__next__ as cwr_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
cwr_next_lock_held(PyObject *op)
|
||||
cwr_next_impl(cwrobject *self)
|
||||
/*[clinic end generated code: output=760ddcfbf905ee37 input=d9ed46a28a5bb29a]*/
|
||||
{
|
||||
cwrobject *co = cwrobject_CAST(op);
|
||||
cwrobject *co = self;
|
||||
PyObject *elem;
|
||||
PyObject *oldelem;
|
||||
PyObject *pool = co->pool;
|
||||
@@ -2753,16 +2759,6 @@ empty:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
cwr_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = cwr_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef cwr_methods[] = {
|
||||
{"__sizeof__", cwr_sizeof, METH_NOARGS, sizeof_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
@@ -2818,7 +2814,7 @@ def permutations(iterable, r=None):
|
||||
return
|
||||
*/
|
||||
|
||||
typedef struct {
|
||||
struct permutationsobject {
|
||||
PyObject_HEAD
|
||||
PyObject *pool; /* input converted to a tuple */
|
||||
Py_ssize_t *indices; /* one index per element in the pool */
|
||||
@@ -2826,7 +2822,7 @@ typedef struct {
|
||||
PyObject *result; /* most recently returned result tuple */
|
||||
Py_ssize_t r; /* size of result tuple */
|
||||
int stopped; /* set to 1 when the iterator is exhausted */
|
||||
} permutationsobject;
|
||||
};
|
||||
|
||||
#define permutationsobject_CAST(op) ((permutationsobject *)(op))
|
||||
|
||||
@@ -2942,10 +2938,16 @@ permutations_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.permutations.__next__ as permutations_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
permutations_next_lock_held(PyObject *op)
|
||||
permutations_next_impl(permutationsobject *self)
|
||||
/*[clinic end generated code: output=17d8db4465bd3ee5 input=d4c1a1eed76a35e0]*/
|
||||
{
|
||||
permutationsobject *po = permutationsobject_CAST(op);
|
||||
permutationsobject *po = self;
|
||||
PyObject *elem;
|
||||
PyObject *oldelem;
|
||||
PyObject *pool = po->pool;
|
||||
@@ -3033,16 +3035,6 @@ empty:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
permutations_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = permutations_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyMethodDef permuations_methods[] = {
|
||||
{"__sizeof__", permutations_sizeof, METH_NOARGS, sizeof_doc},
|
||||
{NULL, NULL} /* sentinel */
|
||||
@@ -3072,14 +3064,14 @@ static PyType_Spec permutations_spec = {
|
||||
|
||||
/* accumulate object ********************************************************/
|
||||
|
||||
typedef struct {
|
||||
struct accumulateobject {
|
||||
PyObject_HEAD
|
||||
PyObject *total;
|
||||
PyObject *it;
|
||||
PyObject *binop;
|
||||
PyObject *initial;
|
||||
itertools_state *state;
|
||||
} accumulateobject;
|
||||
};
|
||||
|
||||
#define accumulateobject_CAST(op) ((accumulateobject *)(op))
|
||||
|
||||
@@ -3149,10 +3141,16 @@ accumulate_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.accumulate.__next__ as accumulate_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
accumulate_next_lock_held(PyObject *op)
|
||||
accumulate_next_impl(accumulateobject *self)
|
||||
/*[clinic end generated code: output=30d3f9b40248d8a6 input=758a144b7cfb1ef4]*/
|
||||
{
|
||||
accumulateobject *lz = accumulateobject_CAST(op);
|
||||
accumulateobject *lz = self;
|
||||
PyObject *val, *newtotal;
|
||||
|
||||
if (lz->initial != Py_None) {
|
||||
@@ -3182,16 +3180,6 @@ accumulate_next_lock_held(PyObject *op)
|
||||
return newtotal;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
accumulate_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = accumulate_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyType_Slot accumulate_slots[] = {
|
||||
{Py_tp_dealloc, accumulate_dealloc},
|
||||
{Py_tp_getattro, PyObject_GenericGetAttr},
|
||||
@@ -3852,14 +3840,14 @@ static PyType_Spec repeat_spec = {
|
||||
|
||||
/* ziplongest object *********************************************************/
|
||||
|
||||
typedef struct {
|
||||
struct ziplongestobject {
|
||||
PyObject_HEAD
|
||||
Py_ssize_t tuplesize;
|
||||
Py_ssize_t numactive;
|
||||
PyObject *ittuple; /* tuple of iterators */
|
||||
PyObject *result;
|
||||
PyObject *fillvalue;
|
||||
} ziplongestobject;
|
||||
};
|
||||
|
||||
#define ziplongestobject_CAST(op) ((ziplongestobject *)(op))
|
||||
|
||||
@@ -3955,10 +3943,16 @@ zip_longest_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
itertools.zip_longest.__next__ as zip_longest_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
zip_longest_next_lock_held(PyObject *op)
|
||||
zip_longest_next_impl(ziplongestobject *self)
|
||||
/*[clinic end generated code: output=11bc756cf896c576 input=5b1e3b52098852a9]*/
|
||||
{
|
||||
ziplongestobject *lz = ziplongestobject_CAST(op);
|
||||
ziplongestobject *lz = self;
|
||||
Py_ssize_t i;
|
||||
Py_ssize_t tuplesize = lz->tuplesize;
|
||||
PyObject *result = lz->result;
|
||||
@@ -4027,16 +4021,6 @@ zip_longest_next_lock_held(PyObject *op)
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
zip_longest_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = zip_longest_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION()
|
||||
return result;
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(zip_longest_doc,
|
||||
"zip_longest(*iterables, fillvalue=None)\n\
|
||||
--\n\
|
||||
|
||||
Generated
+16
-1
@@ -452,4 +452,19 @@ skip_optional_pos:
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=89f7e92de998f9a4 input=a9049054013a1b77]*/
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext_impl(PyObject *self);
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = odictiter_iternext_impl(self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=b729b3f1d40afcd9 input=a9049054013a1b77]*/
|
||||
Generated
+16
-1
@@ -147,4 +147,19 @@ longrange_iterator___setstate__(PyObject *r, PyObject *state)
|
||||
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=719c0e4c81fe0f4a input=a9049054013a1b77]*/
|
||||
|
||||
static PyObject *
|
||||
longrangeiter_next_impl(longrangeiterobject *self);
|
||||
|
||||
static PyObject *
|
||||
longrangeiter_next(PyObject *self)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
|
||||
Py_BEGIN_CRITICAL_SECTION(self);
|
||||
return_value = longrangeiter_next_impl((longrangeiterobject *)self);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=e31cc65008f9db4f input=a9049054013a1b77]*/
|
||||
+10
-13
@@ -478,8 +478,9 @@ later:
|
||||
|
||||
/*[clinic input]
|
||||
class OrderedDict "PyODictObject *" "&PyODict_Type"
|
||||
class odictiter "PyObject *" "&PyODictIter_Type"
|
||||
[clinic start generated code]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ca0641cf6143d4af]*/
|
||||
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=ae5599eeb14d29ae]*/
|
||||
|
||||
|
||||
typedef struct _odictnode _ODictNode;
|
||||
@@ -1804,10 +1805,16 @@ odictiter_nextkey(odictiterobject *di)
|
||||
return res;
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
odictiter.__next__ as odictiter_iternext
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext_lock_held(PyObject *op)
|
||||
odictiter_iternext_impl(PyObject *self)
|
||||
/*[clinic end generated code: output=71aef8eec9e1a133 input=bc8bec6c85481b93]*/
|
||||
{
|
||||
odictiterobject *di = (odictiterobject*)op;
|
||||
odictiterobject *di = (odictiterobject*)self;
|
||||
PyObject *result, *value;
|
||||
PyObject *key = odictiter_nextkey(di); /* new reference */
|
||||
|
||||
@@ -1862,16 +1869,6 @@ error:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
odictiter_iternext(PyObject *op)
|
||||
{
|
||||
PyObject *res;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
res = odictiter_iternext_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* No need for tp_clear because odictiterobject is not mutable. */
|
||||
|
||||
|
||||
+8
-12
@@ -1158,10 +1158,16 @@ longrangeiter_dealloc(PyObject *op)
|
||||
PyObject_Free(r);
|
||||
}
|
||||
|
||||
/*[clinic input]
|
||||
@critical_section
|
||||
longrange_iterator.__next__ as longrangeiter_next
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
longrangeiter_next_lock_held(PyObject *op)
|
||||
longrangeiter_next_impl(longrangeiterobject *self)
|
||||
/*[clinic end generated code: output=49bf9a9b89c07ba5 input=2c4ac537c23a92d3]*/
|
||||
{
|
||||
longrangeiterobject *r = (longrangeiterobject*)op;
|
||||
longrangeiterobject *r = self;
|
||||
if (PyObject_RichCompareBool(r->len, _PyLong_GetZero(), Py_GT) != 1)
|
||||
return NULL;
|
||||
|
||||
@@ -1180,16 +1186,6 @@ longrangeiter_next_lock_held(PyObject *op)
|
||||
return result;
|
||||
}
|
||||
|
||||
static PyObject *
|
||||
longrangeiter_next(PyObject *op)
|
||||
{
|
||||
PyObject *result;
|
||||
Py_BEGIN_CRITICAL_SECTION(op);
|
||||
result = longrangeiter_next_lock_held(op);
|
||||
Py_END_CRITICAL_SECTION();
|
||||
return result;
|
||||
}
|
||||
|
||||
PyTypeObject PyLongRangeIter_Type = {
|
||||
PyVarObject_HEAD_INIT(&PyType_Type, 0)
|
||||
"longrange_iterator", /* tp_name */
|
||||
|
||||
@@ -12,7 +12,7 @@ from libclinic.language import Language
|
||||
from libclinic.function import (
|
||||
Module, Class, Property, Function, Parameter,
|
||||
group_to_variable_name,
|
||||
METHOD_INIT, METHOD_NEW,
|
||||
METHOD_INIT, METHOD_NEW, SLOT,
|
||||
ACCESSORS, SETTERS)
|
||||
from libclinic.converters import self_converter
|
||||
from libclinic.parse_args import (
|
||||
@@ -383,6 +383,10 @@ class CLanguage(Language):
|
||||
if ("METH_O" in templates['methoddef_define'] and
|
||||
'{parser_parameters}' in templates['parser_prototype']):
|
||||
data.declarations.pop(0)
|
||||
# A slot function receives its arguments as parameters, see
|
||||
# parse_slot(): there is nothing to declare or parse.
|
||||
if f.kind is SLOT:
|
||||
data.declarations.clear()
|
||||
|
||||
full_name = f.full_name
|
||||
template_dict = {'full_name': full_name}
|
||||
@@ -404,12 +408,17 @@ class CLanguage(Language):
|
||||
for converter in converters:
|
||||
converter.set_template_dict(template_dict)
|
||||
|
||||
if f.kind not in SETTERS | {METHOD_INIT}:
|
||||
if f.kind not in SETTERS | {METHOD_INIT, SLOT}:
|
||||
f.return_converter.render(f, data)
|
||||
template_dict['impl_return_type'] = f.return_converter.type
|
||||
# tp_init returns int; every other parser returns an object.
|
||||
template_dict['return_type'] = (
|
||||
'int' if f.kind is METHOD_INIT else 'PyObject *')
|
||||
# tp_init returns int, a slot function returns what its slot
|
||||
# declares, every other parser returns an object.
|
||||
if f.kind is SLOT:
|
||||
assert f.slot is not None
|
||||
template_dict['return_type'] = f.slot.return_type
|
||||
else:
|
||||
template_dict['return_type'] = (
|
||||
'int' if f.kind is METHOD_INIT else 'PyObject *')
|
||||
|
||||
template_dict['declarations'] = libclinic.format_escape("\n".join(data.declarations))
|
||||
template_dict['initializers'] = "\n\n".join(data.initializers)
|
||||
|
||||
@@ -275,6 +275,11 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
else: # pragma: no cover
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def dsl_name(cls) -> str:
|
||||
"""The name of the converter in the DSL, e.g. 'object'."""
|
||||
return cls.__name__.removesuffix('_converter')
|
||||
|
||||
def converter_init(self) -> None:
|
||||
pass
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ import libclinic
|
||||
from libclinic import fail, NullType, unspecified, NULL, c_bytes_repr, c_unichar_repr
|
||||
from libclinic.function import (
|
||||
Function, Parameter,
|
||||
CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW,
|
||||
CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW, SLOT,
|
||||
ACCESSORS)
|
||||
from libclinic.codegen import CRenderData, TemplateDict
|
||||
from libclinic.converter import (
|
||||
@@ -1197,7 +1197,7 @@ def correct_name_for_self(
|
||||
f: Function,
|
||||
parser: bool = False
|
||||
) -> tuple[str, str]:
|
||||
if f.kind in {CALLABLE, METHOD_INIT} | ACCESSORS:
|
||||
if f.kind in {CALLABLE, METHOD_INIT, SLOT} | ACCESSORS:
|
||||
if f.cls:
|
||||
return "PyObject *", "self"
|
||||
return "PyObject *", "module"
|
||||
|
||||
@@ -18,7 +18,7 @@ from libclinic.function import (
|
||||
Module, Class, Property, Function, Parameter,
|
||||
FunctionKind,
|
||||
CALLABLE, STATIC_METHOD, CLASS_METHOD, METHOD_INIT, METHOD_NEW,
|
||||
GETTER, SETTER, SETTER_AND_DELETER,
|
||||
GETTER, SETTER, SETTER_AND_DELETER, SLOT,
|
||||
ACCESSORS, SETTERS)
|
||||
from libclinic.converter import (
|
||||
converters, legacy_converters)
|
||||
@@ -28,12 +28,15 @@ from libclinic.converters import (
|
||||
from libclinic.return_converters import (
|
||||
CReturnConverter, return_converters,
|
||||
int_return_converter)
|
||||
from libclinic.slots import SlotSpec, SLOTS
|
||||
from libclinic.parser import create_parser_namespace
|
||||
if TYPE_CHECKING:
|
||||
from libclinic.block_parser import Block
|
||||
from libclinic.app import Clinic
|
||||
|
||||
|
||||
# Special methods which Argument Clinic cannot convert. The type slots
|
||||
# which it supports are listed in libclinic.slots.
|
||||
unsupported_special_methods: set[str] = set("""
|
||||
|
||||
__abs__
|
||||
@@ -76,7 +79,6 @@ __matmul__
|
||||
__mod__
|
||||
__mul__
|
||||
__neg__
|
||||
__next__
|
||||
__or__
|
||||
__pos__
|
||||
__pow__
|
||||
@@ -265,6 +267,7 @@ class DSLParser:
|
||||
critical_section: bool
|
||||
target_critical_section: list[str]
|
||||
disable_fastcall: bool
|
||||
slot: SlotSpec | None
|
||||
# Line of the file which is being parsed.
|
||||
line_number: int | None
|
||||
from_version_re = re.compile(r'([*/]) +\[from +(.+)\]')
|
||||
@@ -312,6 +315,7 @@ class DSLParser:
|
||||
self.target_critical_section = []
|
||||
self.disable_fastcall = False
|
||||
self.vectorcall: bool = False
|
||||
self.slot = None
|
||||
self.permit_long_summary = False
|
||||
self.permit_long_docstring_body = False
|
||||
|
||||
@@ -603,6 +607,9 @@ class DSLParser:
|
||||
fields = full_name.split(".")
|
||||
if fields[-1] == '__new__':
|
||||
fields.pop()
|
||||
elif fields[-1] in SLOTS:
|
||||
# list.__repr__ -> list_repr
|
||||
fields[-1] = fields[-1].strip('_')
|
||||
c_basename = "_".join(fields)
|
||||
if not libclinic.is_legal_py_identifier(full_name):
|
||||
fail(f"Illegal function name: {full_name!r}")
|
||||
@@ -627,12 +634,23 @@ class DSLParser:
|
||||
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)
|
||||
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__':
|
||||
self.kind = METHOD_NEW
|
||||
elif name == '__init__':
|
||||
self.kind = METHOD_INIT
|
||||
elif slot is not None:
|
||||
self.kind = SLOT
|
||||
self.slot = slot
|
||||
|
||||
# Validate @vectorcall usage.
|
||||
if self.vectorcall:
|
||||
@@ -653,6 +671,9 @@ class DSLParser:
|
||||
fail("@setter methods cannot define a return type")
|
||||
if self.kind is METHOD_INIT:
|
||||
fail("__init__ methods cannot define a return type")
|
||||
if self.kind is SLOT:
|
||||
fail(f"{full_name!r} cannot define a return type; "
|
||||
"its type slot defines it")
|
||||
ast_input = f"def x() -> {forced_converter}: pass"
|
||||
try:
|
||||
module_node = ast.parse(ast_input)
|
||||
@@ -672,6 +693,9 @@ class DSLParser:
|
||||
|
||||
if self.kind in {METHOD_INIT} | SETTERS:
|
||||
return int_return_converter()
|
||||
if self.kind is SLOT:
|
||||
assert self.slot is not None
|
||||
return return_converters[self.slot.return_converter]()
|
||||
return CReturnConverter()
|
||||
|
||||
def parse_cloned_function(self, names: FunctionNames, existing: str) -> None:
|
||||
@@ -703,6 +727,8 @@ class DSLParser:
|
||||
"docstring_line_number": None,
|
||||
"line_number": self.line_number,
|
||||
}
|
||||
if existing_function.kind is SLOT or self.slot is not None:
|
||||
fail("Type slot methods cannot be cloned")
|
||||
if not (existing_function.kind is self.kind and
|
||||
existing_function.coexist == self.coexist):
|
||||
# Allow __new__ or __init__ methods.
|
||||
@@ -777,6 +803,7 @@ class DSLParser:
|
||||
forced_text_signature=self.forced_text_signature,
|
||||
line_number=self.line_number,
|
||||
vectorcall=self.vectorcall,
|
||||
slot=self.slot,
|
||||
)
|
||||
self.add_function(func)
|
||||
|
||||
@@ -893,6 +920,10 @@ class DSLParser:
|
||||
assert self.function is not None
|
||||
if self.function.kind is GETTER:
|
||||
fail("@getter methods cannot define parameters")
|
||||
if self.function.kind is SLOT:
|
||||
assert self.function.slot is not None
|
||||
if not self.function.slot.takes_parameters:
|
||||
fail(f"{self.function.name!r} methods cannot define parameters")
|
||||
|
||||
self.parameter_continuation = ''
|
||||
return self.next(self.state_parameter, line)
|
||||
@@ -1498,6 +1529,12 @@ class DSLParser:
|
||||
assert self.function is not None
|
||||
f = self.function
|
||||
# For the following special cases, it does not make sense to render a docstring.
|
||||
if f.kind is SLOT:
|
||||
# A slot function has no docstring (there is no PyMethodDef).
|
||||
if f.docstring:
|
||||
fail(f"{f.name!r} methods cannot have a docstring",
|
||||
line_number=f.docstring_line_number)
|
||||
return f.docstring
|
||||
if f.kind in {METHOD_INIT, METHOD_NEW} | ACCESSORS and not f.docstring:
|
||||
return f.docstring
|
||||
|
||||
@@ -1639,6 +1676,21 @@ class DSLParser:
|
||||
f"parse_arg(); parameter {p.name!r} does not",
|
||||
line_number=lineno)
|
||||
|
||||
def check_slot_parameters(self, func: Function) -> None:
|
||||
"""Check that the parameters of a slot method select a C slot."""
|
||||
assert func.slot is not None
|
||||
params = list(func.parameters.values())[1:]
|
||||
for p in params:
|
||||
if not p.is_positional_only():
|
||||
fail(f"The parameters of {func.name!r} must be positional-only")
|
||||
if p.default is not unspecified:
|
||||
fail(f"The parameters of {func.name!r} cannot have a default value")
|
||||
converters = [p.converter for p in params]
|
||||
if func.slot.c_slot(converters) is None:
|
||||
got = ', '.join(c.dsl_name() for c in converters)
|
||||
fail(f"{func.name!r} must declare "
|
||||
f"{func.slot.describe_signatures()}; got ({got})")
|
||||
|
||||
def do_post_block_processing_cleanup(self, lineno: int) -> None:
|
||||
"""
|
||||
Called when processing the block is done.
|
||||
@@ -1647,6 +1699,8 @@ class DSLParser:
|
||||
return
|
||||
|
||||
func = self.function
|
||||
if func.kind is SLOT:
|
||||
self.check_slot_parameters(func)
|
||||
if func.kind in (SETTER, SETTER_AND_DELETER):
|
||||
# The new value is the only parameter of a setter. The setter
|
||||
# of a deletable attribute is also called with NULL to delete it,
|
||||
|
||||
@@ -11,6 +11,7 @@ if TYPE_CHECKING:
|
||||
from libclinic.converters import self_converter
|
||||
from libclinic.return_converters import CReturnConverter
|
||||
from libclinic.app import Clinic
|
||||
from libclinic.slots import SlotSpec
|
||||
|
||||
from libclinic import VersionTuple, unspecified
|
||||
|
||||
@@ -94,6 +95,8 @@ class FunctionKind(enum.Enum):
|
||||
GETTER = enum.auto()
|
||||
SETTER = enum.auto()
|
||||
SETTER_AND_DELETER = enum.auto()
|
||||
# A type slot ("tp slot"), see libclinic.slots.
|
||||
SLOT = enum.auto()
|
||||
|
||||
@functools.cached_property
|
||||
def new_or_init(self) -> bool:
|
||||
@@ -111,6 +114,7 @@ METHOD_NEW: Final = FunctionKind.METHOD_NEW
|
||||
GETTER: Final = FunctionKind.GETTER
|
||||
SETTER: Final = FunctionKind.SETTER
|
||||
SETTER_AND_DELETER: Final = FunctionKind.SETTER_AND_DELETER
|
||||
SLOT: Final = FunctionKind.SLOT
|
||||
|
||||
# The kinds which implement the setter of an entry of PyGetSetDef.
|
||||
SETTERS: Final = frozenset({SETTER, SETTER_AND_DELETER})
|
||||
@@ -156,6 +160,8 @@ class Function:
|
||||
# Line on which the docstring starts (`None` if there is no docstring).
|
||||
docstring_line_number: int | None = None
|
||||
vectorcall: bool = False
|
||||
# The type slot which the function implements (kind is SLOT).
|
||||
slot: SlotSpec | None = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
self.parent = self.cls or self.module
|
||||
@@ -224,7 +230,8 @@ class Function:
|
||||
|
||||
@property
|
||||
def methoddef_flags(self) -> str | None:
|
||||
if self.kind.new_or_init:
|
||||
if self.kind.new_or_init or self.kind is SLOT:
|
||||
# Slot functions are not stored in a PyMethodDef.
|
||||
return None
|
||||
flags = []
|
||||
match self.kind:
|
||||
|
||||
@@ -6,7 +6,7 @@ from libclinic import fail, warn, unspecified, Sentinels
|
||||
from libclinic.function import (
|
||||
Function, Parameter, ParamTuple,
|
||||
count_required, group_to_variable_name, permute_optional_groups,
|
||||
GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT,
|
||||
GETTER, SETTER, SETTER_AND_DELETER, METHOD_INIT, SLOT,
|
||||
ACCESSORS, SETTERS)
|
||||
from libclinic.converter import CConverter
|
||||
from libclinic.converters import (
|
||||
@@ -142,6 +142,14 @@ PARSER_PROTOTYPE_SETTER: Final[str] = libclinic.normalize_snippet("""
|
||||
static int
|
||||
{c_basename}({self_type}{self_name}, PyObject *arg, void *Py_UNUSED(context))
|
||||
""")
|
||||
PARSER_PROTOTYPE_SLOT: Final[str] = libclinic.normalize_snippet("""
|
||||
static {return_type}
|
||||
{c_basename}({self_type}{self_name})
|
||||
""")
|
||||
PARSER_PROTOTYPE_SLOT_ARGS: Final[str] = libclinic.normalize_snippet("""
|
||||
static {return_type}
|
||||
{c_basename}({self_type}{self_name}, {parser_parameters})
|
||||
""")
|
||||
METH_O_PROTOTYPE: Final[str] = libclinic.normalize_snippet("""
|
||||
static PyObject *
|
||||
{c_basename}({self_type}{self_name}, {parser_parameters})
|
||||
@@ -393,8 +401,11 @@ class ParseArgsCodeGen:
|
||||
and not self.is_new_or_init())
|
||||
|
||||
def use_simple_return(self) -> bool:
|
||||
if self.func.critical_section:
|
||||
return False
|
||||
# A slot function returns the result of the impl unchanged.
|
||||
return (self.func.return_converter.type == 'PyObject *'
|
||||
and not self.func.critical_section)
|
||||
or self.func.kind is SLOT)
|
||||
|
||||
def use_pyobject_self(self) -> bool:
|
||||
return self.self_parameter_converter.use_pyobject_self(self.func)
|
||||
@@ -407,6 +418,19 @@ class ParseArgsCodeGen:
|
||||
|
||||
if self.is_new_or_init() and not self.func.docstring:
|
||||
pass
|
||||
elif self.func.kind is SLOT:
|
||||
# A slot function is not stored in a PyMethodDef and has no
|
||||
# docstring (the DSL parser rejects one). It returns what its
|
||||
# slot declares.
|
||||
assert not self.func.docstring
|
||||
assert self.func.slot is not None
|
||||
self.methoddef_define = ''
|
||||
return_type = self.func.slot.return_type
|
||||
if not return_type.endswith('*'):
|
||||
return_type += ' '
|
||||
self.return_value_declaration = (
|
||||
f"{return_type}{{parser_retval}} = "
|
||||
f"{self.func.slot.error_value};")
|
||||
elif self.func.kind is GETTER:
|
||||
# Only an accessor compiled conditionally announces itself.
|
||||
self.methoddef_define = (GETTERDEF_PROTOTYPE_DEFINE
|
||||
@@ -482,6 +506,18 @@ class ParseArgsCodeGen:
|
||||
libclinic.normalize_snippet("}}", indent=4),
|
||||
])
|
||||
|
||||
def parse_slot(self) -> None:
|
||||
"""Generate the slot function.
|
||||
|
||||
It does not parse anything: it forwards its arguments to the impl
|
||||
and, with @critical_section, holds the lock while doing so.
|
||||
"""
|
||||
if self.parameters:
|
||||
self.parser_prototype = PARSER_PROTOTYPE_SLOT_ARGS
|
||||
else:
|
||||
self.parser_prototype = PARSER_PROTOTYPE_SLOT
|
||||
self.finish_parser_body([])
|
||||
|
||||
def parse_accessor(self) -> None:
|
||||
"""Generate the code of a getter or a setter."""
|
||||
parser_code: list[str] = []
|
||||
@@ -1744,7 +1780,9 @@ class ParseArgsCodeGen:
|
||||
# previous call to parser_body. this is used for an awful hack.
|
||||
self.parser_body_fields: tuple[str, ...] = ()
|
||||
|
||||
if self.func.kind in ACCESSORS:
|
||||
if self.func.kind is SLOT:
|
||||
self.parse_slot()
|
||||
elif self.func.kind in ACCESSORS:
|
||||
self.parse_accessor()
|
||||
elif not self.parameters and not self.varpos and not self.var_keyword:
|
||||
self.parse_no_args()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
"""Type slots ("tp slots") for which Argument Clinic generates the slot function.
|
||||
|
||||
A slot function does not parse arguments: Argument Clinic generates a thin
|
||||
wrapper with the C signature of the slot, which forwards its arguments to
|
||||
the implementation function. The wrapper is the place where
|
||||
``@critical_section`` acquires the lock.
|
||||
|
||||
The parameters declared in the DSL only select the C signature; they must be
|
||||
positional-only, without a default value. When a special method maps to
|
||||
several slots (``__getitem__`` is ``sq_item`` or ``mp_subscript``),
|
||||
the converters of its parameters determine which one it implements.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
import dataclasses as dc
|
||||
from typing import Final
|
||||
|
||||
from libclinic.converter import CConverter
|
||||
|
||||
|
||||
@dc.dataclass(frozen=True, slots=True)
|
||||
class SlotSpec:
|
||||
# The special method name, e.g. '__next__'.
|
||||
name: str
|
||||
# The C return type of the slot function.
|
||||
return_type: str
|
||||
# The name of the return converter which declares `return_type`.
|
||||
return_converter: str
|
||||
# Maps the converter classes of the parameters (excluding self) to
|
||||
# the C slot.
|
||||
signatures: dict[tuple[type[CConverter], ...], str]
|
||||
|
||||
@property
|
||||
def error_value(self) -> str:
|
||||
"""The value returned by the slot function on error."""
|
||||
return 'NULL' if self.return_type.endswith('*') else '-1'
|
||||
|
||||
@property
|
||||
def takes_parameters(self) -> bool:
|
||||
return any(self.signatures)
|
||||
|
||||
def c_slot(self, converters: list[CConverter]) -> str | None:
|
||||
"""Return the C slot for the parameter converters, or None."""
|
||||
classes = tuple(type(c) for c in converters)
|
||||
return self.signatures.get(classes)
|
||||
|
||||
def describe_signatures(self) -> str:
|
||||
return " or ".join(
|
||||
f"({', '.join(c.dsl_name() for c in sig) or 'no parameters'})"
|
||||
f" for {slot}"
|
||||
for sig, slot in self.signatures.items())
|
||||
|
||||
|
||||
# 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('__next__', 'PyObject *', 'object', {
|
||||
(): 'tp_iternext',
|
||||
}),
|
||||
}
|
||||
Reference in new issue
Block a user