gh-104066: Improve performance of hasattr for module objects (#104063) · python/cpython@fdcb49c

GitHub

@@ -702,7 +702,11 @@ int

702702_PyModuleSpec_IsInitializing(PyObject*spec)

703703{

704704if (spec!=NULL) {

705-PyObject*value=PyObject_GetAttr(spec, &_Py_ID(_initializing));

705+PyObject*value;

706+intok=_PyObject_LookupAttr(spec, &_Py_ID(_initializing), &value);

707+if (ok==0) {

708+return0;

709+ }

706710if (value!=NULL) {

707711intinitializing=PyObject_IsTrue(value);

708712Py_DECREF(value);

@@ -738,19 +742,37 @@ _PyModuleSpec_IsUninitializedSubmodule(PyObject *spec, PyObject *name)

738742returnis_uninitialized;

739743}

740744741-staticPyObject*

742-module_getattro(PyModuleObject*m, PyObject*name)

745+PyObject*

746+_Py_module_getattro_impl(PyModuleObject*m, PyObject*name, intsuppress)

743747{

748+// When suppress=1, this function suppresses AttributeError.

744749PyObject*attr, *mod_name, *getattr;

745-attr=PyObject_GenericGetAttr((PyObject*)m, name);

746-if (attr|| !PyErr_ExceptionMatches(PyExc_AttributeError)) {

750+attr=_PyObject_GenericGetAttrWithDict((PyObject*)m, name, NULL, suppress);

751+if (attr) {

747752returnattr;

748753 }

749-PyErr_Clear();

754+if (suppress==1) {

755+if (PyErr_Occurred()) {

756+// pass up non-AttributeError exception

757+returnNULL;

758+ }

759+ }

760+else {

761+if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {

762+// pass up non-AttributeError exception

763+returnNULL;

764+ }

765+PyErr_Clear();

766+ }

750767assert(m->md_dict!=NULL);

751768getattr=PyDict_GetItemWithError(m->md_dict, &_Py_ID(__getattr__));

752769if (getattr) {

753-returnPyObject_CallOneArg(getattr, name);

770+PyObject*result=PyObject_CallOneArg(getattr, name);

771+if (result==NULL&&suppress==1&&PyErr_ExceptionMatches(PyExc_AttributeError)) {

772+// suppress AttributeError

773+PyErr_Clear();

774+ }

775+returnresult;

754776 }

755777if (PyErr_Occurred()) {

756778returnNULL;

@@ -763,37 +785,48 @@ module_getattro(PyModuleObject *m, PyObject *name)

763785Py_DECREF(mod_name);

764786returnNULL;

765787 }

766-Py_XINCREF(spec);

767-if (_PyModuleSpec_IsInitializing(spec)) {

768-PyErr_Format(PyExc_AttributeError,

769-"partially initialized "

770-"module '%U' has no attribute '%U' "

771-"(most likely due to a circular import)",

772-mod_name, name);

773- }

774-elseif (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) {

775-PyErr_Format(PyExc_AttributeError,

776-"cannot access submodule '%U' of module '%U' "

777-"(most likely due to a circular import)",

778-name, mod_name);

779- }

780-else {

781-PyErr_Format(PyExc_AttributeError,

782-"module '%U' has no attribute '%U'",

783-mod_name, name);

788+if (suppress!=1) {

789+Py_XINCREF(spec);

790+if (_PyModuleSpec_IsInitializing(spec)) {

791+PyErr_Format(PyExc_AttributeError,

792+"partially initialized "

793+"module '%U' has no attribute '%U' "

794+"(most likely due to a circular import)",

795+mod_name, name);

796+ }

797+elseif (_PyModuleSpec_IsUninitializedSubmodule(spec, name)) {

798+PyErr_Format(PyExc_AttributeError,

799+"cannot access submodule '%U' of module '%U' "

800+"(most likely due to a circular import)",

801+name, mod_name);

802+ }

803+else {

804+PyErr_Format(PyExc_AttributeError,

805+"module '%U' has no attribute '%U'",

806+mod_name, name);

807+ }

808+Py_XDECREF(spec);

784809 }

785-Py_XDECREF(spec);

786810Py_DECREF(mod_name);

787811returnNULL;

788812 }

789813elseif (PyErr_Occurred()) {

790814returnNULL;

791815 }

792-PyErr_Format(PyExc_AttributeError,

793-"module has no attribute '%U'", name);

816+if (suppress!=1) {

817+PyErr_Format(PyExc_AttributeError,

818+"module has no attribute '%U'", name);

819+ }

794820returnNULL;

795821}

796822823+824+PyObject*

825+_Py_module_getattro(PyModuleObject*m, PyObject*name)

826+{

827+return_Py_module_getattro_impl(m, name, 0);

828+}

829+797830staticint

798831module_traverse(PyModuleObject*m, visitprocvisit, void*arg)

799832{

@@ -951,7 +984,7 @@ PyTypeObject PyModule_Type = {

9519840, /* tp_hash */

9529850, /* tp_call */

9539860, /* tp_str */

954- (getattrofunc)module_getattro, /* tp_getattro */

987+ (getattrofunc)_Py_module_getattro,/* tp_getattro */

955988PyObject_GenericSetAttr, /* tp_setattro */

9569890, /* tp_as_buffer */

957990Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |