Skip to content

Commit 4c914e7

Browse files
gh-133583: Add support for fixed size unsigned integers in argument parsing (GH-133584)
* Add Argument Clinic converters: uint8, uint16, uint32, uint64. * Add private C API: _PyLong_UInt8_Converter(), _PyLong_UInt16_Converter(), _PyLong_UInt32_Converter(), _PyLong_UInt64_Converter().
1 parent 3224b99 commit 4c914e7

File tree

7 files changed

+87
-93
lines changed

7 files changed

+87
-93
lines changed

Include/internal/pycore_long.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,11 @@ PyAPI_FUNC(int) _PyLong_UnsignedLongLong_Converter(PyObject *, void *);
158158
// Export for '_testclinic' shared extension (Argument Clinic code)
159159
PyAPI_FUNC(int) _PyLong_Size_t_Converter(PyObject *, void *);
160160

161+
PyAPI_FUNC(int) _PyLong_UInt8_Converter(PyObject *, void *);
162+
PyAPI_FUNC(int) _PyLong_UInt16_Converter(PyObject *, void *);
163+
PyAPI_FUNC(int) _PyLong_UInt32_Converter(PyObject *, void *);
164+
PyAPI_FUNC(int) _PyLong_UInt64_Converter(PyObject *, void *);
165+
161166
/* Long value tag bits:
162167
* 0-1: Sign bits value = (1-sign), ie. negative=2, positive=0, zero=1.
163168
* 2: Set to 1 for the small ints

Lib/test/test_clinic.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2835,6 +2835,10 @@ def test_cli_converters(self):
28352835
"size_t",
28362836
"slice_index",
28372837
"str",
2838+
"uint16",
2839+
"uint32",
2840+
"uint64",
2841+
"uint8",
28382842
"unicode",
28392843
"unsigned_char",
28402844
"unsigned_int",

Modules/_lzmamodule.c

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <lzma.h>
1919

20+
#include "pycore_long.h" // _PyLong_UInt32_Converter()
2021
// Blocks output buffer wrappers
2122
#include "pycore_blocks_output_buffer.h"
2223

@@ -223,8 +224,6 @@ FUNCNAME(PyObject *obj, void *ptr) \
223224
return 1; \
224225
}
225226

226-
INT_TYPE_CONVERTER_FUNC(uint32_t, uint32_converter)
227-
INT_TYPE_CONVERTER_FUNC(uint64_t, uint64_converter)
228227
INT_TYPE_CONVERTER_FUNC(lzma_vli, lzma_vli_converter)
229228
INT_TYPE_CONVERTER_FUNC(lzma_mode, lzma_mode_converter)
230229
INT_TYPE_CONVERTER_FUNC(lzma_match_finder, lzma_mf_converter)
@@ -254,7 +253,7 @@ parse_filter_spec_lzma(_lzma_state *state, PyObject *spec)
254253
return NULL;
255254
}
256255
if (preset_obj != NULL) {
257-
int ok = uint32_converter(preset_obj, &preset);
256+
int ok = _PyLong_UInt32_Converter(preset_obj, &preset);
258257
Py_DECREF(preset_obj);
259258
if (!ok) {
260259
return NULL;
@@ -275,14 +274,14 @@ parse_filter_spec_lzma(_lzma_state *state, PyObject *spec)
275274
if (!PyArg_ParseTupleAndKeywords(state->empty_tuple, spec,
276275
"|OOO&O&O&O&O&O&O&O&", optnames,
277276
&id, &preset_obj,
278-
uint32_converter, &options->dict_size,
279-
uint32_converter, &options->lc,
280-
uint32_converter, &options->lp,
281-
uint32_converter, &options->pb,
277+
_PyLong_UInt32_Converter, &options->dict_size,
278+
_PyLong_UInt32_Converter, &options->lc,
279+
_PyLong_UInt32_Converter, &options->lp,
280+
_PyLong_UInt32_Converter, &options->pb,
282281
lzma_mode_converter, &options->mode,
283-
uint32_converter, &options->nice_len,
282+
_PyLong_UInt32_Converter, &options->nice_len,
284283
lzma_mf_converter, &options->mf,
285-
uint32_converter, &options->depth)) {
284+
_PyLong_UInt32_Converter, &options->depth)) {
286285
PyErr_SetString(PyExc_ValueError,
287286
"Invalid filter specifier for LZMA filter");
288287
PyMem_Free(options);
@@ -301,7 +300,7 @@ parse_filter_spec_delta(_lzma_state *state, PyObject *spec)
301300
lzma_options_delta *options;
302301

303302
if (!PyArg_ParseTupleAndKeywords(state->empty_tuple, spec, "|OO&", optnames,
304-
&id, uint32_converter, &dist)) {
303+
&id, _PyLong_UInt32_Converter, &dist)) {
305304
PyErr_SetString(PyExc_ValueError,
306305
"Invalid filter specifier for delta filter");
307306
return NULL;
@@ -325,7 +324,7 @@ parse_filter_spec_bcj(_lzma_state *state, PyObject *spec)
325324
lzma_options_bcj *options;
326325

327326
if (!PyArg_ParseTupleAndKeywords(state->empty_tuple, spec, "|OO&", optnames,
328-
&id, uint32_converter, &start_offset)) {
327+
&id, _PyLong_UInt32_Converter, &start_offset)) {
329328
PyErr_SetString(PyExc_ValueError,
330329
"Invalid filter specifier for BCJ filter");
331330
return NULL;
@@ -806,7 +805,7 @@ Compressor_new(PyTypeObject *type, PyObject *args, PyObject *kwargs)
806805
return NULL;
807806
}
808807

809-
if (preset_obj != Py_None && !uint32_converter(preset_obj, &preset)) {
808+
if (preset_obj != Py_None && !_PyLong_UInt32_Converter(preset_obj, &preset)) {
810809
return NULL;
811810
}
812811

@@ -1226,7 +1225,7 @@ _lzma_LZMADecompressor_impl(PyTypeObject *type, int format,
12261225
"Cannot specify memory limit with FORMAT_RAW");
12271226
return NULL;
12281227
}
1229-
if (!uint64_converter(memlimit, &memlimit_)) {
1228+
if (!_PyLong_UInt64_Converter(memlimit, &memlimit_)) {
12301229
return NULL;
12311230
}
12321231
}

Modules/clinic/socketmodule.c.h

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

Modules/socketmodule.c

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -638,33 +638,22 @@ _PyLong_##NAME##_Converter(PyObject *obj, void *ptr) \
638638
return 1; \
639639
}
640640

641-
UNSIGNED_INT_CONVERTER(UInt16, uint16_t)
642-
UNSIGNED_INT_CONVERTER(UInt32, uint32_t)
643-
644641
#if defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
645642
# ifdef MS_WINDOWS
646643
UNSIGNED_INT_CONVERTER(NetIfindex, NET_IFINDEX)
647644
# else
648-
UNSIGNED_INT_CONVERTER(NetIfindex, unsigned int)
645+
# define _PyLong_NetIfindex_Converter _PyLong_UnsignedInt_Converter
649646
# define NET_IFINDEX unsigned int
650647
# endif
651648
#endif // defined(HAVE_IF_NAMEINDEX) || defined(MS_WINDOWS)
652649

653650
/*[python input]
654-
class uint16_converter(CConverter):
655-
type = "uint16_t"
656-
converter = '_PyLong_UInt16_Converter'
657-
658-
class uint32_converter(CConverter):
659-
type = "uint32_t"
660-
converter = '_PyLong_UInt32_Converter'
661-
662651
class NET_IFINDEX_converter(CConverter):
663652
type = "NET_IFINDEX"
664653
converter = '_PyLong_NetIfindex_Converter'
665654
666655
[python start generated code]*/
667-
/*[python end generated code: output=da39a3ee5e6b4b0d input=3de2e4a03fbf83b8]*/
656+
/*[python end generated code: output=da39a3ee5e6b4b0d input=1cf809c40a407c34]*/
668657

