mirror of
https://github.com/python/cpython.git
synced 2026-05-06 12:49:07 -04:00
[3.14] gh-144545: Improve handling of default values in Argument Clinic (GH-146016) (GH-146052)
* Add the c_init_default attribute which is used to initialize the C variable
if the default is not explicitly provided.
* Add the c_default_init() method which is used to derive c_default from
default if c_default is not explicitly provided.
* Explicit c_default and py_default are now almost always have precedence
over the generated value.
* Add support for bytes literals as default values.
* Improve support for str literals as default values (support non-ASCII
and non-printable characters and special characters like backslash or quotes).
* Fix support for str and bytes literals containing trigraphs, "/*" and "*/".
* Improve support for default values in converters "char" and "int(accept={str})".
* Converter "int(accept={str})" now requires 1-character string instead of
integer as default value.
* Add support for non-None default values in converter "Py_buffer": NULL,
str and bytes literals.
* Improve error handling for invalid default values.
* Rename Null to NullType for consistency.
(cherry picked from commit 99e2c5eccd)
This commit is contained in:
+11
-11
@@ -530,19 +530,19 @@ test_char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
char a = 'A';
|
||||
char b = '\x07';
|
||||
char c = '\x08';
|
||||
char b = '\a';
|
||||
char c = '\b';
|
||||
char d = '\t';
|
||||
char e = '\n';
|
||||
char f = '\x0b';
|
||||
char g = '\x0c';
|
||||
char f = '\v';
|
||||
char g = '\f';
|
||||
char h = '\r';
|
||||
char i = '"';
|
||||
char j = '\'';
|
||||
char k = '?';
|
||||
char l = '\\';
|
||||
char m = '\x00';
|
||||
char n = '\xff';
|
||||
char m = '\0';
|
||||
char n = '\377';
|
||||
|
||||
if (!_PyArg_CheckPositional("test_char_converter", nargs, 0, 14)) {
|
||||
goto exit;
|
||||
@@ -936,7 +936,7 @@ static PyObject *
|
||||
test_char_converter_impl(PyObject *module, char a, char b, char c, char d,
|
||||
char e, char f, char g, char h, char i, char j,
|
||||
char k, char l, char m, char n)
|
||||
/*[clinic end generated code: output=ff11e203248582df input=e42330417a44feac]*/
|
||||
/*[clinic end generated code: output=6503d15448e1d4c4 input=e42330417a44feac]*/
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
@@ -1173,14 +1173,14 @@ test_int_converter
|
||||
|
||||
a: int = 12
|
||||
b: int(accept={int}) = 34
|
||||
c: int(accept={str}) = 45
|
||||
c: int(accept={str}) = '-'
|
||||
d: int(type='myenum') = 67
|
||||
/
|
||||
|
||||
[clinic start generated code]*/
|
||||
|
||||
PyDoc_STRVAR(test_int_converter__doc__,
|
||||
"test_int_converter($module, a=12, b=34, c=45, d=67, /)\n"
|
||||
"test_int_converter($module, a=12, b=34, c=\'-\', d=67, /)\n"
|
||||
"--\n"
|
||||
"\n");
|
||||
|
||||
@@ -1196,7 +1196,7 @@ test_int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
PyObject *return_value = NULL;
|
||||
int a = 12;
|
||||
int b = 34;
|
||||
int c = 45;
|
||||
int c = '-';
|
||||
myenum d = 67;
|
||||
|
||||
if (!_PyArg_CheckPositional("test_int_converter", nargs, 0, 4)) {
|
||||
@@ -1247,7 +1247,7 @@ exit:
|
||||
|
||||
static PyObject *
|
||||
test_int_converter_impl(PyObject *module, int a, int b, int c, myenum d)
|
||||
/*[clinic end generated code: output=fbcfb7554688663d input=d20541fc1ca0553e]*/
|
||||
/*[clinic end generated code: output=d5357b563bdb8789 input=5d8f4eb5899b24de]*/
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
|
||||
@@ -1044,6 +1044,187 @@ class ClinicParserTest(TestCase):
|
||||
p = function.parameters['follow_symlinks']
|
||||
self.assertEqual(True, p.default)
|
||||
|
||||
def test_param_default_none(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
obj: object = None
|
||||
str: str(accept={str, NoneType}) = None
|
||||
buf: Py_buffer(accept={str, buffer, NoneType}) = None
|
||||
""")
|
||||
p = function.parameters['obj']
|
||||
self.assertIs(p.default, None)
|
||||
self.assertEqual(p.converter.py_default, 'None')
|
||||
self.assertEqual(p.converter.c_default, 'Py_None')
|
||||
|
||||
p = function.parameters['str']
|
||||
self.assertIs(p.default, None)
|
||||
self.assertEqual(p.converter.py_default, 'None')
|
||||
self.assertEqual(p.converter.c_default, 'NULL')
|
||||
|
||||
p = function.parameters['buf']
|
||||
self.assertIs(p.default, None)
|
||||
self.assertEqual(p.converter.py_default, 'None')
|
||||
self.assertEqual(p.converter.c_default, '{NULL, NULL}')
|
||||
|
||||
def test_param_default_null(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
obj: object = NULL
|
||||
str: str = NULL
|
||||
buf: Py_buffer = NULL
|
||||
fsencoded: unicode_fs_encoded = NULL
|
||||
fsdecoded: unicode_fs_decoded = NULL
|
||||
""")
|
||||
p = function.parameters['obj']
|
||||
self.assertIs(p.default, NULL)
|
||||
self.assertEqual(p.converter.py_default, '<unrepresentable>')
|
||||
self.assertEqual(p.converter.c_default, 'NULL')
|
||||
|
||||
p = function.parameters['str']
|
||||
self.assertIs(p.default, NULL)
|
||||
self.assertEqual(p.converter.py_default, '<unrepresentable>')
|
||||
self.assertEqual(p.converter.c_default, 'NULL')
|
||||
|
||||
p = function.parameters['buf']
|
||||
self.assertIs(p.default, NULL)
|
||||
self.assertEqual(p.converter.py_default, '<unrepresentable>')
|
||||
self.assertEqual(p.converter.c_default, '{NULL, NULL}')
|
||||
|
||||
p = function.parameters['fsencoded']
|
||||
self.assertIs(p.default, NULL)
|
||||
self.assertEqual(p.converter.py_default, '<unrepresentable>')
|
||||
self.assertEqual(p.converter.c_default, 'NULL')
|
||||
|
||||
p = function.parameters['fsdecoded']
|
||||
self.assertIs(p.default, NULL)
|
||||
self.assertEqual(p.converter.py_default, '<unrepresentable>')
|
||||
self.assertEqual(p.converter.c_default, 'NULL')
|
||||
|
||||
def test_param_default_str_literal(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
str: str = ' \t\n\r\v\f\xa0'
|
||||
buf: Py_buffer(accept={str, buffer}) = ' \t\n\r\v\f\xa0'
|
||||
""")
|
||||
p = function.parameters['str']
|
||||
self.assertEqual(p.default, ' \t\n\r\v\f\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"' \t\n\r\x0b\x0c\xa0'")
|
||||
self.assertEqual(p.converter.c_default, r'" \t\n\r\v\f\u00a0"')
|
||||
|
||||
p = function.parameters['buf']
|
||||
self.assertEqual(p.default, ' \t\n\r\v\f\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"' \t\n\r\x0b\x0c\xa0'")
|
||||
self.assertEqual(p.converter.c_default,
|
||||
r'{.buf = " \t\n\r\v\f\302\240", .obj = NULL, .len = 8}')
|
||||
|
||||
def test_param_default_bytes_literal(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
str: str(accept={robuffer}) = b' \t\n\r\v\f\xa0'
|
||||
buf: Py_buffer = b' \t\n\r\v\f\xa0'
|
||||
""")
|
||||
p = function.parameters['str']
|
||||
self.assertEqual(p.default, b' \t\n\r\v\f\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"b' \t\n\r\x0b\x0c\xa0'")
|
||||
self.assertEqual(p.converter.c_default, r'" \t\n\r\v\f\240"')
|
||||
|
||||
p = function.parameters['buf']
|
||||
self.assertEqual(p.default, b' \t\n\r\v\f\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"b' \t\n\r\x0b\x0c\xa0'")
|
||||
self.assertEqual(p.converter.c_default,
|
||||
r'{.buf = " \t\n\r\v\f\240", .obj = NULL, .len = 7}')
|
||||
|
||||
def test_param_default_byte_literal(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
zero: char = b'\0'
|
||||
one: char = b'\1'
|
||||
lf: char = b'\n'
|
||||
nbsp: char = b'\xa0'
|
||||
""")
|
||||
p = function.parameters['zero']
|
||||
self.assertEqual(p.default, b'\0')
|
||||
self.assertEqual(p.converter.py_default, r"b'\x00'")
|
||||
self.assertEqual(p.converter.c_default, r"'\0'")
|
||||
|
||||
p = function.parameters['one']
|
||||
self.assertEqual(p.default, b'\1')
|
||||
self.assertEqual(p.converter.py_default, r"b'\x01'")
|
||||
self.assertEqual(p.converter.c_default, r"'\001'")
|
||||
|
||||
p = function.parameters['lf']
|
||||
self.assertEqual(p.default, b'\n')
|
||||
self.assertEqual(p.converter.py_default, r"b'\n'")
|
||||
self.assertEqual(p.converter.c_default, r"'\n'")
|
||||
|
||||
p = function.parameters['nbsp']
|
||||
self.assertEqual(p.default, b'\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"b'\xa0'")
|
||||
self.assertEqual(p.converter.c_default, r"'\240'")
|
||||
|
||||
def test_param_default_unicode_char(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
zero: int(accept={str}) = '\0'
|
||||
one: int(accept={str}) = '\1'
|
||||
lf: int(accept={str}) = '\n'
|
||||
nbsp: int(accept={str}) = '\xa0'
|
||||
snake: int(accept={str}) = '\U0001f40d'
|
||||
""")
|
||||
p = function.parameters['zero']
|
||||
self.assertEqual(p.default, '\0')
|
||||
self.assertEqual(p.converter.py_default, r"'\x00'")
|
||||
self.assertEqual(p.converter.c_default, '0')
|
||||
|
||||
p = function.parameters['one']
|
||||
self.assertEqual(p.default, '\1')
|
||||
self.assertEqual(p.converter.py_default, r"'\x01'")
|
||||
self.assertEqual(p.converter.c_default, '0x01')
|
||||
|
||||
p = function.parameters['lf']
|
||||
self.assertEqual(p.default, '\n')
|
||||
self.assertEqual(p.converter.py_default, r"'\n'")
|
||||
self.assertEqual(p.converter.c_default, r"'\n'")
|
||||
|
||||
p = function.parameters['nbsp']
|
||||
self.assertEqual(p.default, '\xa0')
|
||||
self.assertEqual(p.converter.py_default, r"'\xa0'")
|
||||
self.assertEqual(p.converter.c_default, '0xa0')
|
||||
|
||||
p = function.parameters['snake']
|
||||
self.assertEqual(p.default, '\U0001f40d')
|
||||
self.assertEqual(p.converter.py_default, "'\U0001f40d'")
|
||||
self.assertEqual(p.converter.c_default, '0x1f40d')
|
||||
|
||||
def test_param_default_bool(self):
|
||||
function = self.parse_function(r"""
|
||||
module test
|
||||
test.func
|
||||
bool: bool = True
|
||||
intbool: bool(accept={int}) = True
|
||||
intbool2: bool(accept={int}) = 2
|
||||
""")
|
||||
p = function.parameters['bool']
|
||||
self.assertIs(p.default, True)
|
||||
self.assertEqual(p.converter.py_default, 'True')
|
||||
self.assertEqual(p.converter.c_default, '1')
|
||||
|
||||
p = function.parameters['intbool']
|
||||
self.assertIs(p.default, True)
|
||||
self.assertEqual(p.converter.py_default, 'True')
|
||||
self.assertEqual(p.converter.c_default, '1')
|
||||
|
||||
p = function.parameters['intbool2']
|
||||
self.assertEqual(p.default, 2)
|
||||
self.assertEqual(p.converter.py_default, '2')
|
||||
self.assertEqual(p.converter.c_default, '2')
|
||||
|
||||
def test_param_default_expr_named_constant(self):
|
||||
function = self.parse_function("""
|
||||
module os
|
||||
@@ -4209,6 +4390,56 @@ class FormatHelperTests(unittest.TestCase):
|
||||
out = libclinic.format_escape(line)
|
||||
self.assertEqual(out, expected)
|
||||
|
||||
def test_c_bytes_repr(self):
|
||||
c_bytes_repr = libclinic.c_bytes_repr
|
||||
self.assertEqual(c_bytes_repr(b''), '""')
|
||||
self.assertEqual(c_bytes_repr(b'abc'), '"abc"')
|
||||
self.assertEqual(c_bytes_repr(b'\a\b\f\n\r\t\v'), r'"\a\b\f\n\r\t\v"')
|
||||
self.assertEqual(c_bytes_repr(b' \0\x7f'), r'" \000\177"')
|
||||
self.assertEqual(c_bytes_repr(b'"'), r'"\""')
|
||||
self.assertEqual(c_bytes_repr(b"'"), r'''"'"''')
|
||||
self.assertEqual(c_bytes_repr(b'\\'), r'"\\"')
|
||||
self.assertEqual(c_bytes_repr(b'??/'), r'"?\?/"')
|
||||
self.assertEqual(c_bytes_repr(b'???/'), r'"?\?\?/"')
|
||||
self.assertEqual(c_bytes_repr(b'/*****/ /*/ */*'), r'"/\*****\/ /\*\/ *\/\*"')
|
||||
self.assertEqual(c_bytes_repr(b'\xa0'), r'"\240"')
|
||||
self.assertEqual(c_bytes_repr(b'\xff'), r'"\377"')
|
||||
|
||||
def test_c_str_repr(self):
|
||||
c_str_repr = libclinic.c_str_repr
|
||||
self.assertEqual(c_str_repr(''), '""')
|
||||
self.assertEqual(c_str_repr('abc'), '"abc"')
|
||||
self.assertEqual(c_str_repr('\a\b\f\n\r\t\v'), r'"\a\b\f\n\r\t\v"')
|
||||
self.assertEqual(c_str_repr(' \0\x7f'), r'" \000\177"')
|
||||
self.assertEqual(c_str_repr('"'), r'"\""')
|
||||
self.assertEqual(c_str_repr("'"), r'''"'"''')
|
||||
self.assertEqual(c_str_repr('\\'), r'"\\"')
|
||||
self.assertEqual(c_str_repr('??/'), r'"?\?/"')
|
||||
self.assertEqual(c_str_repr('???/'), r'"?\?\?/"')
|
||||
self.assertEqual(c_str_repr('/*****/ /*/ */*'), r'"/\*****\/ /\*\/ *\/\*"')
|
||||
self.assertEqual(c_str_repr('\xa0'), r'"\u00a0"')
|
||||
self.assertEqual(c_str_repr('\xff'), r'"\u00ff"')
|
||||
self.assertEqual(c_str_repr('\u20ac'), r'"\u20ac"')
|
||||
self.assertEqual(c_str_repr('\U0001f40d'), r'"\U0001f40d"')
|
||||
|
||||
def test_c_unichar_repr(self):
|
||||
c_unichar_repr = libclinic.c_unichar_repr
|
||||
self.assertEqual(c_unichar_repr('a'), "'a'")
|
||||
self.assertEqual(c_unichar_repr('\n'), r"'\n'")
|
||||
self.assertEqual(c_unichar_repr('\b'), r"'\b'")
|
||||
self.assertEqual(c_unichar_repr('\0'), '0')
|
||||
self.assertEqual(c_unichar_repr('\1'), '0x01')
|
||||
self.assertEqual(c_unichar_repr('\x7f'), '0x7f')
|
||||
self.assertEqual(c_unichar_repr(' '), "' '")
|
||||
self.assertEqual(c_unichar_repr('"'), """'"'""")
|
||||
self.assertEqual(c_unichar_repr("'"), r"'\''")
|
||||
self.assertEqual(c_unichar_repr('\\'), r"'\\'")
|
||||
self.assertEqual(c_unichar_repr('?'), "'?'")
|
||||
self.assertEqual(c_unichar_repr('\xa0'), '0xa0')
|
||||
self.assertEqual(c_unichar_repr('\xff'), '0xff')
|
||||
self.assertEqual(c_unichar_repr('\u20ac'), '0x20ac')
|
||||
self.assertEqual(c_unichar_repr('\U0001f40d'), '0x1f40d')
|
||||
|
||||
def test_indent_all_lines(self):
|
||||
# Blank lines are expected to be unchanged.
|
||||
self.assertEqual(libclinic.indent_all_lines("", prefix="bar"), "")
|
||||
|
||||
@@ -334,14 +334,14 @@ int_converter
|
||||
|
||||
a: int = 12
|
||||
b: int(accept={int}) = 34
|
||||
c: int(accept={str}) = 45
|
||||
c: int(accept={str}) = '-'
|
||||
/
|
||||
|
||||
[clinic start generated code]*/
|
||||
|
||||
static PyObject *
|
||||
int_converter_impl(PyObject *module, int a, int b, int c)
|
||||
/*[clinic end generated code: output=8e56b59be7d0c306 input=a1dbc6344853db7a]*/
|
||||
/*[clinic end generated code: output=8e56b59be7d0c306 input=9a306d4dc907e339]*/
|
||||
{
|
||||
RETURN_PACKED_ARGS(3, PyLong_FromLong, long, a, b, c);
|
||||
}
|
||||
@@ -1360,6 +1360,7 @@ clone_f2_impl(PyObject *module, const char *path)
|
||||
class custom_t_converter(CConverter):
|
||||
type = 'custom_t'
|
||||
converter = 'custom_converter'
|
||||
c_init_default = "<placeholder>" # overridden in pre_render(()
|
||||
|
||||
def pre_render(self):
|
||||
self.c_default = f'''{{
|
||||
@@ -1367,7 +1368,7 @@ class custom_t_converter(CConverter):
|
||||
}}'''
|
||||
|
||||
[python start generated code]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=b2fb801e99a06bf6]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=78fe84e5ecc0481b]*/
|
||||
|
||||
|
||||
/*[clinic input]
|
||||
|
||||
@@ -658,9 +658,9 @@ _blake2.blake2b.__new__ as py_blake2b_new
|
||||
data as data_obj: object(c_default="NULL") = b''
|
||||
*
|
||||
digest_size: int(c_default="HACL_HASH_BLAKE2B_OUT_BYTES") = _blake2.blake2b.MAX_DIGEST_SIZE
|
||||
key: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
salt: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
person: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
key: Py_buffer = b''
|
||||
salt: Py_buffer = b''
|
||||
person: Py_buffer = b''
|
||||
fanout: int = 1
|
||||
depth: int = 1
|
||||
leaf_size: unsigned_long = 0
|
||||
@@ -681,7 +681,7 @@ py_blake2b_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size,
|
||||
unsigned long long node_offset, int node_depth,
|
||||
int inner_size, int last_node, int usedforsecurity,
|
||||
PyObject *string)
|
||||
/*[clinic end generated code: output=de64bd850606b6a0 input=78cf60a2922d2f90]*/
|
||||
/*[clinic end generated code: output=de64bd850606b6a0 input=32832fb37d13c03d]*/
|
||||
{
|
||||
PyObject *data;
|
||||
if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) {
|
||||
@@ -696,9 +696,9 @@ _blake2.blake2s.__new__ as py_blake2s_new
|
||||
data as data_obj: object(c_default="NULL") = b''
|
||||
*
|
||||
digest_size: int(c_default="HACL_HASH_BLAKE2S_OUT_BYTES") = _blake2.blake2s.MAX_DIGEST_SIZE
|
||||
key: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
salt: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
person: Py_buffer(c_default="NULL", py_default="b''") = None
|
||||
key: Py_buffer = b''
|
||||
salt: Py_buffer = b''
|
||||
person: Py_buffer = b''
|
||||
fanout: int = 1
|
||||
depth: int = 1
|
||||
leaf_size: unsigned_long = 0
|
||||
@@ -719,7 +719,7 @@ py_blake2s_new_impl(PyTypeObject *type, PyObject *data_obj, int digest_size,
|
||||
unsigned long long node_offset, int node_depth,
|
||||
int inner_size, int last_node, int usedforsecurity,
|
||||
PyObject *string)
|
||||
/*[clinic end generated code: output=582a0c4295cc3a3c input=6843d6332eefd295]*/
|
||||
/*[clinic end generated code: output=582a0c4295cc3a3c input=da467fc9dae646bb]*/
|
||||
{
|
||||
PyObject *data;
|
||||
if (_Py_hashlib_data_argument(&data, data_obj, string) < 0) {
|
||||
|
||||
Generated
+9
-9
@@ -273,19 +273,19 @@ char_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
{
|
||||
PyObject *return_value = NULL;
|
||||
char a = 'A';
|
||||
char b = '\x07';
|
||||
char c = '\x08';
|
||||
char b = '\a';
|
||||
char c = '\b';
|
||||
char d = '\t';
|
||||
char e = '\n';
|
||||
char f = '\x0b';
|
||||
char g = '\x0c';
|
||||
char f = '\v';
|
||||
char g = '\f';
|
||||
char h = '\r';
|
||||
char i = '"';
|
||||
char j = '\'';
|
||||
char k = '?';
|
||||
char l = '\\';
|
||||
char m = '\x00';
|
||||
char n = '\xff';
|
||||
char m = '\0';
|
||||
char n = '\377';
|
||||
|
||||
if (!_PyArg_CheckPositional("char_converter", nargs, 0, 14)) {
|
||||
goto exit;
|
||||
@@ -860,7 +860,7 @@ exit:
|
||||
}
|
||||
|
||||
PyDoc_STRVAR(int_converter__doc__,
|
||||
"int_converter($module, a=12, b=34, c=45, /)\n"
|
||||
"int_converter($module, a=12, b=34, c=\'-\', /)\n"
|
||||
"--\n"
|
||||
"\n");
|
||||
|
||||
@@ -876,7 +876,7 @@ int_converter(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
|
||||
PyObject *return_value = NULL;
|
||||
int a = 12;
|
||||
int b = 34;
|
||||
int c = 45;
|
||||
int c = '-';
|
||||
|
||||
if (!_PyArg_CheckPositional("int_converter", nargs, 0, 3)) {
|
||||
goto exit;
|
||||
@@ -4481,4 +4481,4 @@ _testclinic_TestClass_posonly_poskw_varpos_array_no_fastcall(PyObject *type, PyO
|
||||
exit:
|
||||
return return_value;
|
||||
}
|
||||
/*[clinic end generated code: output=84ffc31f27215baa input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=8af194d826d6740d input=a9049054013a1b77]*/
|
||||
|
||||
Generated
+7
-7
@@ -63,9 +63,9 @@ py_blake2b_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
|
||||
PyObject *data_obj = NULL;
|
||||
int digest_size = HACL_HASH_BLAKE2B_OUT_BYTES;
|
||||
Py_buffer key = {NULL, NULL};
|
||||
Py_buffer salt = {NULL, NULL};
|
||||
Py_buffer person = {NULL, NULL};
|
||||
Py_buffer key = {.buf = "", .obj = NULL, .len = 0};
|
||||
Py_buffer salt = {.buf = "", .obj = NULL, .len = 0};
|
||||
Py_buffer person = {.buf = "", .obj = NULL, .len = 0};
|
||||
int fanout = 1;
|
||||
int depth = 1;
|
||||
unsigned long leaf_size = 0;
|
||||
@@ -272,9 +272,9 @@ py_blake2s_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
|
||||
Py_ssize_t noptargs = nargs + (kwargs ? PyDict_GET_SIZE(kwargs) : 0) - 0;
|
||||
PyObject *data_obj = NULL;
|
||||
int digest_size = HACL_HASH_BLAKE2S_OUT_BYTES;
|
||||
Py_buffer key = {NULL, NULL};
|
||||
Py_buffer salt = {NULL, NULL};
|
||||
Py_buffer person = {NULL, NULL};
|
||||
Py_buffer key = {.buf = "", .obj = NULL, .len = 0};
|
||||
Py_buffer salt = {.buf = "", .obj = NULL, .len = 0};
|
||||
Py_buffer person = {.buf = "", .obj = NULL, .len = 0};
|
||||
int fanout = 1;
|
||||
int depth = 1;
|
||||
unsigned long leaf_size = 0;
|
||||
@@ -502,4 +502,4 @@ _blake2_blake2b_hexdigest(PyObject *self, PyObject *Py_UNUSED(ignored))
|
||||
{
|
||||
return _blake2_blake2b_hexdigest_impl((Blake2Object *)self);
|
||||
}
|
||||
/*[clinic end generated code: output=eed18dcfaf6f7731 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=bf30e70c312718cb input=a9049054013a1b77]*/
|
||||
|
||||
Generated
+2
-2
@@ -205,7 +205,7 @@ exit:
|
||||
PyDoc_STRVAR(zlib_compressobj__doc__,
|
||||
"compressobj($module, /, level=Z_DEFAULT_COMPRESSION, method=DEFLATED,\n"
|
||||
" wbits=MAX_WBITS, memLevel=DEF_MEM_LEVEL,\n"
|
||||
" strategy=Z_DEFAULT_STRATEGY, zdict=None)\n"
|
||||
" strategy=Z_DEFAULT_STRATEGY, zdict=<unrepresentable>)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return a compressor object.\n"
|
||||
@@ -1121,4 +1121,4 @@ exit:
|
||||
#ifndef ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF
|
||||
#define ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF
|
||||
#endif /* !defined(ZLIB_DECOMPRESS___DEEPCOPY___METHODDEF) */
|
||||
/*[clinic end generated code: output=33938c7613a8c1c7 input=a9049054013a1b77]*/
|
||||
/*[clinic end generated code: output=3611ce90fe05accb input=a9049054013a1b77]*/
|
||||
|
||||
+10
-8
@@ -3057,25 +3057,22 @@ class path_t_converter(CConverter):
|
||||
type = "path_t"
|
||||
impl_by_reference = True
|
||||
parse_by_reference = True
|
||||
default_type = ()
|
||||
c_init_default = "<placeholder>" # overridden in pre_render(()
|
||||
|
||||
converter = 'path_converter'
|
||||
|
||||
def converter_init(self, *, allow_fd=False, make_wide=None,
|
||||
nonstrict=False, nullable=False,
|
||||
suppress_value_error=False):
|
||||
# right now path_t doesn't support default values.
|
||||
# to support a default value, you'll need to override initialize().
|
||||
if self.default not in (unspecified, None):
|
||||
fail("Can't specify a default to the path_t converter!")
|
||||
|
||||
if self.c_default not in (None, 'Py_None'):
|
||||
raise RuntimeError("Can't specify a c_default to the path_t converter!")
|
||||
|
||||
self.nullable = nullable
|
||||
self.nonstrict = nonstrict
|
||||
self.make_wide = make_wide
|
||||
self.suppress_value_error = suppress_value_error
|
||||
self.allow_fd = allow_fd
|
||||
if nullable:
|
||||
self.default_type = NoneType
|
||||
|
||||
def pre_render(self):
|
||||
def strify(value):
|
||||
@@ -3110,6 +3107,8 @@ class path_t_converter(CConverter):
|
||||
|
||||
class dir_fd_converter(CConverter):
|
||||
type = 'int'
|
||||
default_type = NoneType
|
||||
c_init_default = 'DEFAULT_DIR_FD'
|
||||
|
||||
def converter_init(self, requires=None):
|
||||
if self.default in (unspecified, None):
|
||||
@@ -3119,6 +3118,9 @@ class dir_fd_converter(CConverter):
|
||||
else:
|
||||
self.converter = 'dir_fd_converter'
|
||||
|
||||
def c_default_init(self):
|
||||
self.c_default = 'DEFAULT_DIR_FD'
|
||||
|
||||
class uid_t_converter(CConverter):
|
||||
type = "uid_t"
|
||||
converter = '_Py_Uid_Converter'
|
||||
@@ -3199,7 +3201,7 @@ class confname_converter(CConverter):
|
||||
""", argname=argname, converter=self.converter, table=self.table)
|
||||
|
||||
[python start generated code]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=d2759f2332cd39b3]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=d58f18bdf3bd3565]*/
|
||||
|
||||
/*[clinic input]
|
||||
|
||||
|
||||
@@ -556,7 +556,7 @@ zlib.compressobj
|
||||
strategy: int(c_default="Z_DEFAULT_STRATEGY") = Z_DEFAULT_STRATEGY
|
||||
Used to tune the compression algorithm. Possible values are
|
||||
Z_DEFAULT_STRATEGY, Z_FILTERED, and Z_HUFFMAN_ONLY.
|
||||
zdict: Py_buffer = None
|
||||
zdict: Py_buffer = NULL
|
||||
The predefined compression dictionary - a sequence of bytes
|
||||
containing subsequences that are likely to occur in the input data.
|
||||
|
||||
@@ -566,7 +566,7 @@ Return a compressor object.
|
||||
static PyObject *
|
||||
zlib_compressobj_impl(PyObject *module, int level, int method, int wbits,
|
||||
int memLevel, int strategy, Py_buffer *zdict)
|
||||
/*[clinic end generated code: output=8b5bed9c8fc3814d input=2fa3d026f90ab8d5]*/
|
||||
/*[clinic end generated code: output=8b5bed9c8fc3814d input=1a6f61d8a8885c0d]*/
|
||||
{
|
||||
zlibstate *state = get_zlib_state(module);
|
||||
if (zdict->buf != NULL && (size_t)zdict->len > UINT_MAX) {
|
||||
|
||||
@@ -87,14 +87,12 @@ class Py_UCS4_converter(CConverter):
|
||||
type = 'Py_UCS4'
|
||||
converter = 'convert_uc'
|
||||
|
||||
def converter_init(self):
|
||||
if self.default is not unspecified:
|
||||
self.c_default = ascii(self.default)
|
||||
if len(self.c_default) > 4 or self.c_default[0] != "'":
|
||||
self.c_default = hex(ord(self.default))
|
||||
def c_default_init(self):
|
||||
import libclinic
|
||||
self.c_default = libclinic.c_unichar_repr(self.default)
|
||||
|
||||
[python start generated code]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=88f5dd06cd8e7a61]*/
|
||||
/*[python end generated code: output=da39a3ee5e6b4b0d input=22f057b68fd9a65a]*/
|
||||
|
||||
/* --- Globals ------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -333,6 +333,7 @@ MAX_SIZES = {
|
||||
_abs('Modules/_ssl_data_300.h'): (80_000, 10_000),
|
||||
_abs('Modules/_ssl_data_111.h'): (80_000, 10_000),
|
||||
_abs('Modules/cjkcodecs/mappings_*.h'): (160_000, 2_000),
|
||||
_abs('Modules/clinic/_testclinic.c.h'): (120_000, 5_000),
|
||||
_abs('Modules/unicodedata_db.h'): (180_000, 3_000),
|
||||
_abs('Modules/unicodename_db.h'): (1_200_000, 15_000),
|
||||
_abs('Objects/unicodetype_db.h'): (240_000, 3_000),
|
||||
|
||||
@@ -7,7 +7,9 @@ from .errors import (
|
||||
)
|
||||
from .formatting import (
|
||||
SIG_END_MARKER,
|
||||
c_repr,
|
||||
c_str_repr,
|
||||
c_bytes_repr,
|
||||
c_unichar_repr,
|
||||
docstring_for_c_string,
|
||||
format_escape,
|
||||
indent_all_lines,
|
||||
@@ -26,7 +28,7 @@ from .identifiers import (
|
||||
from .utils import (
|
||||
FormatCounterFormatter,
|
||||
NULL,
|
||||
Null,
|
||||
NullType,
|
||||
Sentinels,
|
||||
VersionTuple,
|
||||
compute_checksum,
|
||||
@@ -45,7 +47,9 @@ __all__ = [
|
||||
|
||||
# Formatting helpers
|
||||
"SIG_END_MARKER",
|
||||
"c_repr",
|
||||
"c_str_repr",
|
||||
"c_bytes_repr",
|
||||
"c_unichar_repr",
|
||||
"docstring_for_c_string",
|
||||
"format_escape",
|
||||
"indent_all_lines",
|
||||
@@ -64,7 +68,7 @@ __all__ = [
|
||||
# Utility functions
|
||||
"FormatCounterFormatter",
|
||||
"NULL",
|
||||
"Null",
|
||||
"NullType",
|
||||
"Sentinels",
|
||||
"VersionTuple",
|
||||
"compute_checksum",
|
||||
|
||||
@@ -101,7 +101,7 @@ class CLanguage(Language):
|
||||
code = self.COMPILER_DEPRECATION_WARNING_PROTOTYPE.format(
|
||||
major=minversion[0],
|
||||
minor=minversion[1],
|
||||
message=libclinic.c_repr(message),
|
||||
message=libclinic.c_str_repr(message),
|
||||
)
|
||||
return libclinic.normalize_snippet(code)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections.abc import Callable
|
||||
|
||||
import libclinic
|
||||
from libclinic import fail
|
||||
from libclinic import Sentinels, unspecified, unknown
|
||||
from libclinic import Sentinels, unspecified, unknown, NULL
|
||||
from libclinic.codegen import CRenderData, Include, TemplateDict
|
||||
from libclinic.function import Function, Parameter
|
||||
|
||||
@@ -83,9 +83,9 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
# at runtime).
|
||||
default: object = unspecified
|
||||
|
||||
# If not None, default must be isinstance() of this type.
|
||||
# default must be isinstance() of this type.
|
||||
# (You can also specify a tuple of types.)
|
||||
default_type: bltns.type[object] | tuple[bltns.type[object], ...] | None = None
|
||||
default_type: bltns.type[object] | tuple[bltns.type[object], ...] = object
|
||||
|
||||
# "default" converted into a C value, as a string.
|
||||
# Or None if there is no default.
|
||||
@@ -95,6 +95,13 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
# Or None if there is no default.
|
||||
py_default: str | None = None
|
||||
|
||||
# The default value used to initialize the C variable when
|
||||
# there is no default.
|
||||
#
|
||||
# Every non-abstract subclass with non-trivial cleanup() should supply
|
||||
# a valid value.
|
||||
c_init_default: str = ''
|
||||
|
||||
# The default value used to initialize the C variable when
|
||||
# there is no default, but not specifying a default may
|
||||
# result in an "uninitialized variable" warning. This can
|
||||
@@ -105,7 +112,7 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
#
|
||||
# This value is specified as a string.
|
||||
# Every non-abstract subclass should supply a valid value.
|
||||
c_ignored_default: str = 'NULL'
|
||||
c_ignored_default: str = ''
|
||||
|
||||
# If true, wrap with Py_UNUSED.
|
||||
unused = False
|
||||
@@ -182,21 +189,6 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
self.unused = unused
|
||||
self._includes: list[Include] = []
|
||||
|
||||
if default is not unspecified:
|
||||
if (self.default_type
|
||||
and default is not unknown
|
||||
and not isinstance(default, self.default_type)
|
||||
):
|
||||
if isinstance(self.default_type, type):
|
||||
types_str = self.default_type.__name__
|
||||
else:
|
||||
names = [cls.__name__ for cls in self.default_type]
|
||||
types_str = ', '.join(names)
|
||||
cls_name = self.__class__.__name__
|
||||
fail(f"{cls_name}: default value {default!r} for field "
|
||||
f"{name!r} is not of type {types_str!r}")
|
||||
self.default = default
|
||||
|
||||
if c_default:
|
||||
self.c_default = c_default
|
||||
if py_default:
|
||||
@@ -210,6 +202,56 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
# about the function in converter_init().
|
||||
# (That breaks if we get cloned.)
|
||||
self.converter_init(**kwargs)
|
||||
|
||||
if default is not unspecified:
|
||||
if self.default_type == ():
|
||||
conv_name = self.__class__.__name__.removesuffix('_converter')
|
||||
fail(f"A '{conv_name}' parameter cannot be marked optional.")
|
||||
if (default is not unknown
|
||||
and not isinstance(default, self.default_type)
|
||||
):
|
||||
if isinstance(self.default_type, type):
|
||||
types_str = self.default_type.__name__
|
||||
else:
|
||||
names = [cls.__name__ for cls in self.default_type]
|
||||
types_str = ', '.join(names)
|
||||
cls_name = self.__class__.__name__
|
||||
fail(f"{cls_name}: default value {default!r} for field "
|
||||
f"{name!r} is not of type {types_str!r}")
|
||||
self.default = default
|
||||
|
||||
if not self.c_default:
|
||||
if default is unspecified:
|
||||
if self.c_init_default:
|
||||
self.c_default = self.c_init_default
|
||||
elif default is NULL:
|
||||
self.c_default = self.c_ignored_default or self.c_init_default
|
||||
if not self.c_default:
|
||||
cls_name = self.__class__.__name__
|
||||
fail(f"{cls_name}: c_default is required for "
|
||||
f"default value NULL")
|
||||
else:
|
||||
assert default is not unknown
|
||||
self.c_default_init()
|
||||
if not self.c_default:
|
||||
if default is None:
|
||||
self.c_default = self.c_init_default
|
||||
if not self.c_default:
|
||||
cls_name = self.__class__.__name__
|
||||
fail(f"{cls_name}: c_default is required for "
|
||||
f"default value None")
|
||||
elif isinstance(default, str):
|
||||
self.c_default = libclinic.c_str_repr(default)
|
||||
elif isinstance(default, bytes):
|
||||
self.c_default = libclinic.c_bytes_repr(default)
|
||||
elif isinstance(default, (int, float)):
|
||||
self.c_default = repr(default)
|
||||
else:
|
||||
cls_name = self.__class__.__name__
|
||||
fail(f"{cls_name}: c_default is required for "
|
||||
f"default value {default!r}")
|
||||
fail(f"Unsupported default value {default!r}.")
|
||||
|
||||
self.function = function
|
||||
|
||||
# Add a custom __getattr__ method to improve the error message
|
||||
@@ -233,6 +275,9 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
def converter_init(self) -> None:
|
||||
pass
|
||||
|
||||
def c_default_init(self) -> None:
|
||||
return
|
||||
|
||||
def is_optional(self) -> bool:
|
||||
return (self.default is not unspecified)
|
||||
|
||||
@@ -324,7 +369,7 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
args.append(self.converter)
|
||||
|
||||
if self.encoding:
|
||||
args.append(libclinic.c_repr(self.encoding))
|
||||
args.append(libclinic.c_str_repr(self.encoding))
|
||||
elif self.subclass_of:
|
||||
args.append(self.subclass_of)
|
||||
|
||||
@@ -371,7 +416,7 @@ class CConverter(metaclass=CConverterAutoRegister):
|
||||
declaration = [self.simple_declaration(in_parser=True)]
|
||||
default = self.c_default
|
||||
if not default and self.parameter.group:
|
||||
default = self.c_ignored_default
|
||||
default = self.c_ignored_default or self.c_init_default
|
||||
if default:
|
||||
declaration.append(" = ")
|
||||
declaration.append(default)
|
||||
|
||||
@@ -4,7 +4,7 @@ import sys
|
||||
from types import NoneType
|
||||
from typing import Any
|
||||
|
||||
from libclinic import fail, Null, unspecified, unknown
|
||||
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,
|
||||
@@ -18,6 +18,9 @@ TypeSet = set[bltns.type[object]]
|
||||
|
||||
|
||||
class BaseUnsignedIntConverter(CConverter):
|
||||
bitwise = False
|
||||
default_type = int
|
||||
c_ignored_default = '0'
|
||||
|
||||
def use_converter(self) -> None:
|
||||
if self.converter:
|
||||
@@ -74,12 +77,13 @@ class bool_converter(CConverter):
|
||||
def converter_init(self, *, accept: TypeSet = {object}) -> None:
|
||||
if accept == {int}:
|
||||
self.format_unit = 'i'
|
||||
self.default_type = int # type: ignore[assignment]
|
||||
elif accept != {object}:
|
||||
fail(f"bool_converter: illegal 'accept' argument {accept!r}")
|
||||
if self.default is not unspecified and self.default is not unknown:
|
||||
self.default = bool(self.default)
|
||||
if self.c_default in {'Py_True', 'Py_False'}:
|
||||
self.c_default = str(int(self.default))
|
||||
|
||||
def c_default_init(self) -> None:
|
||||
assert isinstance(self.default, int)
|
||||
self.c_default = str(int(self.default))
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'i':
|
||||
@@ -107,6 +111,7 @@ class defining_class_converter(CConverter):
|
||||
this is the default converter used for the defining class.
|
||||
"""
|
||||
type = 'PyTypeObject *'
|
||||
default_type = ()
|
||||
format_unit = ''
|
||||
show_in_signature = False
|
||||
specified_type: str | None = None
|
||||
@@ -123,7 +128,7 @@ class defining_class_converter(CConverter):
|
||||
|
||||
class char_converter(CConverter):
|
||||
type = 'char'
|
||||
default_type = (bytes, bytearray)
|
||||
default_type = bytes
|
||||
format_unit = 'c'
|
||||
c_ignored_default = "'\0'"
|
||||
|
||||
@@ -132,9 +137,18 @@ class char_converter(CConverter):
|
||||
if len(self.default) != 1:
|
||||
fail(f"char_converter: illegal default value {self.default!r}")
|
||||
|
||||
self.c_default = repr(bytes(self.default))[1:]
|
||||
if self.c_default == '"\'"':
|
||||
self.c_default = r"'\''"
|
||||
def c_default_init(self) -> None:
|
||||
default = self.default
|
||||
assert isinstance(default, bytes)
|
||||
if default == b"'":
|
||||
self.c_default = r"'\''"
|
||||
elif default == b'"':
|
||||
self.c_default = r"""'"'"""
|
||||
elif default == b'\0':
|
||||
self.c_default = r"'\0'"
|
||||
else:
|
||||
r = c_bytes_repr(default)[1:-1]
|
||||
self.c_default = "'" + r + "'"
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'c':
|
||||
@@ -174,7 +188,6 @@ class char_converter(CConverter):
|
||||
@add_legacy_c_converter('B', bitwise=True)
|
||||
class unsigned_char_converter(CConverter):
|
||||
type = 'unsigned char'
|
||||
default_type = int
|
||||
format_unit = 'b'
|
||||
c_ignored_default = "'\0'"
|
||||
|
||||
@@ -261,8 +274,6 @@ class short_converter(CConverter):
|
||||
|
||||
class unsigned_short_converter(BaseUnsignedIntConverter):
|
||||
type = 'unsigned short'
|
||||
default_type = int
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, bitwise: bool = False) -> None:
|
||||
if bitwise:
|
||||
@@ -294,11 +305,19 @@ class int_converter(CConverter):
|
||||
) -> None:
|
||||
if accept == {str}:
|
||||
self.format_unit = 'C'
|
||||
self.default_type = str # type: ignore[assignment]
|
||||
if isinstance(self.default, str):
|
||||
if len(self.default) != 1:
|
||||
fail(f"int_converter: illegal default value {self.default!r}")
|
||||
elif accept != {int}:
|
||||
fail(f"int_converter: illegal 'accept' argument {accept!r}")
|
||||
if type is not None:
|
||||
self.type = type
|
||||
|
||||
def c_default_init(self) -> None:
|
||||
if isinstance(self.default, str):
|
||||
self.c_default = c_unichar_repr(self.default)
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'i':
|
||||
return self.format_code("""
|
||||
@@ -332,8 +351,6 @@ class int_converter(CConverter):
|
||||
|
||||
class unsigned_int_converter(BaseUnsignedIntConverter):
|
||||
type = 'unsigned int'
|
||||
default_type = int
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, bitwise: bool = False) -> None:
|
||||
if bitwise:
|
||||
@@ -373,8 +390,6 @@ class long_converter(CConverter):
|
||||
|
||||
class unsigned_long_converter(BaseUnsignedIntConverter):
|
||||
type = 'unsigned long'
|
||||
default_type = int
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, bitwise: bool = False) -> None:
|
||||
if bitwise:
|
||||
@@ -417,8 +432,6 @@ class long_long_converter(CConverter):
|
||||
|
||||
class unsigned_long_long_converter(BaseUnsignedIntConverter):
|
||||
type = 'unsigned long long'
|
||||
default_type = int
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, bitwise: bool = False) -> None:
|
||||
if bitwise:
|
||||
@@ -443,12 +456,13 @@ class unsigned_long_long_converter(BaseUnsignedIntConverter):
|
||||
|
||||
class Py_ssize_t_converter(CConverter):
|
||||
type = 'Py_ssize_t'
|
||||
default_type = (int, NoneType)
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, accept: TypeSet = {int}) -> None:
|
||||
if accept == {int}:
|
||||
self.format_unit = 'n'
|
||||
self.default_type = int
|
||||
self.default_type = int # type: ignore[assignment]
|
||||
elif accept == {int, NoneType}:
|
||||
self.converter = '_Py_convert_optional_to_ssize_t'
|
||||
else:
|
||||
@@ -505,10 +519,13 @@ class Py_ssize_t_converter(CConverter):
|
||||
|
||||
class slice_index_converter(CConverter):
|
||||
type = 'Py_ssize_t'
|
||||
default_type = (int, NoneType)
|
||||
c_ignored_default = "0"
|
||||
|
||||
def converter_init(self, *, accept: TypeSet = {int, NoneType}) -> None:
|
||||
if accept == {int}:
|
||||
self.converter = '_PyEval_SliceIndexNotNone'
|
||||
self.default_type = int # type: ignore[assignment]
|
||||
self.nullable = False
|
||||
elif accept == {int, NoneType}:
|
||||
self.converter = '_PyEval_SliceIndex'
|
||||
@@ -558,7 +575,6 @@ class slice_index_converter(CConverter):
|
||||
class size_t_converter(BaseUnsignedIntConverter):
|
||||
type = 'size_t'
|
||||
converter = '_PyLong_Size_t_Converter'
|
||||
c_ignored_default = "0"
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'n':
|
||||
@@ -677,6 +693,7 @@ class Py_complex_converter(CConverter):
|
||||
class object_converter(CConverter):
|
||||
type = 'PyObject *'
|
||||
format_unit = 'O'
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def converter_init(
|
||||
self, *,
|
||||
@@ -696,6 +713,10 @@ class object_converter(CConverter):
|
||||
if type is not None:
|
||||
self.type = type
|
||||
|
||||
def c_default_init(self) -> None:
|
||||
default = self.default
|
||||
if default is None or isinstance(default, bool):
|
||||
self.c_default = "Py_" + repr(default)
|
||||
|
||||
#
|
||||
# We define three conventions for buffer types in the 'accept' argument:
|
||||
@@ -725,8 +746,9 @@ str_converter_argument_map: dict[StrConverterKeyType, str] = {}
|
||||
|
||||
class str_converter(CConverter):
|
||||
type = 'const char *'
|
||||
default_type = (str, Null, NoneType)
|
||||
default_type = (str, bytes, NullType, NoneType)
|
||||
format_unit = 's'
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def converter_init(
|
||||
self,
|
||||
@@ -744,14 +766,16 @@ class str_converter(CConverter):
|
||||
self.format_unit = format_unit
|
||||
self.length = bool(zeroes)
|
||||
if encoding:
|
||||
if self.default not in (Null, None, unspecified):
|
||||
if self.default not in (NULL, None, unspecified):
|
||||
fail("str_converter: Argument Clinic doesn't support default values for encoded strings")
|
||||
self.encoding = encoding
|
||||
self.type = 'char *'
|
||||
# sorry, clinic can't support preallocated buffers
|
||||
# for es# and et#
|
||||
self.c_default = "NULL"
|
||||
if NoneType in accept and self.c_default == "Py_None":
|
||||
|
||||
def c_default_init(self) -> None:
|
||||
if self.default is None:
|
||||
self.c_default = "NULL"
|
||||
|
||||
def post_parsing(self) -> str:
|
||||
@@ -864,6 +888,7 @@ class PyBytesObject_converter(CConverter):
|
||||
type = 'PyBytesObject *'
|
||||
format_unit = 'S'
|
||||
# accept = {bytes}
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'S':
|
||||
@@ -884,6 +909,7 @@ class PyByteArrayObject_converter(CConverter):
|
||||
type = 'PyByteArrayObject *'
|
||||
format_unit = 'Y'
|
||||
# accept = {bytearray}
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'Y':
|
||||
@@ -902,8 +928,9 @@ class PyByteArrayObject_converter(CConverter):
|
||||
|
||||
class unicode_converter(CConverter):
|
||||
type = 'PyObject *'
|
||||
default_type = (str, Null, NoneType)
|
||||
default_type = (str, NullType, NoneType)
|
||||
format_unit = 'U'
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
if self.format_unit == 'U':
|
||||
@@ -922,11 +949,11 @@ class unicode_converter(CConverter):
|
||||
|
||||
class _unicode_fs_converter_base(CConverter):
|
||||
type = 'PyObject *'
|
||||
default_type = NullType
|
||||
c_init_default = 'NULL'
|
||||
|
||||
def converter_init(self) -> None:
|
||||
if self.default is not unspecified:
|
||||
fail(f"{self.__class__.__name__} does not support default values")
|
||||
self.c_default = 'NULL'
|
||||
def c_default_init(self) -> None:
|
||||
fail(f"{self.__class__.__name__} does not support default values")
|
||||
|
||||
def cleanup(self) -> str:
|
||||
return f"Py_XDECREF({self.parser_name});"
|
||||
@@ -946,7 +973,8 @@ class unicode_fs_decoded_converter(_unicode_fs_converter_base):
|
||||
@add_legacy_c_converter('Z#', accept={str, NoneType}, zeroes=True)
|
||||
class Py_UNICODE_converter(CConverter):
|
||||
type = 'const wchar_t *'
|
||||
default_type = (str, Null, NoneType)
|
||||
default_type = (str, NullType, NoneType)
|
||||
c_ignored_default = 'NULL'
|
||||
|
||||
def converter_init(
|
||||
self, *,
|
||||
@@ -962,6 +990,7 @@ class Py_UNICODE_converter(CConverter):
|
||||
self.accept = accept
|
||||
if accept == {str}:
|
||||
self.converter = '_PyUnicode_WideCharString_Converter'
|
||||
self.default_type = (str, NullType) # type: ignore[assignment]
|
||||
elif accept == {str, NoneType}:
|
||||
self.converter = '_PyUnicode_WideCharString_Opt_Converter'
|
||||
else:
|
||||
@@ -1017,28 +1046,34 @@ class Py_UNICODE_converter(CConverter):
|
||||
@add_legacy_c_converter('w*', accept={rwbuffer})
|
||||
class Py_buffer_converter(CConverter):
|
||||
type = 'Py_buffer'
|
||||
default_type = (str, bytes, NullType, NoneType)
|
||||
format_unit = 'y*'
|
||||
impl_by_reference = True
|
||||
c_ignored_default = "{NULL, NULL}"
|
||||
c_init_default = "{NULL, NULL}"
|
||||
|
||||
def converter_init(self, *, accept: TypeSet = {buffer}) -> None:
|
||||
if self.default not in (unspecified, None):
|
||||
fail("The only legal default value for Py_buffer is None.")
|
||||
|
||||
self.c_default = self.c_ignored_default
|
||||
|
||||
if accept == {str, buffer, NoneType}:
|
||||
format_unit = 'z*'
|
||||
self.format_unit = 'z*'
|
||||
self.default_type = (str, bytes, NullType, NoneType)
|
||||
elif accept == {str, buffer}:
|
||||
format_unit = 's*'
|
||||
self.format_unit = 's*'
|
||||
self.default_type = (str, bytes, NullType) # type: ignore[assignment]
|
||||
elif accept == {buffer}:
|
||||
format_unit = 'y*'
|
||||
self.format_unit = 'y*'
|
||||
self.default_type = (bytes, NullType) # type: ignore[assignment]
|
||||
elif accept == {rwbuffer}:
|
||||
format_unit = 'w*'
|
||||
self.format_unit = 'w*'
|
||||
self.default_type = NullType # type: ignore[assignment]
|
||||
else:
|
||||
fail("Py_buffer_converter: illegal combination of arguments")
|
||||
|
||||
self.format_unit = format_unit
|
||||
def c_default_init(self) -> None:
|
||||
default = self.default
|
||||
if isinstance(default, bytes):
|
||||
self.c_default = f'{{.buf = {c_bytes_repr(default)}, .obj = NULL, .len = {len(default)}}}'
|
||||
elif isinstance(default, str):
|
||||
default = default.encode()
|
||||
self.c_default = f'{{.buf = {c_bytes_repr(default)}, .obj = NULL, .len = {len(default)}}}'
|
||||
|
||||
def cleanup(self) -> str:
|
||||
name = self.name
|
||||
@@ -1119,6 +1154,7 @@ class self_converter(CConverter):
|
||||
this is the default converter used for "self".
|
||||
"""
|
||||
type: str | None = None
|
||||
default_type = ()
|
||||
format_unit = ''
|
||||
specified_type: str | None = None
|
||||
|
||||
@@ -1233,6 +1269,7 @@ class self_converter(CConverter):
|
||||
# Converters for var-positional parameter.
|
||||
|
||||
class VarPosCConverter(CConverter):
|
||||
default_type = ()
|
||||
format_unit = ''
|
||||
|
||||
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
|
||||
@@ -1245,8 +1282,7 @@ class VarPosCConverter(CConverter):
|
||||
|
||||
class varpos_tuple_converter(VarPosCConverter):
|
||||
type = 'PyObject *'
|
||||
format_unit = ''
|
||||
c_default = 'NULL'
|
||||
c_init_default = 'NULL'
|
||||
|
||||
def cleanup(self) -> str:
|
||||
return f"""Py_XDECREF({self.parser_name});\n"""
|
||||
@@ -1304,7 +1340,6 @@ class varpos_tuple_converter(VarPosCConverter):
|
||||
class varpos_array_converter(VarPosCConverter):
|
||||
type = 'PyObject * const *'
|
||||
length = True
|
||||
c_ignored_default = ''
|
||||
|
||||
def parse_vararg(self, *, pos_only: int, min_pos: int, max_pos: int,
|
||||
fastcall: bool, limited_capi: bool) -> str:
|
||||
|
||||
@@ -7,7 +7,7 @@ import re
|
||||
import shlex
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from types import FunctionType, NoneType
|
||||
from types import FunctionType
|
||||
from typing import TYPE_CHECKING, Any, NamedTuple
|
||||
|
||||
import libclinic
|
||||
@@ -914,16 +914,17 @@ class DSLParser:
|
||||
name = 'varpos_' + name
|
||||
|
||||
value: object
|
||||
has_c_default = 'c_default' in kwargs
|
||||
if not function_args.defaults:
|
||||
if is_vararg:
|
||||
value = NULL
|
||||
else:
|
||||
if self.parameter_state is ParamState.OPTIONAL:
|
||||
fail(f"Can't have a parameter without a default ({parameter_name!r}) "
|
||||
"after a parameter with a default!")
|
||||
value = unspecified
|
||||
value = unspecified
|
||||
if (not is_vararg
|
||||
and self.parameter_state is ParamState.OPTIONAL):
|
||||
fail(f"Can't have a parameter without a default ({parameter_name!r}) "
|
||||
"after a parameter with a default!")
|
||||
if 'py_default' in kwargs:
|
||||
fail("You can't specify py_default without specifying a default value!")
|
||||
if has_c_default:
|
||||
fail("You can't specify c_default without specifying a default value!")
|
||||
else:
|
||||
expr = function_args.defaults[0]
|
||||
default = ast_input[expr.col_offset: expr.end_col_offset].strip()
|
||||
@@ -932,7 +933,7 @@ class DSLParser:
|
||||
self.parameter_state = ParamState.OPTIONAL
|
||||
bad = False
|
||||
try:
|
||||
if 'c_default' not in kwargs:
|
||||
if not has_c_default:
|
||||
# we can only represent very simple data values in C.
|
||||
# detect whether default is okay, via a denylist
|
||||
# of disallowed ast nodes.
|
||||
@@ -978,18 +979,15 @@ class DSLParser:
|
||||
fail(f"Unsupported expression as default value: {default!r}")
|
||||
|
||||
# mild hack: explicitly support NULL as a default value
|
||||
c_default: str | None
|
||||
if isinstance(expr, ast.Name) and expr.id == 'NULL':
|
||||
value = NULL
|
||||
py_default = '<unrepresentable>'
|
||||
c_default = "NULL"
|
||||
elif (isinstance(expr, ast.BinOp) or
|
||||
(isinstance(expr, ast.UnaryOp) and
|
||||
not (isinstance(expr.operand, ast.Constant) and
|
||||
type(expr.operand.value) in {int, float, complex})
|
||||
)):
|
||||
c_default = kwargs.get("c_default")
|
||||
if not (isinstance(c_default, str) and c_default):
|
||||
if not has_c_default:
|
||||
fail(f"When you specify an expression ({default!r}) "
|
||||
f"as your default value, "
|
||||
f"you MUST specify a valid c_default.",
|
||||
@@ -1008,8 +1006,7 @@ class DSLParser:
|
||||
a.append(n.id)
|
||||
py_default = ".".join(reversed(a))
|
||||
|
||||
c_default = kwargs.get("c_default")
|
||||
if not (isinstance(c_default, str) and c_default):
|
||||
if not has_c_default:
|
||||
fail(f"When you specify a named constant ({py_default!r}) "
|
||||
"as your default value, "
|
||||
"you MUST specify a valid c_default.")
|
||||
@@ -1021,23 +1018,15 @@ class DSLParser:
|
||||
else:
|
||||
value = ast.literal_eval(expr)
|
||||
py_default = repr(value)
|
||||
if isinstance(value, (bool, NoneType)):
|
||||
c_default = "Py_" + py_default
|
||||
elif isinstance(value, str):
|
||||
c_default = libclinic.c_repr(value)
|
||||
else:
|
||||
c_default = py_default
|
||||
|
||||
except (ValueError, AttributeError):
|
||||
value = unknown
|
||||
c_default = kwargs.get("c_default")
|
||||
py_default = default
|
||||
if not (isinstance(c_default, str) and c_default):
|
||||
if not has_c_default:
|
||||
fail("When you specify a named constant "
|
||||
f"({py_default!r}) as your default value, "
|
||||
"you MUST specify a valid c_default.")
|
||||
|
||||
kwargs.setdefault('c_default', c_default)
|
||||
kwargs.setdefault('py_default', py_default)
|
||||
|
||||
dict = legacy_converters if legacy else converters
|
||||
@@ -1058,12 +1047,10 @@ class DSLParser:
|
||||
|
||||
if isinstance(converter, self_converter):
|
||||
if len(self.function.parameters) == 1:
|
||||
if self.parameter_state is not ParamState.REQUIRED:
|
||||
fail("A 'self' parameter cannot be marked optional.")
|
||||
if value is not unspecified:
|
||||
fail("A 'self' parameter cannot have a default value.")
|
||||
if self.group:
|
||||
fail("A 'self' parameter cannot be in an optional group.")
|
||||
assert self.parameter_state is ParamState.REQUIRED
|
||||
assert value is unspecified
|
||||
kind = inspect.Parameter.POSITIONAL_ONLY
|
||||
self.parameter_state = ParamState.START
|
||||
self.function.parameters.clear()
|
||||
@@ -1074,14 +1061,12 @@ class DSLParser:
|
||||
if isinstance(converter, defining_class_converter):
|
||||
_lp = len(self.function.parameters)
|
||||
if _lp == 1:
|
||||
if self.parameter_state is not ParamState.REQUIRED:
|
||||
fail("A 'defining_class' parameter cannot be marked optional.")
|
||||
if value is not unspecified:
|
||||
fail("A 'defining_class' parameter cannot have a default value.")
|
||||
if self.group:
|
||||
fail("A 'defining_class' parameter cannot be in an optional group.")
|
||||
if self.function.cls is None:
|
||||
fail("A 'defining_class' parameter cannot be defined at module level.")
|
||||
assert self.parameter_state is ParamState.REQUIRED
|
||||
assert value is unspecified
|
||||
kind = inspect.Parameter.POSITIONAL_ONLY
|
||||
else:
|
||||
fail("A 'defining_class' parameter, if specified, must either "
|
||||
|
||||
@@ -39,8 +39,55 @@ def _quoted_for_c_string(text: str) -> str:
|
||||
return text
|
||||
|
||||
|
||||
def c_repr(text: str) -> str:
|
||||
return '"' + text + '"'
|
||||
# Use octals, because \x... in C has arbitrary number of hexadecimal digits.
|
||||
_c_repr = [chr(i) if 32 <= i < 127 else fr'\{i:03o}' for i in range(256)]
|
||||
_c_repr[ord('"')] = r'\"'
|
||||
_c_repr[ord('\\')] = r'\\'
|
||||
_c_repr[ord('\a')] = r'\a'
|
||||
_c_repr[ord('\b')] = r'\b'
|
||||
_c_repr[ord('\f')] = r'\f'
|
||||
_c_repr[ord('\n')] = r'\n'
|
||||
_c_repr[ord('\r')] = r'\r'
|
||||
_c_repr[ord('\t')] = r'\t'
|
||||
_c_repr[ord('\v')] = r'\v'
|
||||
|
||||
def _break_trigraphs(s: str) -> str:
|
||||
# Prevent trigraphs from being interpreted inside string literals.
|
||||
if '??' in s:
|
||||
s = s.replace('??', r'?\?')
|
||||
s = s.replace(r'\??', r'\?\?')
|
||||
# Also Argument Clinic does not like comment-like sequences
|
||||
# in string literals.
|
||||
s = s.replace(r'/*', r'/\*')
|
||||
s = s.replace(r'*/', r'*\/')
|
||||
return s
|
||||
|
||||
def c_bytes_repr(data: bytes) -> str:
|
||||
r = ''.join(_c_repr[i] for i in data)
|
||||
r = _break_trigraphs(r)
|
||||
return '"' + r + '"'
|
||||
|
||||
def c_str_repr(text: str) -> str:
|
||||
r = ''.join(_c_repr[i] if i < 0x80
|
||||
else fr'\u{i:04x}' if i < 0x10000
|
||||
else fr'\U{i:08x}'
|
||||
for i in map(ord, text))
|
||||
r = _break_trigraphs(r)
|
||||
return '"' + r + '"'
|
||||
|
||||
def c_unichar_repr(char: str) -> str:
|
||||
if char == "'":
|
||||
return r"'\''"
|
||||
if char == '"':
|
||||
return """'"'"""
|
||||
if char == '\0':
|
||||
return '0'
|
||||
i = ord(char)
|
||||
if i < 0x80:
|
||||
r = _c_repr[i]
|
||||
if not r.startswith((r'\0', r'\1')):
|
||||
return "'" + r + "'"
|
||||
return f'0x{i:02x}'
|
||||
|
||||
|
||||
def wrapped_c_string_literal(
|
||||
@@ -58,8 +105,8 @@ def wrapped_c_string_literal(
|
||||
drop_whitespace=False,
|
||||
break_on_hyphens=False,
|
||||
)
|
||||
separator = c_repr(suffix + "\n" + subsequent_indent * " ")
|
||||
return initial_indent * " " + c_repr(separator.join(wrapped))
|
||||
separator = '"' + suffix + "\n" + subsequent_indent * " " + '"'
|
||||
return initial_indent * " " + '"' + separator.join(wrapped) + '"'
|
||||
|
||||
|
||||
def _add_prefix_and_suffix(text: str, *, prefix: str = "", suffix: str = "") -> str:
|
||||
|
||||
@@ -85,9 +85,9 @@ unknown: Final = Sentinels.unknown
|
||||
|
||||
|
||||
# This one needs to be a distinct class, unlike the other two
|
||||
class Null:
|
||||
class NullType:
|
||||
def __repr__(self) -> str:
|
||||
return '<Null>'
|
||||
|
||||
|
||||
NULL = Null()
|
||||
NULL = NullType()
|
||||
|
||||
Reference in New Issue
Block a user