gh-108511: Add C API functions which do not silently ignore errors (GH-109025)

Add the following functions:

* PyObject_HasAttrWithError()
* PyObject_HasAttrStringWithError()
* PyMapping_HasKeyWithError()
* PyMapping_HasKeyStringWithError()
This commit is contained in:
Serhiy Storchaka 2023-09-17 14:23:31 +03:00 committed by GitHub
parent e57ecf6bbc
commit add16f1a5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 330 additions and 111 deletions

View File

@ -76,6 +76,24 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and
rather than a :c:expr:`PyObject*`. rather than a :c:expr:`PyObject*`.
.. c:function:: int PyMapping_HasKeyWithError(PyObject *o, PyObject *key)
Return ``1`` if the mapping object has the key *key* and ``0`` otherwise.
This is equivalent to the Python expression ``key in o``.
On failure, return ``-1``.
.. versionadded:: 3.13
.. c:function:: int PyMapping_HasKeyStringWithError(PyObject *o, const char *key)
This is the same as :c:func:`PyMapping_HasKeyWithError`, but *key* is
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
rather than a :c:expr:`PyObject*`.
.. versionadded:: 3.13
.. c:function:: int PyMapping_HasKey(PyObject *o, PyObject *key) .. c:function:: int PyMapping_HasKey(PyObject *o, PyObject *key)
Return ``1`` if the mapping object has the key *key* and ``0`` otherwise. Return ``1`` if the mapping object has the key *key* and ``0`` otherwise.
@ -86,8 +104,8 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and
Exceptions which occur when this calls :meth:`~object.__getitem__` Exceptions which occur when this calls :meth:`~object.__getitem__`
method are silently ignored. method are silently ignored.
For proper error handling, use :c:func:`PyMapping_GetOptionalItem` or For proper error handling, use :c:func:`PyMapping_HasKeyWithError`,
:c:func:`PyObject_GetItem()` instead. :c:func:`PyMapping_GetOptionalItem` or :c:func:`PyObject_GetItem()` instead.
.. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key) .. c:function:: int PyMapping_HasKeyString(PyObject *o, const char *key)
@ -101,7 +119,8 @@ See also :c:func:`PyObject_GetItem`, :c:func:`PyObject_SetItem` and
Exceptions that occur when this calls :meth:`~object.__getitem__` Exceptions that occur when this calls :meth:`~object.__getitem__`
method or while creating the temporary :class:`str` method or while creating the temporary :class:`str`
object are silently ignored. object are silently ignored.
For proper error handling, use :c:func:`PyMapping_GetOptionalItemString` or For proper error handling, use :c:func:`PyMapping_HasKeyStringWithError`,
:c:func:`PyMapping_GetOptionalItemString` or
:c:func:`PyMapping_GetItemString` instead. :c:func:`PyMapping_GetItemString` instead.

View File

@ -27,6 +27,24 @@ Object Protocol
instead of the :func:`repr`. instead of the :func:`repr`.
.. c:function:: int PyObject_HasAttrWithError(PyObject *o, const char *attr_name)
Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise.
This is equivalent to the Python expression ``hasattr(o, attr_name)``.
On failure, return ``-1``.
.. versionadded:: 3.13
.. c:function:: int PyObject_HasAttrStringWithError(PyObject *o, const char *attr_name)
This is the same as :c:func:`PyObject_HasAttrWithError`, but *attr_name* is
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
rather than a :c:expr:`PyObject*`.
.. versionadded:: 3.13
.. c:function:: int PyObject_HasAttr(PyObject *o, PyObject *attr_name) .. c:function:: int PyObject_HasAttr(PyObject *o, PyObject *attr_name)
Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise. This Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise. This
@ -37,8 +55,8 @@ Object Protocol
Exceptions that occur when this calls :meth:`~object.__getattr__` and Exceptions that occur when this calls :meth:`~object.__getattr__` and
:meth:`~object.__getattribute__` methods are silently ignored. :meth:`~object.__getattribute__` methods are silently ignored.
For proper error handling, use :c:func:`PyObject_GetOptionalAttr` or For proper error handling, use :c:func:`PyObject_HasAttrWithError`,
:c:func:`PyObject_GetAttr` instead. :c:func:`PyObject_GetOptionalAttr` or :c:func:`PyObject_GetAttr` instead.
.. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name) .. c:function:: int PyObject_HasAttrString(PyObject *o, const char *attr_name)
@ -52,7 +70,8 @@ Object Protocol
Exceptions that occur when this calls :meth:`~object.__getattr__` and Exceptions that occur when this calls :meth:`~object.__getattr__` and
:meth:`~object.__getattribute__` methods or while creating the temporary :meth:`~object.__getattribute__` methods or while creating the temporary
:class:`str` object are silently ignored. :class:`str` object are silently ignored.
For proper error handling, use :c:func:`PyObject_GetOptionalAttrString` For proper error handling, use :c:func:`PyObject_HasAttrStringWithError`,
:c:func:`PyObject_GetOptionalAttrString`
or :c:func:`PyObject_GetAttrString` instead. or :c:func:`PyObject_GetAttrString` instead.

