summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Include/object.h4
-rw-r--r--Misc/NEWS.d/next/Core and Builtins/2018-01-16-18-51-58.bpo-32544.ga-cFE.rst3
-rw-r--r--Modules/_threadmodule.c6
-rw-r--r--Objects/object.c56
-rw-r--r--Python/bltinmodule.c21
5 files changed, 71 insertions, 19 deletions
diff --git a/Include/object.h b/Include/object.h
index 7db5bfe..274b676 100644
--- a/Include/object.h
+++ b/Include/object.h
@@ -536,6 +536,8 @@ PyAPI_FUNC(int) PyObject_SetAttr(PyObject *, PyObject *, PyObject *);
PyAPI_FUNC(int) PyObject_HasAttr(PyObject *, PyObject *);
#ifndef Py_LIMITED_API
PyAPI_FUNC(int) _PyObject_IsAbstract(PyObject *);
+/* Same as PyObject_GetAttr(), but don't raise AttributeError. */
+PyAPI_FUNC(PyObject *) _PyObject_GetAttrWithoutError(PyObject *, PyObject *);
PyAPI_FUNC(PyObject *) _PyObject_GetAttrId(PyObject *, struct _Py_Identifier *);
PyAPI_FUNC(int) _PyObject_SetAttrId(PyObject *, struct _Py_Identifier *, PyObject *);
PyAPI_FUNC(int) _PyObject_HasAttrId(PyObject *, struct _Py_Identifier *);
@@ -567,7 +569,7 @@ PyAPI_FUNC(int) PyObject_CallFinalizerFromDealloc(PyObject *);
/* Same as PyObject_Generic{Get,Set}Attr, but passing the attributes
dict as the last parameter. */
PyAPI_FUNC(PyObject *)
-_PyObject_GenericGetAttrWithDict(PyObject *, PyObject *, PyObject *);
+_PyObject_GenericGetAttrWithDict(PyObject *, PyObject *, PyObject *, int);
PyAPI_FUNC(int)
_PyObject_GenericSetAttrWithDict(PyObject *, PyObject *,
PyObject *, PyObject *);
diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-16-18-51-58.bpo-32544.ga-cFE.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-16-18-51-58.bpo-32544.ga-cFE.rst
new file mode 100644
index 0000000..55a52fa
--- /dev/null
+++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-16-18-51-58.bpo-32544.ga-cFE.rst
@@ -0,0 +1,3 @@
+``hasattr(obj, name)`` and ``getattr(obj, name, default)`` are about 4 times
+faster than before when ``name`` is not found and ``obj`` doesn't override
+``__getattr__`` or ``__getattribute__``.
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
index c9171f5..f594bda 100644
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -925,13 +925,15 @@ local_getattro(localobject *self, PyObject *name)
if (Py_TYPE(self) != &localtype)
/* use generic lookup for subtypes */
- return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
+ return _PyObject_GenericGetAttrWithDict(
+ (PyObject *)self, name, ldict, 0);
/* Optimization: just look in dict ourselves */
value = PyDict_GetItem(ldict, name);
if (value == NULL)
/* Fall back on generic to get __class__ and __dict__ */
- return _PyObject_GenericGetAttrWithDict((PyObject *)self, name, ldict);
+ return _PyObject_GenericGetAttrWithDict(
+ (PyObject *)self, name, ldict, 0);
Py_INCREF(value);
return value;
diff --git a/Objects/object.c b/Objects/object.c
index a0d651d..62d7fbe 100644
--- a/Objects/object.c
+++ b/Objects/object.c
@@ -887,10 +887,41 @@ PyObject_GetAttr(PyObject *v, PyObject *name)
return NULL;
}
+PyObject *
+_PyObject_GetAttrWithoutError(PyObject *v, PyObject *name)
+{
+ PyTypeObject *tp = Py_TYPE(v);
+ PyObject *ret = NULL;
+
+ if (!PyUnicode_Check(name)) {
+ PyErr_Format(PyExc_TypeError,
+ "attribute name must be string, not '%.200s'",
+ name->ob_type->tp_name);
+ return NULL;
+ }
+
+ if (tp->tp_getattro == PyObject_GenericGetAttr) {
+ return _PyObject_GenericGetAttrWithDict(v, name, NULL, 1);
+ }
+ if (tp->tp_getattro != NULL) {
+ ret = (*tp->tp_getattro)(v, name);
+ }
+ else if (tp->tp_getattr != NULL) {
+ const char *name_str = PyUnicode_AsUTF8(name);
+ if (name_str == NULL)
+ return NULL;
+ ret = (*tp->tp_getattr)(v, (char *)name_str);
+ }
+ if (ret == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
+ return ret;
+}
+
int
PyObject_HasAttr(PyObject *v, PyObject *name)
{
- PyObject *res = PyObject_GetAttr(v, name);
+ PyObject *res = _PyObject_GetAttrWithoutError(v, name);
if (res != NULL) {
Py_DECREF(res);
return 1;
@@ -1098,10 +1129,13 @@ _PyObject_GetMethod(PyObject *obj, PyObject *name, PyObject **method)
/* Generic GetAttr functions - put these in your tp_[gs]etattro slot. */
PyObject *
-_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
+_PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name,
+ PyObject *dict, int suppress)
{
/* Make sure the logic of _PyObject_GetMethod is in sync with
this method.
+
+ When suppress=1, this function suppress AttributeError.
*/
PyTypeObject *tp = Py_TYPE(obj);
@@ -1132,6 +1166,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
f = descr->ob_type->tp_descr_get;
if (f != NULL && PyDescr_IsData(descr)) {
res = f(descr, obj, (PyObject *)obj->ob_type);
+ if (res == NULL && suppress &&
+ PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
goto done;
}
}
@@ -1171,6 +1209,10 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
if (f != NULL) {
res = f(descr, obj, (PyObject *)Py_TYPE(obj));
+ if (res == NULL && suppress &&
+ PyErr_ExceptionMatches(PyExc_AttributeError)) {
+ PyErr_Clear();
+ }
goto done;
}
@@ -1180,9 +1222,11 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
goto done;
}
- PyErr_Format(PyExc_AttributeError,
- "'%.50s' object has no attribute '%U'",
- tp->tp_name, name);
+ if (!suppress) {
+ PyErr_Format(PyExc_AttributeError,
+ "'%.50s' object has no attribute '%U'",
+ tp->tp_name, name);
+ }
done:
Py_XDECREF(descr);
Py_DECREF(name);
@@ -1192,7 +1236,7 @@ _PyObject_GenericGetAttrWithDict(PyObject *obj, PyObject *name, PyObject *dict)
PyObject *
PyObject_GenericGetAttr(PyObject *obj, PyObject *name)
{
- return _PyObject_GenericGetAttrWithDict(obj, name, NULL);
+ return _PyObject_GenericGetAttrWithDict(obj, name, NULL, 0);
}
int
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index e702f7c..844548f 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -1126,13 +1126,15 @@ builtin_getattr(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
"getattr(): attribute name must be string");
return NULL;
}
- result = PyObject_GetAttr(v, name);
- if (result == NULL && dflt != NULL &&
- PyErr_ExceptionMatches(PyExc_AttributeError))
- {
- PyErr_Clear();
- Py_INCREF(dflt);
- result = dflt;
+ if (dflt != NULL) {
+ result = _PyObject_GetAttrWithoutError(v, name);
+ if (result == NULL && !PyErr_Occurred()) {
+ Py_INCREF(dflt);
+ return dflt;
+ }
+ }
+ else {
+ result = PyObject_GetAttr(v, name);
}
return result;
}
@@ -1189,10 +1191,9 @@ builtin_hasattr_impl(PyObject *module, PyObject *obj, PyObject *name)
"hasattr(): attribute name must be string");
return NULL;
}
- v = PyObject_GetAttr(obj, name);
+ v = _PyObject_GetAttrWithoutError(obj, name);
if (v == NULL) {
- if (PyErr_ExceptionMatches(PyExc_AttributeError)) {
- PyErr_Clear();
+ if (!PyErr_Occurred()) {
Py_RETURN_FALSE;
}
return NULL;