Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 35.2k
gh-153785: Generate AttributeError messages from context#153786
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
Changes from all commits
ac555bc4f22a16b30d67628e04d3e5c492238545631e883312fd216a356f56e8db0394ebcc795b6fda10d624835c50a3567ad5204b65a7fde99d8758c29de36faf358086a0c9e5bb17ef2507a59c81069e905ab27d720ac337cef46aa47db4ff6a48e00db5fb5ce3c9da8c723c9416579ea6c8af825f277b73fb87378e30faf6e53a86fde59b00321df7682ca419eb2a7e7f0a08f8818b9b8d7dcb5ae96404359df28f1d542522c0File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -215,6 +215,8 @@ The following exceptions are the exceptions that are usually raised. | ||
| The object that was accessed for the named attribute. | ||
| When possible, :attr:`name` and :attr:`obj` are set automatically. | ||
| .. versionchanged:: 3.10 | ||
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We dropped the
| ||
| Added the :attr:`name` and :attr:`obj` attributes. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -10,6 +10,7 @@ | ||
| from codecs import BOM_UTF8 | ||
| from itertools import product | ||
| from textwrap import dedent | ||
| from types import ModuleType | ||
| from test.support import (captured_stderr, check_impl_detail, | ||
| cpython_only, gc_collect, | ||
| @@ -2047,6 +2048,129 @@ def blech(self): | ||
| self.assertEqual("bluch", exc.name) | ||
| self.assertEqual(obj, exc.obj) | ||
| def test_getattr_error_message(self): | ||
johnslavik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| def fqn(type): | ||
| return f'{type.__module__}.{type.__qualname__}' | ||
| class RaiseWithName: | ||
| def __getattr__(self, name): | ||
| raise AttributeError(name) | ||
| obj = RaiseWithName() | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(obj, "missing1") | ||
| self.assertEqual(str(cm.exception), | ||
| f"'{fqn(RaiseWithName)}' object has no attribute 'missing1'") | ||
| self.assertIs(cm.exception.obj, obj) | ||
| self.assertEqual(cm.exception.name, "missing1") | ||
| class BareRaise: | ||
| def __getattr__(self, name): | ||
| raise AttributeError | ||
| obj = BareRaise() | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(obj, "missing2") | ||
| self.assertEqual(str(cm.exception), | ||
| f"'{fqn(BareRaise)}' object has no attribute 'missing2'") | ||
| self.assertIs(cm.exception.obj, obj) | ||
| self.assertEqual(cm.exception.name, "missing2") | ||
| class RaiseCustom: | ||
| def __getattr__(self, name): | ||
| raise AttributeError("custom") | ||
| obj = RaiseCustom() | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(obj, "missing3") | ||
| self.assertEqual(str(cm.exception), "custom") | ||
| self.assertIs(cm.exception.obj, obj) | ||
| self.assertEqual(cm.exception.name, "missing3") | ||
| def test_class_getattr_error_message(self): | ||
| def fqn(type): | ||
| return f'{type.__module__}.{type.__qualname__}' | ||
| class MetaclassRaiseWithName(type): | ||
| def __getattr__(self, name): | ||
| raise AttributeError(name) | ||
| cls = MetaclassRaiseWithName("spam", (), {}) | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(cls, "missing1") | ||
| self.assertEqual(str(cm.exception), | ||
| f"type object '{fqn(cls)}' has no attribute 'missing1'") | ||
| self.assertIs(cm.exception.obj, cls) | ||
| self.assertEqual(cm.exception.name, "missing1") | ||
| class MetaclassBareRaise(type): | ||
| def __getattr__(self, name): | ||
| raise AttributeError | ||
| cls = MetaclassBareRaise("eggs", (), {}) | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(cls, "missing2") | ||
| self.assertEqual(str(cm.exception), | ||
| f"type object '{fqn(cls)}' has no attribute 'missing2'") | ||
| self.assertIs(cm.exception.obj, cls) | ||
| self.assertEqual(cm.exception.name, "missing2") | ||
| class MetaclassRaiseCustom(type): | ||
| def __getattr__(self, name): | ||
| raise AttributeError("custom") | ||
| cls = MetaclassRaiseCustom("ham", (), {}) | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(cls, "missing3") | ||
| self.assertEqual(str(cm.exception), "custom") | ||
| self.assertIs(cm.exception.obj, cls) | ||
| self.assertEqual(cm.exception.name, "missing3") | ||
| def test_module_getattr_error_message(self): | ||
| raisewithname_mod = ModuleType("raisewithname") | ||
| def raise_with_name(name): | ||
| raise AttributeError(name) | ||
| raisewithname_mod.__getattr__ = raise_with_name | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(raisewithname_mod, "missing1") | ||
| self.assertEqual(str(cm.exception), | ||
| "module 'raisewithname' has no attribute 'missing1'") | ||
| self.assertIs(cm.exception.obj, raisewithname_mod) | ||
| self.assertEqual(cm.exception.name, "missing1") | ||
| bareraise_mod = ModuleType("bareraise") | ||
| def bare_raise(name): | ||
| raise AttributeError | ||
| bareraise_mod.__getattr__ = bare_raise | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(bareraise_mod, "missing2") | ||
| self.assertEqual(str(cm.exception), | ||
| "module 'bareraise' has no attribute 'missing2'") | ||
| self.assertIs(cm.exception.obj, bareraise_mod) | ||
| self.assertEqual(cm.exception.name, "missing2") | ||
| custom_mod = ModuleType("custom") | ||
| def raise_custom(name): | ||
| raise AttributeError("custom") | ||
| custom_mod.__getattr__ = raise_custom | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(custom_mod, "missing3") | ||
| self.assertEqual(str(cm.exception), "custom") | ||
| self.assertIs(cm.exception.obj, custom_mod) | ||
| self.assertEqual(cm.exception.name, "missing3") | ||
| nameless_mod = ModuleType("forgettable") | ||
| del nameless_mod.__dict__["__name__"] | ||
| nameless_mod.__getattr__ = raise_with_name | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(nameless_mod, "missing4") | ||
| self.assertEqual(str(cm.exception), "module has no attribute 'missing4'") | ||
| self.assertIs(cm.exception.obj, nameless_mod) | ||
| self.assertEqual(cm.exception.name, "missing4") | ||
| nameless_mod = ModuleType("broken") | ||
| nameless_mod.__dict__["__name__"] = 10j | ||
| nameless_mod.__getattr__ = raise_with_name | ||
| with self.assertRaises(AttributeError) as cm: | ||
| getattr(nameless_mod, "missing4") | ||
| self.assertEqual(str(cm.exception), "module has no attribute 'missing4'") | ||
| self.assertIs(cm.exception.obj, nameless_mod) | ||
| self.assertEqual(cm.exception.name, "missing4") | ||
| # Note: name suggestion tests live in `test_traceback`. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| :exc:`AttributeError`: The default error message is now generated from ``name`` | ||
| and ``obj`` attributes when both are set and the exception was constructed with | ||
| no positional arguments, or with a single positional argument equal to ``name``. | ||
| Patch by Bartosz Sławecki. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -11,9 +11,11 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_exceptions.h" // struct _Py_exc_state | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_initconfig.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_modsupport.h" // _PyArg_NoKeywords() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_moduleobject.h" // _PyModule_CAST() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_object.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_pyerrors.h" // struct _PyErr_SetRaisedException | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_tuple.h" // _PyTuple_FromPair | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "pycore_unicodeobject.h" // _PyUnicode_Equal() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "osdefs.h" // SEP | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #include "clinic/exceptions.c.h" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @@ -2702,6 +2704,65 @@ AttributeError_dealloc(PyObject *self) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_TYPE(self)->tp_free(self); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static PyObject * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AttributeError_str(PyObject *op) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyAttributeErrorObject *self = PyAttributeErrorObject_CAST(op); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *arg; // borrowed ref | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *obj = NULL, *name = NULL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* .name and .obj are set automatically when attribute lookup fails, so | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| synthesize a more informative message from them when the caller | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| didn't supply a meaningful one of their own -- that is, when args is | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| empty, or contains only the attribute name. Otherwise, use the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| message the caller gave. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_BEGIN_CRITICAL_SECTION(self); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| self->obj && self->name && PyUnicode_Check(self->name) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && ((PyTuple_GET_SIZE(self->args) == 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && PyUnicode_Check(arg = PyTuple_GET_ITEM(self->args, 0)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| && _PyUnicode_Equal(arg, self->name)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| || PyTuple_GET_SIZE(self->args) == 0) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
johnslavik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| obj = Py_NewRef(self->obj); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| name = Py_NewRef(self->name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
serhiy-storchaka marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_END_CRITICAL_SECTION(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!obj) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| assert(!name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return BaseException_str(op); /* re-acquires lock */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
johnslavik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *result = NULL; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (PyModule_Check(obj)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyModuleObject *mod = _PyModule_CAST(obj); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
johnslavik marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PyObject *modname; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (PyDict_GetItemRef(mod->md_dict, &_Py_ID(__name__), &modname) < 0) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| goto done; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (modname && PyUnicode_Check(modname)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = PyUnicode_FromFormat("module %R has no attribute %R", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| modname, name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_DECREF(modname); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_XDECREF(modname); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = PyUnicode_FromFormat("module has no attribute %R", name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else if (PyType_Check(obj)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = PyUnicode_FromFormat("type object '%N' has no attribute %R", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| obj, name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| result = PyUnicode_FromFormat("'%T' object has no attribute %R", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| obj, name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+2745
to
+2758
Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The exc=AttributeError()
exc.name="a'b"exc.obj= ["list"]
print(str(exc))Current output: I suggesting using
Suggested change
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| done: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_DECREF(obj); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Py_DECREF(name); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| static int | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AttributeError_traverse(PyObject *op, visitproc visit, void *arg) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @@ -2770,7 +2831,7 @@ static PyMethodDef AttributeError_methods[] = { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ComplexExtendsException(PyExc_Exception, AttributeError, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AttributeError, 0, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| AttributeError_methods, AttributeError_members, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0, BaseException_str, 0, "Attribute not found."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0, AttributeError_str, 0, "Attribute not found."); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /* | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * SyntaxError extends Exception | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.