Skip to content

gh-132987: Support __index__() for unsigned integers in Argument Clinic #133011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Many builtin and extension functions which accept an unsigned integer
argument, now use :meth:`~object.__index__` if available.
119 changes: 25 additions & 94 deletions Objects/longobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1735,100 +1735,31 @@ PyLong_AsLongLongAndOverflow(PyObject *vv, int *overflow)
return res;
}

int
_PyLong_UnsignedShort_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;

if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
uval = PyLong_AsUnsignedLong(obj);
if (uval == (unsigned long)-1 && PyErr_Occurred())
return 0;
if (uval > USHRT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large for C unsigned short");
return 0;
}

*(unsigned short *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned short);
return 1;
}

int
_PyLong_UnsignedInt_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;

if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
uval = PyLong_AsUnsignedLong(obj);
if (uval == (unsigned long)-1 && PyErr_Occurred())
return 0;
if (uval > UINT_MAX) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large for C unsigned int");
return 0;
}

*(unsigned int *)ptr = Py_SAFE_DOWNCAST(uval, unsigned long, unsigned int);
return 1;
}

int
_PyLong_UnsignedLong_Converter(PyObject *obj, void *ptr)
{
unsigned long uval;

if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
uval = PyLong_AsUnsignedLong(obj);
if (uval == (unsigned long)-1 && PyErr_Occurred())
return 0;

*(unsigned long *)ptr = uval;
return 1;
}

int
_PyLong_UnsignedLongLong_Converter(PyObject *obj, void *ptr)
{
unsigned long long uval;

if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
uval = PyLong_AsUnsignedLongLong(obj);
if (uval == (unsigned long long)-1 && PyErr_Occurred())
return 0;

*(unsigned long long *)ptr = uval;
return 1;
}

int
_PyLong_Size_t_Converter(PyObject *obj, void *ptr)
{
size_t uval;

if (PyLong_Check(obj) && _PyLong_IsNegative((PyLongObject *)obj)) {
PyErr_SetString(PyExc_ValueError, "value must be positive");
return 0;
}
uval = PyLong_AsSize_t(obj);
if (uval == (size_t)-1 && PyErr_Occurred())
return 0;

*(size_t *)ptr = uval;
return 1;
}
#define UNSIGNED_INT_CONVERTER(NAME, TYPE) \
int \
_PyLong_##NAME##_Converter(PyObject *obj, void *ptr) \
{ \
Py_ssize_t bytes = PyLong_AsNativeBytes(obj, ptr, sizeof(TYPE), \
Py_ASNATIVEBYTES_NATIVE_ENDIAN | \
Py_ASNATIVEBYTES_ALLOW_INDEX | \
Py_ASNATIVEBYTES_REJECT_NEGATIVE | \
Py_ASNATIVEBYTES_UNSIGNED_BUFFER); \
if (bytes < 0) { \
return 0; \
} \
if ((size_t)bytes > sizeof(TYPE)) { \
PyErr_SetString(PyExc_OverflowError, \
"Python int too large for C "#TYPE); \
return 0; \
} \
return 1; \
}

UNSIGNED_INT_CONVERTER(UnsignedShort, unsigned short)
UNSIGNED_INT_CONVERTER(UnsignedInt, unsigned int)
UNSIGNED_INT_CONVERTER(UnsignedLong, unsigned long)
UNSIGNED_INT_CONVERTER(UnsignedLongLong, unsigned long long)
UNSIGNED_INT_CONVERTER(Size_t, size_t)


#define CHECK_BINOP(v,w) \
Expand Down
83 changes: 27 additions & 56 deletions Tools/clinic/libclinic/converters.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,28 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
return super().parse_arg(argname, displayname, limited_capi=limited_capi)


def format_inline_unsigned_int_converter(self: CConverter, argname: str) -> str:
return self.format_code("""
{{{{
Py_ssize_t _bytes = PyLong_AsNativeBytes({argname}, &{paramname}, sizeof({type}),
Py_ASNATIVEBYTES_NATIVE_ENDIAN |
Py_ASNATIVEBYTES_ALLOW_INDEX |
Py_ASNATIVEBYTES_REJECT_NEGATIVE |
Py_ASNATIVEBYTES_UNSIGNED_BUFFER);
if (_bytes < 0) {{{{
goto exit;
}}}}
if ((size_t)_bytes > sizeof({type})) {{{{
PyErr_SetString(PyExc_OverflowError,
"Python int too large for C {type}");
goto exit;
}}}}
}}}}
""",
argname=argname,
type=self.type)


class unsigned_short_converter(CConverter):
type = 'unsigned short'
default_type = int
Expand Down Expand Up @@ -238,22 +260,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
argname=argname)
if not limited_capi:
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
# NOTE: Raises OverflowError for negative integer.
return self.format_code("""
{{{{
unsigned long uval = PyLong_AsUnsignedLong({argname});
if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{
goto exit;
}}}}
if (uval > USHRT_MAX) {{{{
PyErr_SetString(PyExc_OverflowError,
"Python int too large for C unsigned short");
goto exit;
}}}}
{paramname} = (unsigned short) uval;
}}}}
""",
argname=argname)
return format_inline_unsigned_int_converter(self, argname)


@add_legacy_c_converter('C', accept={str})
Expand Down Expand Up @@ -331,22 +338,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
argname=argname)
if not limited_capi:
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
# NOTE: Raises OverflowError for negative integer.
return self.format_code("""
{{{{
unsigned long uval = PyLong_AsUnsignedLong({argname});
if (uval == (unsigned long)-1 && PyErr_Occurred()) {{{{
goto exit;
}}}}
if (uval > UINT_MAX) {{{{
PyErr_SetString(PyExc_OverflowError,
"Python int too large for C unsigned int");
goto exit;
}}}}
{paramname} = (unsigned int) uval;
}}}}
""",
argname=argname)
return format_inline_unsigned_int_converter(self, argname)


class long_converter(CConverter):
Expand Down Expand Up @@ -397,14 +389,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
)
if not limited_capi:
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
# NOTE: Raises OverflowError for negative integer.
return self.format_code("""
{paramname} = PyLong_AsUnsignedLong({argname});
if ({paramname} == (unsigned long)-1 && PyErr_Occurred()) {{{{
goto exit;
}}}}
""",
argname=argname)
return format_inline_unsigned_int_converter(self, argname)


class long_long_converter(CConverter):
Expand Down Expand Up @@ -455,14 +440,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
)
if not limited_capi:
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
# NOTE: Raises OverflowError for negative integer.
return self.format_code("""
{paramname} = PyLong_AsUnsignedLongLong({argname});
if ({paramname} == (unsigned long long)-1 && PyErr_Occurred()) {{{{
goto exit;
}}}}
""",
argname=argname)
return format_inline_unsigned_int_converter(self, argname)


class Py_ssize_t_converter(CConverter):
Expand Down Expand Up @@ -599,14 +577,7 @@ def parse_arg(self, argname: str, displayname: str, *, limited_capi: bool) -> st
argname=argname)
if not limited_capi:
return super().parse_arg(argname, displayname, limited_capi=limited_capi)
# NOTE: Raises OverflowError for negative integer.
return self.format_code("""
{paramname} = PyLong_AsSize_t({argname});
if ({paramname} == (size_t)-1 && PyErr_Occurred()) {{{{
goto exit;
}}}}
""",
argname=argname)
return format_inline_unsigned_int_converter(self, argname)


class fildes_converter(CConverter):
Expand Down
Loading