8000 gh-132987: Support __index__() for unsigned integers in Argument Clin… · python/cpython@6677c2c · GitHub
[go: up one dir, main page]

Skip to content

Commit 6677c2c

Browse files
gh-132987: Support __index__() for unsigned integers in Argument Clinic (GH-133011)
1 parent ed8e886 commit 6677c2c

File tree

3 files changed

+54
-150
lines changed

3 files changed

+54
-150
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Many builtin and extension functions which accept an unsigned integer
2+
argument, now use :meth:`~object.__index__` if available.

Objects/longobject.c

Lines changed: 25 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1735,100 +1735,31 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
17351735
return res;
17361736
}
17371737

1738-
int
1739-
_PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr)
1740-
{
1741-
unsigned long uval;
1742-
1743-
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
1744-
PyErr_SetString(PyExc_ValueError, "value must be positive");
1745-
return 0;
1746-
}
1747-
uval = PyLong_AsUnsignedLong(obj);
1748-
if (uval == (unsigned long)-1 && PyErr_Occurred())
1749-
return 0;
1750-
if (uval > USHRT_MAX) {
1751-
PyErr_SetString(PyExc_OverflowError,
1752-
"Python int too large for C unsigned short");
1753-
return 0;
1754-
}
1755-
1756-
*(unsigned short *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned short);
1757-
return 1;
1758-
}
1759-
1760-
int
1761-
_PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr)
1762-
{
1763-
unsigned long uval;
1764-
1765-
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
1766-
PyErr_SetString(PyExc_ValueError, "value must be positive");
1767-
return 0;
1768-
}
1769-
uval = PyLong_AsUnsignedLong(obj);
1770-
if (uval == (unsigned long)-1 && PyErr_Occurred())
1771-
return 0;
1772-
if (uval > UINT_MAX) {
1773-
PyErr_SetString(PyExc_OverflowError,
1774-
"Python int too large for C unsigned int");
1775-
return 0;
1776-
}
1777-
1778-
*(unsigned int *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned int);
1779-
return 1;
1780-
}
1781-
1782-
int
1783-
_PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr)
1784-
{
1785-
unsigned long uval;
1786-
1787-
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
1788-
PyErr_SetString(PyExc_ValueError, "value must be positive");
1789-
return 0;
1790-
}
1791-
uval = PyLong_AsUnsignedLong(obj);
1792-
if (uval == (unsigned long)-1 && PyErr_Occurred())
1793-
return 0;
1794-
1795-
*(unsigned long *)ptr = uval;
1796-
return 1;
1797-
}
1798-
1799-
int
1800-
_PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr)
1801-
{
1802-
unsigned long long uval;
1803-
1804-
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
1805-
PyErr_SetString(PyExc_ValueError, "value must be positive");
1806-
return 0;
1807-
}
1808-
uval = PyLong_AsUnsignedLongLong(obj);
1809-
if (uval == (unsigned long long)-1 && PyErr_Occurred())
1810-
return 0;
1811-
1812-
*(unsigned long long *)ptr = uval;
1813-
return 1;
1814-
}
1815-
1816-
int
1817-
_PyLong_Size_t_Converter(PyObject *obj, void *ptr)
1818-
{
1819-
size_t uval;
1820-
1821-
if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
1822-
PyErr_SetString(PyExc_ValueError, "value must be positive");
1823-
return 0;
1824-
}
1825-
uval = PyLong_AsSize_t(obj);
1826-
if (uval == (size_t)-1 && PyErr_Occurred())
1827-
return 0;
1828-
1829-
*(size_t *)ptr = uval;
1830-
return 1;
1831-
}
1738+
#define UNSIGNED_INT_CONVERTER(NAME, TYPE) \
1739+
int \
1740+
_PyLong_##NAME##_Converter(PyObject *obj, void *ptr) \
1741+
{ \
1742+
Py_ssize_t bytes = PyLong_AsNativeBytes(obj, ptr, sizeof(TYPE), \
1743+
Py_ASNATIVEBYTES_NATIVE_ENDIAN | \
1744+
Py_ASNATIVEBYTES_ALLOW_INDEX | \
1745+
Py_ASNATIVEBYTES_REJECT_NEGATIVE | \
1746+
Py_ASNATIVEBYTES_UNSIGNED_BUFFER); \
1747+
if (bytes < 0) { \
1748+
return 0; \
1749+
} \
1750+
if ((size_t)bytes > sizeof(TYPE)) { \
1751+
PyErr_SetString(PyExc_OverflowError, \
1752+
"Python int too large for C "#TYPE); \
1753+
return 0; \
1754+
} \
1755+
return 1; \
1756+
}
1757+
1758+
UNSIGNED_INT_CONVERTER(UnsignedShort, unsigned short)
1759+
UNSIGNED_INT_CONVERTER(UnsignedInt, unsigned int)
1760+
UNSIGNED_INT_CONVERTER(UnsignedLong, unsigned long)
1761+
UNSIGNED_INT_CONVERTER(UnsignedLongLong, unsigned long long)
1762+
UNSIGNED_INT_CONVERTER(Size_t, size_t)
18321763

18331764

18341765
#define CHECK_BINOP(v,w) \

Tools/clinic/libclinic/converters.py

Lines changed: 27 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,28 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
211211
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
212212

213213

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+
214236
class unsigned_short_converter(CConverter):
215237
type = 'unsigned short'
216238
default_type = int
@@ -238,22 +260,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
238260
argname=argname)
239261
if not limited_capi:
240262
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
241-
# NOTE: Raises OverflowError for negative integer.
242-
return self.format_code("""
243-
{{{{
244-
unsigned long uval = PyLong_AsUnsignedLong({argname});
245-
if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{
246-
goto exit;
247-
}}}}
248-
if (uval > USHRT_MAX) {{{{
249-
PyErr_SetString(PyExc_OverflowError,
250-
"Python int too large for C unsigned short");
251-
goto exit;
252-
}}}}
253-
{paramname} = (unsigned short) uval;
254-
}}}}
255-
""",
256-
argname=argname)
263+
return format_inline_unsigned_int_converter(self, argname)
257264