View File

@ -377,6 +377,8 @@ function,PyMapping_GetOptionalItem,3.13,,
function,PyMapping_GetOptionalItemString,3.13,, function,PyMapping_GetOptionalItemString,3.13,,
function,PyMapping_HasKey,3.2,, function,PyMapping_HasKey,3.2,,
function,PyMapping_HasKeyString,3.2,, function,PyMapping_HasKeyString,3.2,,
function,PyMapping_HasKeyStringWithError,3.13,,
function,PyMapping_HasKeyWithError,3.13,,
function,PyMapping_Items,3.2,, function,PyMapping_Items,3.2,,
function,PyMapping_Keys,3.2,, function,PyMapping_Keys,3.2,,
function,PyMapping_Length,3.2,, function,PyMapping_Length,3.2,,
@ -523,6 +525,8 @@ function,PyObject_GetOptionalAttrString,3.13,,
function,PyObject_GetTypeData,3.12,, function,PyObject_GetTypeData,3.12,,
function,PyObject_HasAttr,3.2,, function,PyObject_HasAttr,3.2,,
function,PyObject_HasAttrString,3.2,, function,PyObject_HasAttrString,3.2,,
function,PyObject_HasAttrStringWithError,3.13,,
function,PyObject_HasAttrWithError,3.13,,
function,PyObject_Hash,3.2,, function,PyObject_Hash,3.2,,
function,PyObject_HashNotImplemented,3.2,, function,PyObject_HashNotImplemented,3.2,,
function,PyObject_Init,3.2,, function,PyObject_Init,3.2,,

View File

@ -926,6 +926,18 @@ New Features
be treated as a failure. be treated as a failure.
(Contributed by Serhiy Storchaka in :gh:`106307`.) (Contributed by Serhiy Storchaka in :gh:`106307`.)
* Add fixed variants of functions which silently ignore errors:
- :c:func:`PyObject_HasAttrWithError` replaces :c:func:`PyObject_HasAttr`.
- :c:func:`PyObject_HasAttrStringWithError` replaces :c:func:`PyObject_HasAttrString`.
- :c:func:`PyMapping_HasKeyWithError` replaces :c:func:`PyMapping_HasKey`.
- :c:func:`PyMapping_HasKeyStringWithError` replaces :c:func:`PyMapping_HasKeyString`.
New functions return not only ``1`` for true and ``0`` for false, but also
``-1`` for error.
(Contributed by Serhiy Storchaka in :gh:`108511`.)
* If Python is built in :ref:`debug mode <debug-build>` or :option:`with * If Python is built in :ref:`debug mode <debug-build>` or :option:`with
assertions <--with-assertions>`, :c:func:`PyTuple_SET_ITEM` and assertions <--with-assertions>`, :c:func:`PyTuple_SET_ITEM` and
:c:func:`PyList_SET_ITEM` now check the index argument with an assertion. :c:func:`PyList_SET_ITEM` now check the index argument with an assertion.

View File