669658
/*[clinic input]
670659
module _socket

Objects/longobject.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1760,6 +1760,10 @@ UNSIGNED_INT_CONVERTER(UnsignedInt, unsigned int)
17601760
UNSIGNED_INT_CONVERTER(UnsignedLong, unsigned long)
17611761
UNSIGNED_INT_CONVERTER(UnsignedLongLong, unsigned long long)
17621762
UNSIGNED_INT_CONVERTER(Size_t, size_t)
1763+
UNSIGNED_INT_CONVERTER(UInt8, uint8_t)
1764+
UNSIGNED_INT_CONVERTER(UInt16, uint16_t)
1765+
UNSIGNED_INT_CONVERTER(UInt32, uint32_t)
1766+
UNSIGNED_INT_CONVERTER(UInt64, uint64_t)
17631767

17641768

17651769
#define CHECK_BINOP(v,w) \

Tools/clinic/libclinic/converters.py

Lines changed: 58 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,54 @@
1717
TypeSet = set[bltns.type[object]]
1818

1919

20+
class BaseUnsignedIntConverter(CConverter):
21+
22+
def use_converter(self) -> None:
23+
if self.converter:
24+
self.add_include('pycore_long.h',
25+
f'{self.converter}()')
26+
27+
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
28+
if not limited_capi:
29+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
30+
return self.format_code("""
31+
{{{{
32+
Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}),
33+
Py_ASNATIVEBYTES_NATIVE_ENDIAN |
34+
Py_ASNATIVEBYTES_ALLOW_INDEX |
35+
Py_ASNATIVEBYTES_REJECT_NEGATIVE |
36+
Py_ASNATIVEBYTES_UNSIGNED_BUFFER);
37+
if (_bytes < 0) {{{{
38+
goto exit;
39+
}}}}
40+
if ((size_t)_bytes > sizeof({type})) {{{{
41+
PyErr_SetString(PyExc_OverflowError,
42+
"Python int too large for C {type}");
43+
goto exit;
44+
}}}}
45+
}}}}
46+
""",
47+
argname=argname,
48+
type=self.type)
49+
50+
51+
class uint8_converter(BaseUnsignedIntConverter):
52+
type = "uint8_t"
53+
converter = '_PyLong_UInt8_Converter'
54+
55+
class uint16_converter(BaseUnsignedIntConverter):
56+
type = "uint16_t"
57+
converter = '_PyLong_UInt16_Converter'
58+
59+
class uint32_converter(BaseUnsignedIntConverter):
60+
type = "uint32_t"
61+
converter = '_PyLong_UInt32_Converter'
62+
63+
class uint64_converter(BaseUnsignedIntConverter):
64+
type = "uint64_t"
65+
converter = '_PyLong_UInt64_Converter'
66+
67+
2068
class bool_converter(CConverter):
2169
type = 'int'
2270
default_type = bool
@@ -211,29 +259,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
211259
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
212260

213261

214-
def format_inline_unsigned_int_converter(self: CConverter, argname: str) -> str:
215-
return self.format_code("""
216-
{{{{
217-
Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}),
218-
Py_ASNATIVEBYTES_NATIVE_ENDIAN |
219-
Py_ASNATIVEBYTES_ALLOW_INDEX |
220-
Py_ASNATIVEBYTES_REJECT_NEGATIVE |
221-
Py_ASNATIVEBYTES_UNSIGNED_BUFFER);
222-
if (_bytes < 0) {{{{
223-
goto exit;
224-
}}}}
225-
if ((size_t)_bytes > sizeof({type})) {{{{
226-
PyErr_SetString(PyExc_OverflowError,
227-
"Python int too large for C {type}");
228-
goto exit;
229-
}}}}
230-
}}}}
231-
""",
232-
argname=argname,
233-
type=self.type)
234-
235-
236-
class unsigned_short_converter(CConverter):
262+
class unsigned_short_converter(BaseUnsignedIntConverter):
237263
type = 'unsigned short'
238264
default_type = int
239265
c_ignored_default = "0"
@@ -244,11 +270,6 @@ def converter_init(self, *, bitwise: bool = False) -> None:
244270
else:
245271
self.converter = '_PyLong_UnsignedShort_Converter'
246272

247-
def use_converter(self) -> None:
248-
if self.converter == '_PyLong_UnsignedShort_Converter':
249-
self.add_include('pycore_long.h',
250-
'_PyLong_UnsignedShort_Converter()')
251-
252273
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
253274
if self.format_unit == 'H':
254275
return self.format_code("""
@@ -258,9 +279,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
258279
}}}}
259280
""",
260281
argname=argname)
261-
if not limited_capi:
262-
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
263-
return format_inline_unsigned_int_converter(self, argname)
282+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
264283

265284

266285
@add_legacy_c_converter('C', accept={str})
@@ -311,7 +330,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
311330
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
312331

313332

314-
class unsigned_int_converter(CConverter):
333+
class unsigned_int_converter(BaseUnsignedIntConverter):
315334
type = 'unsigned int'
316335
default_type = int
317336
c_ignored_default = "0"
@@ -322,11 +341,6 @@ def converter_init(self, *, bitwise: bool = False) -> None:
322341
else:
323342
self.converter = '_PyLong_UnsignedInt_Converter'
324343

325-
def use_converter(self) -> None:
326-
if self.converter == '_PyLong_UnsignedInt_Converter':
327-
self.add_include('pycore_long.h',
328-
'_PyLong_UnsignedInt_Converter()')
329-
330344
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
331345
if self.format_unit == 'I':
332346
return self.format_code("""
@@ -336,9 +350,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
336350
}}}}
337351
""",
338352
argname=argname)
339-
if not limited_capi:
340-
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
341-
return format_inline_unsigned_int_converter(self, argname)
353+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
342354

343355

344356
class long_converter(CConverter):
@@ -359,7 +371,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
359371
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
360372

361373

362-
class unsigned_long_converter(CConverter):
374+
class unsigned_long_converter(BaseUnsignedIntConverter):
363375
type = 'unsigned long'
364376
default_type = int
365377
c_ignored_default = "0"
@@ -370,11 +382,6 @@ def converter_init(self, *, bitwise: bool = False) -> None:
370382
else:
371383
self.converter = '_PyLong_UnsignedLong_Converter'
372384

373-
def use_converter(self) -> None:
374-
if self.converter == '_PyLong_UnsignedLong_Converter':
375-
self.add_include('pycore_long.h',
376-
'_PyLong_UnsignedLong_Converter()')
377-
378385
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
379386
if self.format_unit == 'k':
380387
return self.format_code("""
@@ -387,9 +394,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
387394
argname=argname,
388395
bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi),
389396
)
390-
if not limited_capi:
391-
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
392-
return format_inline_unsigned_int_converter(self, argname)
397+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
393398

394399

395400
class long_long_converter(CConverter):
@@ -410,7 +415,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
410415
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
411416

412417

413-
class unsigned_long_long_converter(CConverter):
418+
class unsigned_long_long_converter(BaseUnsignedIntConverter):
414419
type = 'unsigned long long'
415420
default_type = int
416421
c_ignored_default = "0"
@@ -421,11 +426,6 @@ def converter_init(self, *, bitwise: bool = False) -> None:
421426
else:
422427
self.converter = '_PyLong_UnsignedLongLong_Converter'
423428

424-
def use_converter(self) -> None:
425-
if self.converter == '_PyLong_UnsignedLongLong_Converter':
426-
self.add_include('pycore_long.h',
427-
'_PyLong_UnsignedLongLong_Converter()')
428-
429429
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
430430
if self.format_unit == 'K':
431431
return self.format_code("""
@@ -438,9 +438,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
438438
argname=argname,
439439
bad_argument=self.bad_argument(displayname, 'int', limited_capi=limited_capi),
440440
)
441-
if not limited_capi:
442-
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
443-
return format_inline_unsigned_int_converter(self, argname)
441+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
444442

445443

446444
class Py_ssize_t_converter(CConverter):
@@ -557,15 +555,11 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
557555
argname=argname)
558556

559557

560-
class size_t_converter(CConverter):
558+
class size_t_converter(BaseUnsignedIntConverter):
561559
type = 'size_t'
562560
converter = '_PyLong_Size_t_Converter'
563561
c_ignored_default = "0"
564562

565-
def use_converter(self) -> None:
566-
self.add_include('pycore_long.h',
567-
'_PyLong_Size_t_Converter()')
568-
569563
def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> str | None:
570564
if self.format_unit == 'n':
571565
return self.format_code("""
@@ -575,9 +569,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
575569
}}}}
576570
""",
577571
argname=argname)
578-
if not limited_capi:
579-
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
580-
return format_inline_unsigned_int_converter(self, argname)
572+
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
581573

582574

583575
class fildes_converter(CConverter):

0 commit comments

Comments
 (0)