258265

259266
@add_legacy_c_converter('C', accept={str})
@@ -331,22 +338,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
331338
argname=argname)
332339
if not limited_capi:
333340
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
334-
# NOTE: Raises OverflowError for negative integer.
335-
return self.format_code("""
336-
{{{{
337-
unsigned long uval = PyLong_AsUnsignedLong({argname});
338-
if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{
339-
goto exit;
340-
}}}}
341-
if (uval > UINT_MAX) {{{{
342-
PyErr_SetString(PyExc_OverflowError,
343-
"Python int too large for C unsigned int");
344-
goto exit;
345-
}}}}
346-
{paramname} = (unsigned int) uval;
347-
}}}}
348-
""",
349-
argname=argname)
341+
return format_inline_unsigned_int_converter(self, argname)
350342

351343

352344
class long_converter(CConverter):
@@ -397,14 +389,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
397389
)
398390
if not limited_capi:
399391
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
400-
# NOTE: Raises OverflowError for negative integer.
401-
return self.format_code("""
402-
{paramname} = PyLong_AsUnsignedLong({argname});
403-
if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{
404-
goto exit;
405-
}}}}
406-
""",
407-
argname=argname)
392+
return format_inline_unsigned_int_converter(self, argname)
408393

409394

410395
class long_long_converter(CConverter):
@@ -455,14 +440,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
455440
)
456441
if not limited_capi:
457442
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
458-
# NOTE: Raises OverflowError for negative integer.
459-
return self.format_code("""
460-
{paramname} = PyLong_AsUnsignedLongLong({argname});
461-
if ({paramname} == (unsigned long long)-1 && PyErr_Occurred()) {{{{
462-
goto exit;
463-
}}}}
464-
""",
465-
argname=argname)
443+
return format_inline_unsigned_int_converter(self, argname)
466444

467445

468446
class Py_ssize_t_converter(CConverter):
@@ -599,14 +577,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
599577
argname=argname)
600578
if not limited_capi:
601579
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
602-
# NOTE: Raises OverflowError for negative integer.
603-
return self.format_code("""
604-
{paramname} = PyLong_AsSize_t({argname});
605-
if ({paramname} == (size_t)-1 && PyErr_Occurred()) {{{{
606-
goto exit;
607-
}}}}
608-
""",
609-
argname=argname)
580+
return format_inline_unsigned_int_converter(self, argname)
610581

611582

612583
class fildes_converter(CConverter):

0 commit comments

Comments
 (0)
0