@ -50,6 +50,25 @@ extern "C" {
This function always succeeds. */ This function always succeeds. */
/* Implemented elsewhere:
int PyObject_HasAttrStringWithError(PyObject *o, const char *attr_name);
Returns 1 if object 'o' has the attribute attr_name, and 0 otherwise.
This is equivalent to the Python expression: hasattr(o,attr_name).
Returns -1 on failure. */
/* Implemented elsewhere:
int PyObject_HasAttrWithError(PyObject *o, PyObject *attr_name);
Returns 1 if o has the attribute attr_name, and 0 otherwise.
This is equivalent to the Python expression: hasattr(o,attr_name).
Returns -1 on failure. */
/* Implemented elsewhere: /* Implemented elsewhere:
PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name); PyObject* PyObject_GetAttr(PyObject *o, PyObject *attr_name);
@ -821,6 +840,18 @@ PyAPI_FUNC(int) PyMapping_HasKeyString(PyObject *o, const char *key);
This function always succeeds. */ This function always succeeds. */
PyAPI_FUNC(int) PyMapping_HasKey(PyObject *o, PyObject *key); PyAPI_FUNC(int) PyMapping_HasKey(PyObject *o, PyObject *key);
/* Return 1 if the mapping object has the key 'key', and 0 otherwise.
This is equivalent to the Python expression: key in o.
On failure, return -1. */
PyAPI_FUNC(int) PyMapping_HasKeyWithError(PyObject *o, PyObject *key);
/* Return 1 if the mapping object has the key 'key', and 0 otherwise.
This is equivalent to the Python expression: key in o.
On failure, return -1. */
PyAPI_FUNC(int) PyMapping_HasKeyStringWithError(PyObject *o, const char *key);
/* On success, return a list or tuple of the keys in mapping object 'o'. /* On success, return a list or tuple of the keys in mapping object 'o'.
On failure, return NULL. */ On failure, return NULL. */
PyAPI_FUNC(PyObject *) PyMapping_Keys(PyObject *o); PyAPI_FUNC(PyObject *) PyMapping_Keys(PyObject *o);

View File

@ -394,6 +394,10 @@ PyAPI_FUNC(int) PyObject_GetOptionalAttrString(PyObject *, const char *, PyObjec
PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_DelAttr(PyObject *v, PyObject *name); PyAPI_FUNC(int) PyObject_DelAttr(PyObject *v, PyObject *name);
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
#if !defined(Py_LIMITED_API) || Py_LIMITED_API+0 >= 0x030d0000
PyAPI_FUNC(int) PyObject_HasAttrWithError(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttrStringWithError(PyObject *, const char *);
#endif
PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *); PyAPI_FUNC(PyObject *) PyObject_SelfIter(PyObject *);
PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *); PyAPI_FUNC(PyObject *) PyObject_GenericGetAttr(PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *); PyAPI_FUNC(int) PyObject_GenericSetAttr(PyObject *, PyObject *, PyObject *);

View File

@ -129,6 +129,34 @@ class CAPITest(unittest.TestCase):
# CRASHES hasattrstring(obj, NULL) # CRASHES hasattrstring(obj, NULL)
# CRASHES hasattrstring(NULL, b'a') # CRASHES hasattrstring(NULL, b'a')
def test_object_hasattrwitherror(self):
xhasattr = _testcapi.object_hasattrwitherror
obj = TestObject()
obj.a = 1
setattr(obj, '\U0001f40d', 2)
self.assertTrue(xhasattr(obj, 'a'))
self.assertFalse(xhasattr(obj, 'b'))
self.assertTrue(xhasattr(obj, '\U0001f40d'))
self.assertRaises(RuntimeError, xhasattr, obj, 'evil')
self.assertRaises(TypeError, xhasattr, obj, 1)
# CRASHES xhasattr(obj, NULL)
# CRASHES xhasattr(NULL, 'a')
def test_object_hasattrstringwitherror(self):
hasattrstring = _testcapi.object_hasattrstringwitherror
obj = TestObject()
obj.a = 1
setattr(obj, '\U0001f40d', 2)
self.assertTrue(hasattrstring(obj, b'a'))
self.assertFalse(hasattrstring(obj, b'b'))
self.assertTrue(hasattrstring(obj, '\U0001f40d'.encode()))
self.assertRaises(RuntimeError, hasattrstring, obj, b'evil')
self.assertRaises(UnicodeDecodeError, hasattrstring, obj, b'\xff')
# CRASHES hasattrstring(obj, NULL)
# CRASHES hasattrstring(NULL, b'a')
def test_object_setattr(self): def test_object_setattr(self):
xsetattr = _testcapi.object_setattr xsetattr = _testcapi.object_setattr
obj = TestObject() obj = TestObject()
@ -339,6 +367,44 @@ class CAPITest(unittest.TestCase):
self.assertFalse(haskeystring([], b'a')) self.assertFalse(haskeystring([], b'a'))
self.assertFalse(haskeystring(NULL, b'a')) self.assertFalse(haskeystring(NULL, b'a'))
def test_mapping_haskeywitherror(self):
haskey = _testcapi.mapping_haskeywitherror
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(haskey(dct, 'a'))
self.assertFalse(haskey(dct, 'b'))
self.assertTrue(haskey(dct, '\U0001f40d'))
dct2 = ProxyGetItem(dct)
self.assertTrue(haskey(dct2, 'a'))
self.assertFalse(haskey(dct2, 'b'))
self.assertTrue(haskey(['a', 'b', 'c'], 1))
self.assertRaises(TypeError, haskey, 42, 'a')
self.assertRaises(TypeError, haskey, {}, []) # unhashable
self.assertRaises(IndexError, haskey, [], 1)
self.assertRaises(TypeError, haskey, [], 'a')
# CRASHES haskey({}, NULL))
# CRASHES haskey(NULL, 'a'))
def test_mapping_haskeystringwitherror(self):
haskeystring = _testcapi.mapping_haskeystringwitherror
dct = {'a': 1, '\U0001f40d': 2}
self.assertTrue(haskeystring(dct, b'a'))
self.assertFalse(haskeystring(dct, b'b'))
self.assertTrue(haskeystring(dct, '\U0001f40d'.encode()))
dct2 = ProxyGetItem(dct)
self.assertTrue(haskeystring(dct2, b'a'))
self.assertFalse(haskeystring(dct2, b'b'))
self.assertRaises(TypeError, haskeystring, 42, b'a')
self.assertRaises(UnicodeDecodeError, haskeystring, {}, b'\xff')
self.assertRaises(SystemError, haskeystring, {}, NULL)
self.assertRaises(TypeError, haskeystring, [], b'a')
# CRASHES haskeystring(NULL, b'a')
def test_object_setitem(self): def test_object_setitem(self):
setitem = _testcapi.object_setitem setitem = _testcapi.object_setitem
dct = {} dct = {}

View File

@ -405,6 +405,8 @@ SYMBOL_NAMES = (
"PyMapping_GetOptionalItemString", "PyMapping_GetOptionalItemString",
"PyMapping_HasKey", "PyMapping_HasKey",
"PyMapping_HasKeyString", "PyMapping_HasKeyString",
"PyMapping_HasKeyStringWithError",
"PyMapping_HasKeyWithError",
"PyMapping_Items", "PyMapping_Items",
"PyMapping_Keys", "PyMapping_Keys",
"PyMapping_Length", "PyMapping_Length",
@ -542,6 +544,8 @@ SYMBOL_NAMES = (
"PyObject_GetTypeData", "PyObject_GetTypeData",
"PyObject_HasAttr", "PyObject_HasAttr",
"PyObject_HasAttrString", "PyObject_HasAttrString",
"PyObject_HasAttrStringWithError",
"PyObject_HasAttrWithError",
"PyObject_Hash", "PyObject_Hash",
"PyObject_HashNotImplemented", "PyObject_HashNotImplemented",
"PyObject_Init", "PyObject_Init",

View File

@ -0,0 +1,4 @@
Add functions :c:func:`PyObject_HasAttrWithError`,
:c:func:`PyObject_HasAttrStringWithError`,
:c:func:`PyMapping_HasKeyWithError` and
:c:func:`PyMapping_HasKeyStringWithError`.

View File

@ -2452,3 +2452,11 @@
added = '3.13' added = '3.13'
[function.PyLong_AsInt] [function.PyLong_AsInt]
added = '3.13' added = '3.13'
[function.PyObject_HasAttrWithError]
added = '3.13'
[function.PyObject_HasAttrStringWithError]
added = '3.13'
[function.PyMapping_HasKeyWithError]
added = '3.13'
[function.PyMapping_HasKeyStringWithError]
added = '3.13'

View File

@ -386,11 +386,11 @@ PyCStructUnionType_update_stgdict(PyObject *type, PyObject *fields, int isStruct
if (fields == NULL) if (fields == NULL)
return 0; return 0;
if (PyObject_GetOptionalAttr(type, &_Py_ID(_swappedbytes_), &tmp) < 0) { int rc = PyObject_HasAttrWithError(type, &_Py_ID(_swappedbytes_));
if (rc < 0) {
return -1; return -1;
} }
if (tmp) { if (rc) {
Py_DECREF(tmp);
big_endian = !PY_BIG_ENDIAN; big_endian = !PY_BIG_ENDIAN;
} }
else { else {

View File

@ -3532,12 +3532,11 @@ expat_start_doctype_handler(XMLParserObject *self,
sysid_obj, NULL); sysid_obj, NULL);
Py_XDECREF(res); Py_XDECREF(res);
} }
else if (PyObject_GetOptionalAttr((PyObject *)self, st->str_doctype, &res) > 0) { else if (PyObject_HasAttrWithError((PyObject *)self, st->str_doctype) > 0) {
(void)PyErr_WarnEx(PyExc_RuntimeWarning, (void)PyErr_WarnEx(PyExc_RuntimeWarning,
"The doctype() method of XMLParser is ignored. " "The doctype() method of XMLParser is ignored. "
"Define doctype() method on the TreeBuilder target.", "Define doctype() method on the TreeBuilder target.",
1); 1);
Py_DECREF(res);
} }
Py_DECREF(doctype_name_obj); Py_DECREF(doctype_name_obj);

View File

@ -148,13 +148,9 @@ _io__IOBase_truncate_impl(PyObject *self, PyTypeObject *cls,
static int static int
iobase_is_closed(PyObject *self) iobase_is_closed(PyObject *self)
{ {
PyObject *res;
int ret;
/* This gets the derived attribute, which is *not* __IOBase_closed /* This gets the derived attribute, which is *not* __IOBase_closed
in most cases! */ in most cases! */
ret = PyObject_GetOptionalAttr(self, &_Py_ID(__IOBase_closed), &res); return PyObject_HasAttrWithError(self, &_Py_ID(__IOBase_closed));
Py_XDECREF(res);
return ret;
} }
/* Flush and close methods */ /* Flush and close methods */

View File

@ -1223,11 +1223,10 @@ _io_TextIOWrapper___init___impl(textio *self, PyObject *buffer,
goto error; goto error;
self->seekable = self->telling = r; self->seekable = self->telling = r;
r = PyObject_GetOptionalAttr(buffer, &_Py_ID(read1), &res); r = PyObject_HasAttrWithError(buffer, &_Py_ID(read1));
if (r < 0) { if (r < 0) {
goto error; goto error;
} }
Py_XDECREF(res);
self->has_read1 = r; self->has_read1 = r;
self->encoding_start_of_stream = 0; self->encoding_start_of_stream = 0;

View File

@ -5799,14 +5799,13 @@ instantiate(PyObject *cls, PyObject *args)
into a newly created tuple. */ into a newly created tuple. */
assert(PyTuple_Check(args)); assert(PyTuple_Check(args));
if (!PyTuple_GET_SIZE(args) && PyType_Check(cls)) { if (!PyTuple_GET_SIZE(args) && PyType_Check(cls)) {
PyObject *func; int rc = PyObject_HasAttrWithError(cls, &_Py_ID(__getinitargs__));
if (PyObject_GetOptionalAttr(cls, &_Py_ID(__getinitargs__), &func) < 0) { if (rc < 0) {
return NULL; return NULL;
} }
if (func == NULL) { if (!rc) {
return PyObject_CallMethodOneArg(cls, &_Py_ID(__new__), cls); return PyObject_CallMethodOneArg(cls, &_Py_ID(__new__), cls);
} }
Py_DECREF(func);
} }
return PyObject_CallObject(cls, args); return PyObject_CallObject(cls, args);
} }

View File

@ -105,6 +105,31 @@ object_hasattrstring(PyObject *self, PyObject *args)
return PyLong_FromLong(PyObject_HasAttrString(obj, attr_name)); return PyLong_FromLong(PyObject_HasAttrString(obj, attr_name));
} }
static PyObject *
object_hasattrwitherror(PyObject *self, PyObject *args)
{
PyObject *obj, *attr_name;
if (!PyArg_ParseTuple(args, "OO", &obj, &attr_name)) {
return NULL;
}
NULLABLE(obj);
NULLABLE(attr_name);
RETURN_INT(PyObject_HasAttrWithError(obj, attr_name));
}
static PyObject *
object_hasattrstringwitherror(PyObject *self, PyObject *args)
{
PyObject *obj;
const char *attr_name;
Py_ssize_t size;
if (!PyArg_ParseTuple(args, "Oz#", &obj, &attr_name, &size)) {
return NULL;
}
NULLABLE(obj);
RETURN_INT(PyObject_HasAttrStringWithError(obj, attr_name));
}
static PyObject * static PyObject *
object_setattr(PyObject *self, PyObject *args) object_setattr(PyObject *self, PyObject *args)
{ {
@ -280,6 +305,31 @@ mapping_haskeystring(PyObject *self, PyObject *args)
return PyLong_FromLong(PyMapping_HasKeyString(mapping, key)); return PyLong_FromLong(PyMapping_HasKeyString(mapping, key));
} }
static PyObject *
mapping_haskeywitherror(PyObject *self, PyObject *args)
{
PyObject *mapping, *key;
if (!PyArg_ParseTuple(args, "OO", &mapping, &key)) {
return NULL;
}
NULLABLE(mapping);
NULLABLE(key);
RETURN_INT(PyMapping_HasKeyWithError(mapping, key));
}
static PyObject *
mapping_haskeystringwitherror(PyObject *self, PyObject *args)
{
PyObject *mapping;
const char *key;
Py_ssize_t size;
if (!PyArg_ParseTuple(args, "Oz#", &mapping, &key, &size)) {
return NULL;
}
NULLABLE(mapping);
RETURN_INT(PyMapping_HasKeyStringWithError(mapping, key));
}
static PyObject * static PyObject *
object_setitem(PyObject *self, PyObject *args) object_setitem(PyObject *self, PyObject *args)
{ {
@ -568,6 +618,8 @@ static PyMethodDef test_methods[] = {
{"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS}, {"object_getoptionalattrstring", object_getoptionalattrstring, METH_VARARGS},
{"object_hasattr", object_hasattr, METH_VARARGS}, {"object_hasattr", object_hasattr, METH_VARARGS},
{"object_hasattrstring", object_hasattrstring, METH_VARARGS}, {"object_hasattrstring", object_hasattrstring, METH_VARARGS},
{"object_hasattrwitherror", object_hasattrwitherror, METH_VARARGS},
{"object_hasattrstringwitherror", object_hasattrstringwitherror, METH_VARARGS},
{"object_setattr", object_setattr, METH_VARARGS}, {"object_setattr", object_setattr, METH_VARARGS},
{"object_setattrstring", object_setattrstring, METH_VARARGS}, {"object_setattrstring", object_setattrstring, METH_VARARGS},
{"object_delattr", object_delattr, METH_VARARGS}, {"object_delattr", object_delattr, METH_VARARGS},
@ -582,6 +634,8 @@ static PyMethodDef test_methods[] = {
{"mapping_getoptionalitemstring", mapping_getoptionalitemstring, METH_VARARGS}, {"mapping_getoptionalitemstring", mapping_getoptionalitemstring, METH_VARARGS},
{"mapping_haskey", mapping_haskey, METH_VARARGS}, {"mapping_haskey", mapping_haskey, METH_VARARGS},
{"mapping_haskeystring", mapping_haskeystring, METH_VARARGS}, {"mapping_haskeystring", mapping_haskeystring, METH_VARARGS},
{"mapping_haskeywitherror", mapping_haskeywitherror, METH_VARARGS},
{"mapping_haskeystringwitherror", mapping_haskeystringwitherror, METH_VARARGS},
{"object_setitem", object_setitem, METH_VARARGS}, {"object_setitem", object_setitem, METH_VARARGS},
{"mapping_setitemstring", mapping_setitemstring, METH_VARARGS}, {"mapping_setitemstring", mapping_setitemstring, METH_VARARGS},
{"object_delitem", object_delitem, METH_VARARGS}, {"object_delitem", object_delitem, METH_VARARGS},

View File

@ -123,7 +123,7 @@ get_module_from_type(PyTypeObject *cls)
static PyObject * static PyObject *
add_new_exception(PyObject *mod, const char *name, PyObject *base) add_new_exception(PyObject *mod, const char *name, PyObject *base)
{ {
assert(!PyObject_HasAttrString(mod, name)); assert(!PyObject_HasAttrStringWithError(mod, name));
PyObject *exctype = PyErr_NewException(name, base, NULL); PyObject *exctype = PyErr_NewException(name, base, NULL);
if (exctype == NULL) { if (exctype == NULL) {
return NULL; return NULL;

View File

@ -41,7 +41,7 @@ _get_current_interp(void)
static PyObject * static PyObject *
add_new_exception(PyObject *mod, const char *name, PyObject *base) add_new_exception(PyObject *mod, const char *name, PyObject *base)
{ {
assert(!PyObject_HasAttrString(mod, name)); assert(!PyObject_HasAttrStringWithError(mod, name));
PyObject *exctype = PyErr_NewException(name, base, NULL); PyObject *exctype = PyErr_NewException(name, base, NULL);
if (exctype == NULL) { if (exctype == NULL) {
return NULL; return NULL;

View File

@ -2426,6 +2426,24 @@ PyMapping_SetItemString(PyObject *o, const char *key, PyObject *value)
return r; return r;
} }
int
PyMapping_HasKeyStringWithError(PyObject *obj, const char *key)
{
PyObject *res;
int rc = PyMapping_GetOptionalItemString(obj, key, &res);
Py_XDECREF(res);
return rc;
}
int
PyMapping_HasKeyWithError(PyObject *obj, PyObject *key)
{
PyObject *res;
int rc = PyMapping_GetOptionalItem(obj, key, &res);
Py_XDECREF(res);
return rc;
}
int int
PyMapping_HasKeyString(PyObject *o, const char *key) PyMapping_HasKeyString(PyObject *o, const char *key)
{ {

View File

@ -2688,12 +2688,11 @@ dict_update_arg(PyObject *self, PyObject *arg)
if (PyDict_CheckExact(arg)) { if (PyDict_CheckExact(arg)) {
return PyDict_Merge(self, arg, 1); return PyDict_Merge(self, arg, 1);
} }
PyObject *func; int has_keys = PyObject_HasAttrWithError(arg, &_Py_ID(keys));
if (PyObject_GetOptionalAttr(arg, &_Py_ID(keys), &func) < 0) { if (has_keys < 0) {
return -1; return -1;
} }
if (func != NULL) { if (has_keys) {
Py_DECREF(func);
return PyDict_Merge(self, arg, 1); return PyDict_Merge(self, arg, 1);
} }
return PyDict_MergeFromSeq2(self, arg, 1); return PyDict_MergeFromSeq2(self, arg, 1);

View File

@ -55,8 +55,7 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
PyObject *qualname = NULL; PyObject *qualname = NULL;
PyObject *module = NULL; PyObject *module = NULL;
PyObject *r = NULL; PyObject *r = NULL;
PyObject *tmp; int rc;
int err;
if (p == Py_Ellipsis) { if (p == Py_Ellipsis) {
// The Ellipsis object // The Ellipsis object
@ -64,19 +63,14 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
goto done; goto done;
} }
if (PyObject_GetOptionalAttr(p, &_Py_ID(__origin__), &tmp) < 0) { if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
goto done; (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
{
// It looks like a GenericAlias
goto use_repr;
} }
if (tmp != NULL) { if (rc < 0) {
Py_DECREF(tmp); goto done;
if (PyObject_GetOptionalAttr(p, &_Py_ID(__args__), &tmp) < 0) {
goto done;
}
if (tmp != NULL) {
Py_DECREF(tmp);
// It looks like a GenericAlias
goto use_repr;
}
} }
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) { if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
@ -113,13 +107,13 @@ done:
Py_XDECREF(module); Py_XDECREF(module);
if (r == NULL) { if (r == NULL) {
// error if any of the above PyObject_Repr/PyUnicode_From* fail // error if any of the above PyObject_Repr/PyUnicode_From* fail
err = -1; rc = -1;
} }
else { else {
err = _PyUnicodeWriter_WriteStr(writer, r); rc = _PyUnicodeWriter_WriteStr(writer, r);
Py_DECREF(r); Py_DECREF(r);
} }
return err; return rc;
} }
static int static int
@ -253,18 +247,17 @@ _Py_make_parameters(PyObject *args)
Py_ssize_t iparam = 0; Py_ssize_t iparam = 0;
for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) { for (Py_ssize_t iarg = 0; iarg < nargs; iarg++) {
PyObject *t = PyTuple_GET_ITEM(args, iarg); PyObject *t = PyTuple_GET_ITEM(args, iarg);
PyObject *subst;
// We don't want __parameters__ descriptor of a bare Python class. // We don't want __parameters__ descriptor of a bare Python class.
if (PyType_Check(t)) { if (PyType_Check(t)) {
continue; continue;
} }
if (PyObject_GetOptionalAttr(t, &_Py_ID(__typing_subst__), &subst) < 0) { int rc = PyObject_HasAttrWithError(t, &_Py_ID(__typing_subst__));
if (rc < 0) {
Py_DECREF(parameters); Py_DECREF(parameters);
return NULL; return NULL;
} }
if (subst) { if (rc) {
iparam += tuple_add(parameters, iparam, t); iparam += tuple_add(parameters, iparam, t);
Py_DECREF(subst);
} }
else { else {
PyObject *subparams; PyObject *subparams;

View File

@ -911,26 +911,24 @@ PyObject_GetAttrString(PyObject *v, const char *name)
} }
int int
PyObject_HasAttrString(PyObject *v, const char *name) PyObject_HasAttrStringWithError(PyObject *obj, const char *name)
{ {
if (Py_TYPE(v)->tp_getattr != NULL) { PyObject *res;
PyObject *res = (*Py_TYPE(v)->tp_getattr)(v, (char*)name); int rc = PyObject_GetOptionalAttrString(obj, name, &res);
if (res != NULL) { Py_XDECREF(res);
Py_DECREF(res); return rc;
return 1; }
}
PyErr_Clear();
return 0;
}
PyObject *attr_name = PyUnicode_FromString(name);
if (attr_name == NULL) { int
PyObject_HasAttrString(PyObject *obj, const char *name)
{
int rc = PyObject_HasAttrStringWithError(obj, name);
if (rc < 0) {
PyErr_Clear(); PyErr_Clear();
return 0; return 0;
} }
int ok = PyObject_HasAttr(v, attr_name); return rc;
Py_DECREF(attr_name);
return ok;
} }
int int
@ -1149,18 +1147,23 @@ PyObject_GetOptionalAttrString(PyObject *obj, const char *name, PyObject **resul
} }
int int
PyObject_HasAttr(PyObject *v, PyObject *name) PyObject_HasAttrWithError(PyObject *obj, PyObject *name)
{ {
PyObject *res; PyObject *res;
if (PyObject_GetOptionalAttr(v, name, &res) < 0) { int rc = PyObject_GetOptionalAttr(obj, name, &res);
Py_XDECREF(res);
return rc;
}
int
PyObject_HasAttr(PyObject *obj, PyObject *name)
{
int rc = PyObject_HasAttrWithError(obj, name);
if (rc < 0) {
PyErr_Clear(); PyErr_Clear();
return 0; return 0;
} }
if (res == NULL) { return rc;
return 0;
}
Py_DECREF(res);
return 1;
} }
int int

View File

@ -3879,16 +3879,14 @@ type_new_get_bases(type_new_ctx *ctx, PyObject **type)
if (PyType_Check(base)) { if (PyType_Check(base)) {
continue; continue;
} }
PyObject *mro_entries; int rc = PyObject_HasAttrWithError(base, &_Py_ID(__mro_entries__));
if (PyObject_GetOptionalAttr(base, &_Py_ID(__mro_entries__), if (rc < 0) {
&mro_entries) < 0) {
return -1; return -1;
} }
if (mro_entries != NULL) { if (rc) {
PyErr_SetString(PyExc_TypeError, PyErr_SetString(PyExc_TypeError,
"type() doesn't support MRO entry resolution; " "type() doesn't support MRO entry resolution; "
"use types.new_class()"); "use types.new_class()");
Py_DECREF(mro_entries);
return -1; return -1;
} }
} }

View File

@ -186,28 +186,21 @@ union_repr_item(_PyUnicodeWriter *writer, PyObject *p)
{ {
PyObject *qualname = NULL; PyObject *qualname = NULL;
PyObject *module = NULL; PyObject *module = NULL;
PyObject *tmp;
PyObject *r = NULL; PyObject *r = NULL;
int err; int rc;
if (p == (PyObject *)&_PyNone_Type) { if (p == (PyObject *)&_PyNone_Type) {
return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4); return _PyUnicodeWriter_WriteASCIIString(writer, "None", 4);
} }
if (PyObject_GetOptionalAttr(p, &_Py_ID(__origin__), &tmp) < 0) { if ((rc = PyObject_HasAttrWithError(p, &_Py_ID(__origin__))) > 0 &&
goto exit; (rc = PyObject_HasAttrWithError(p, &_Py_ID(__args__))) > 0)
{
// It looks like a GenericAlias
goto use_repr;
} }
if (rc < 0) {
if (tmp) { goto exit;
Py_DECREF(tmp);
if (PyObject_GetOptionalAttr(p, &_Py_ID(__args__), &tmp) < 0) {
goto exit;
}
if (tmp) {
// It looks like a GenericAlias
Py_DECREF(tmp);
goto use_repr;
}
} }
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) { if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
@ -244,9 +237,9 @@ exit:
if (r == NULL) { if (r == NULL) {
return -1; return -1;
} }
err = _PyUnicodeWriter_WriteStr(writer, r); rc = _PyUnicodeWriter_WriteStr(writer, r);
Py_DECREF(r); Py_DECREF(r);
return err; return rc;
} }
static PyObject * static PyObject *

4
PC/python3dll.c generated
View File

@ -359,6 +359,8 @@ EXPORT_FUNC(PyMapping_GetOptionalItem)
EXPORT_FUNC(PyMapping_GetOptionalItemString) EXPORT_FUNC(PyMapping_GetOptionalItemString)
EXPORT_FUNC(PyMapping_HasKey) EXPORT_FUNC(PyMapping_HasKey)
EXPORT_FUNC(PyMapping_HasKeyString) EXPORT_FUNC(PyMapping_HasKeyString)
EXPORT_FUNC(PyMapping_HasKeyStringWithError)
EXPORT_FUNC(PyMapping_HasKeyWithError)
EXPORT_FUNC(PyMapping_Items) EXPORT_FUNC(PyMapping_Items)
EXPORT_FUNC(PyMapping_Keys) EXPORT_FUNC(PyMapping_Keys)
EXPORT_FUNC(PyMapping_Length) EXPORT_FUNC(PyMapping_Length)
@ -480,6 +482,8 @@ EXPORT_FUNC(PyObject_GetOptionalAttrString)
EXPORT_FUNC(PyObject_GetTypeData) EXPORT_FUNC(PyObject_GetTypeData)
EXPORT_FUNC(PyObject_HasAttr) EXPORT_FUNC(PyObject_HasAttr)
EXPORT_FUNC(PyObject_HasAttrString) EXPORT_FUNC(PyObject_HasAttrString)
EXPORT_FUNC(PyObject_HasAttrStringWithError)
EXPORT_FUNC(PyObject_HasAttrWithError)
EXPORT_FUNC(PyObject_Hash) EXPORT_FUNC(PyObject_Hash)
EXPORT_FUNC(PyObject_HashNotImplemented) EXPORT_FUNC(PyObject_HashNotImplemented)
EXPORT_FUNC(PyObject_Init) EXPORT_FUNC(PyObject_Init)

View File

@ -1776,13 +1776,11 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
} }
} }
if ((PyObject *)Py_TYPE(exc) != PyExc_SyntaxError) { if ((PyObject *)Py_TYPE(exc) != PyExc_SyntaxError) {
if (PyObject_GetOptionalAttr(exc, &_Py_ID(msg), &tmp) < 0) { int rc = PyObject_HasAttrWithError(exc, &_Py_ID(msg));
if (rc < 0) {
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }
else if (tmp) { else if (!rc) {
Py_DECREF(tmp);
}
else {
tmp = PyObject_Str(exc); tmp = PyObject_Str(exc);
if (tmp) { if (tmp) {
if (PyObject_SetAttr(exc, &_Py_ID(msg), tmp)) { if (PyObject_SetAttr(exc, &_Py_ID(msg), tmp)) {
@ -1795,13 +1793,11 @@ PyErr_SyntaxLocationObjectEx(PyObject *filename, int lineno, int col_offset,
} }
} }
if (PyObject_GetOptionalAttr(exc, &_Py_ID(print_file_and_line), &tmp) < 0) { rc = PyObject_HasAttrWithError(exc, &_Py_ID(print_file_and_line));
if (rc < 0) {
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }
else if (tmp) { else if (!rc) {
Py_DECREF(tmp);
}
else {
if (PyObject_SetAttr(exc, &_Py_ID(print_file_and_line), Py_None)) { if (PyObject_SetAttr(exc, &_Py_ID(print_file_and_line), Py_None)) {
_PyErr_Clear(tstate); _PyErr_Clear(tstate);
} }

View File

@ -2887,12 +2887,11 @@ PyImport_ImportModuleLevelObject(PyObject *name, PyObject *globals,
} }
} }
else { else {
PyObject *path; int has_path = PyObject_HasAttrWithError(mod, &_Py_ID(__path__));
if (PyObject_GetOptionalAttr(mod, &_Py_ID(__path__), &path) < 0) { if (has_path < 0) {
goto error; goto error;
} }
if (path) { if (has_path) {
Py_DECREF(path);
final_mod = PyObject_CallMethodObjArgs( final_mod = PyObject_CallMethodObjArgs(
IMPORTLIB(interp), &_Py_ID(_handle_fromlist), IMPORTLIB(interp), &_Py_ID(_handle_fromlist),
mod, fromlist, IMPORT_FUNC(interp), NULL); mod, fromlist, IMPORT_FUNC(interp), NULL);

View File

@ -245,14 +245,12 @@ get_suggestions_for_name_error(PyObject* name, PyFrameObject* frame)
goto error; goto error;
} }
PyObject *value; res = PyObject_HasAttrWithError(self, name);
res = PyObject_GetOptionalAttr(self, name, &value);
Py_DECREF(locals); Py_DECREF(locals);
if (res < 0) { if (res < 0) {
goto error; goto error;
} }
if (value) { if (res) {
Py_DECREF(value);
Py_DECREF(dir); Py_DECREF(dir);
return PyUnicode_FromFormat("self.%U", name); return PyUnicode_FromFormat("self.%U", name);
} }