From cb764d9dd9d6ad1e10ad89150c3634e1b567e1f9 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Thu, 6 Apr 2017 01:29:49 +0300 Subject: [PATCH 01/26] bpo-29999: repr() of ImportError now contains attributes name and path. --- Lib/test/test_exceptions.py | 30 +++++++++++++++++++++++ Objects/exceptions.c | 47 ++++++++++++++++++++++++++++++++----- 2 files changed, 71 insertions(+), 6 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 57d0656487d4db..5140923b43107c 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2595,6 +2595,36 @@ def after_with(): with ExitFails(): 1/0 self.lineno_after_raise(after_with, 1, 1) + def test_repr(self): + exc = ImportError() + self.assertEqual(repr(exc), "ImportError()") + + exc = ImportError('test') + self.assertEqual(repr(exc), "ImportError('test')") + + exc = ImportError('test', 'case') + self.assertEqual(repr(exc), "ImportError('test', 'case')") + + exc = ImportError(name='somemodule') + self.assertEqual(repr(exc), "ImportError(name='somemodule')") + + exc = ImportError('test', name='somemodule') + self.assertEqual(repr(exc), "ImportError('test', name='somemodule')") + + exc = ImportError(path='somepath') + self.assertEqual(repr(exc), "ImportError(path='somepath')") + + exc = ImportError('test', path='somepath') + self.assertEqual(repr(exc), "ImportError('test', path='somepath')") + + exc = ImportError(name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError(name='somename', path='somepath')") + + exc = ImportError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError('test', name='somename', path='somepath')") + if __name__ == '__main__': unittest.main() diff --git a/Objects/exceptions.c b/Objects/exceptions.c index b17cac83551670..ff0122c6aa2fa6 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1864,6 +1864,30 @@ ImportError_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) return res; } +static PyObject * +ImportError_repr(PyImportErrorObject *self) +{ + int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; + PyObject *r = BaseException_repr((PyBaseExceptionObject *)self); + if (r && (self->name || self->path)) { + /* remove ')' */ + Py_SETREF(r, PyUnicode_Substring(r, 0, PyUnicode_GET_LENGTH(r) - 1)); + if (r && self->name) { + Py_SETREF(r, PyUnicode_FromFormat("%U%sname=%R", + r, hasargs ? ", " : "", self->name)); + hasargs = 1; + } + if (r && self->path) { + Py_SETREF(r, PyUnicode_FromFormat("%U%spath=%R", + r, hasargs ? ", " : "", self->path)); + } + if (r) { + Py_SETREF(r, PyUnicode_FromFormat("%U)", r)); + } + } + return r; +} + static PyMemberDef ImportError_members[] = { {"msg", _Py_T_OBJECT, offsetof(PyImportErrorObject, msg), 0, PyDoc_STR("exception message")}, @@ -1881,12 +1905,23 @@ static PyMethodDef ImportError_methods[] = { {NULL} }; -ComplexExtendsException(PyExc_Exception, ImportError, - ImportError, 0 /* new */, - ImportError_methods, ImportError_members, - 0 /* getset */, ImportError_str, - "Import can't find module, or can't find name in " - "module."); +static PyTypeObject _PyExc_ImportError = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImportError", + sizeof(PyImportErrorObject), 0, + (destructor)ImportError_dealloc, 0, 0, 0, 0, + (reprfunc)ImportError_repr, 0, 0, 0, 0, 0, + (reprfunc)ImportError_str, 0, 0, 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + PyDoc_STR("Import can't find module, or can't find name in " + "module."), + (traverseproc)ImportError_traverse, + (inquiry)ImportError_clear, 0, 0, 0, 0, ImportError_methods, + ImportError_members, 0, &_PyExc_Exception, + 0, 0, 0, offsetof(PyImportErrorObject, dict), + (initproc)ImportError_init, +}; +PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; /* * ModuleNotFoundError extends ImportError From db41c7da327b82b9e9989ec03ab2c28437463f90 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sun, 12 Feb 2023 22:12:10 +0400 Subject: [PATCH 02/26] Use GitHub Actions version of Bedevere --- .github/workflows/trigger-bots.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .github/workflows/trigger-bots.yml diff --git a/.github/workflows/trigger-bots.yml b/.github/workflows/trigger-bots.yml new file mode 100644 index 00000000000000..995ca2658bf173 --- /dev/null +++ b/.github/workflows/trigger-bots.yml @@ -0,0 +1,20 @@ +name: Bots + +on: + # PR synchronization is a push into a branch associated with the PR + pull_request: + types: [opened, edited, synchronize, labeled, unlabeled] + issue_comment: + types: [created, edited] + pull_request_review_comment: + types: [created, edited] + +jobs: + call_bedevere: + name: Call Bedevere + runs-on: ubuntu-latest + steps: + - uses: arhadthedev/bedevere@c51d1d2d5c7a9cec31ce16af209f42a5905070dd + with: + token: ${{ secrets.BEDEVERE_PAT }} + event: ${{ toJSON(github.event) }} From 1caeb91017146d86dfc0cb812f8b776a342701ff Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Mon, 13 Feb 2023 21:27:04 +0400 Subject: [PATCH 03/26] Add the news entry back --- .../2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst new file mode 100644 index 00000000000000..3a992bfe6b8413 --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst @@ -0,0 +1,2 @@ +:meth:`repr` of :class:`ImportError` now contains attributes name and path. +Patch by Serhiy Storchaka. From 7f29270d2ff442f62a532dfe3a7d211e2f58977e Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Mon, 13 Feb 2023 21:28:52 +0400 Subject: [PATCH 04/26] Remove an accidentally included untracked file --- .github/workflows/trigger-bots.yml | 20 -------------------- 1 file changed, 20 deletions(-) delete mode 100644 .github/workflows/trigger-bots.yml diff --git a/.github/workflows/trigger-bots.yml b/.github/workflows/trigger-bots.yml deleted file mode 100644 index 995ca2658bf173..00000000000000 --- a/.github/workflows/trigger-bots.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: Bots - -on: - # PR synchronization is a push into a branch associated with the PR - pull_request: - types: [opened, edited, synchronize, labeled, unlabeled] - issue_comment: - types: [created, edited] - pull_request_review_comment: - types: [created, edited] - -jobs: - call_bedevere: - name: Call Bedevere - runs-on: ubuntu-latest - steps: - - uses: arhadthedev/bedevere@c51d1d2d5c7a9cec31ce16af209f42a5905070dd - with: - token: ${{ secrets.BEDEVERE_PAT }} - event: ${{ toJSON(github.event) }} From c80e056c025b2124152917bb97cd13e358c833f6 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:04:37 +0200 Subject: [PATCH 05/26] first commit --- .../2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst | 2 -- Objects/exceptions.c | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst b/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst deleted file mode 100644 index 3a992bfe6b8413..00000000000000 --- a/Misc/NEWS.d/next/Core_and_Builtins/2023-02-13-21-26-54.gh-issue-74185.Sk3O7m.rst +++ /dev/null @@ -1,2 +0,0 @@ -:meth:`repr` of :class:`ImportError` now contains attributes name and path. -Patch by Serhiy Storchaka. diff --git a/Objects/exceptions.c b/Objects/exceptions.c index ff0122c6aa2fa6..344d961b09b357 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1868,7 +1868,7 @@ static PyObject * ImportError_repr(PyImportErrorObject *self) { int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; - PyObject *r = BaseException_repr((PyBaseExceptionObject *)self); + PyObject *r = BaseException_repr((PyObject *)self); if (r && (self->name || self->path)) { /* remove ')' */ Py_SETREF(r, PyUnicode_Substring(r, 0, PyUnicode_GET_LENGTH(r) - 1)); From 126be2061860537ec963915711c831387b4b365f Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:23:59 +0200 Subject: [PATCH 06/26] add test_ModuleNotFoundError_repr_with_failed_import --- Lib/test/test_exceptions.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 5140923b43107c..747fd603907d69 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2595,6 +2595,7 @@ def after_with(): with ExitFails(): 1/0 self.lineno_after_raise(after_with, 1, 1) + def test_repr(self): exc = ImportError() self.assertEqual(repr(exc), "ImportError()") @@ -2624,6 +2625,22 @@ def test_repr(self): exc = ImportError('test', name='somename', path='somepath') self.assertEqual(repr(exc), "ImportError('test', name='somename', path='somepath')") + + exc = ModuleNotFoundError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ModuleNotFoundError('test', name='somename', path='somepath')") + + def test_importerror_name_and_path(self): + try: + import does_not_exist # noqa: F401 + except ModuleNotFoundError as e: + self.assertEqual(e.name, "does_not_exist") + self.assertIsNone(e.path) + + self.assertEqual(repr(e), + "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") + else: + self.fail("Expected ModuleNotFoundError was not raised") if __name__ == '__main__': From 4a97f5cc2dff67e0cf33d8a931e2f480e1d2ee90 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:26:12 +0200 Subject: [PATCH 07/26] add test_ModuleNotFoundError_repr_with_failed_import --- Lib/test/test_exceptions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 747fd603907d69..46397f5ab1a08b 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2630,9 +2630,9 @@ def test_repr(self): self.assertEqual(repr(exc), "ModuleNotFoundError('test', name='somename', path='somepath')") - def test_importerror_name_and_path(self): + def test_ModuleNotFoundError_repr_with_failed_import(self): try: - import does_not_exist # noqa: F401 + import does_not_exist # type: ignore[import] except ModuleNotFoundError as e: self.assertEqual(e.name, "does_not_exist") self.assertIsNone(e.path) From 852da8c7a39c9c5191fd00b74c4fb81c20de44ea Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:36:30 +0200 Subject: [PATCH 08/26] add blurb --- .../2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst new file mode 100644 index 00000000000000..d5fef8dc6e1c0b --- /dev/null +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst @@ -0,0 +1,2 @@ +:meth:`repr` of :class:`ImportError` now contains attributes name and path. +Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir From 6ab73622820365efe0372c21f658ed445646091f Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:42:34 +0200 Subject: [PATCH 09/26] add noqa:F401 --- Lib/test/test_exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 46397f5ab1a08b..fbf1c917bcbbea 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2632,7 +2632,7 @@ def test_repr(self): def test_ModuleNotFoundError_repr_with_failed_import(self): try: - import does_not_exist # type: ignore[import] + import does_not_exist # type: ignore[import] # noqa: F401 except ModuleNotFoundError as e: self.assertEqual(e.name, "does_not_exist") self.assertIsNone(e.path) From 12c2293635c5e2af566e8d3a6e29968fa11ba66a Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:49:56 +0200 Subject: [PATCH 10/26] remove whitespace --- Lib/test/test_exceptions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index fbf1c917bcbbea..1db6d38709269c 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2642,6 +2642,5 @@ def test_ModuleNotFoundError_repr_with_failed_import(self): else: self.fail("Expected ModuleNotFoundError was not raised") - if __name__ == '__main__': unittest.main() From a53da379c26ccef190ca4efa27aa3d80312bfcbd Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 10:53:47 +0200 Subject: [PATCH 11/26] fix trailing-whitespace --- Lib/test/test_exceptions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 1db6d38709269c..9f1c3b545bda6d 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2625,7 +2625,7 @@ def test_repr(self): exc = ImportError('test', name='somename', path='somepath') self.assertEqual(repr(exc), "ImportError('test', name='somename', path='somepath')") - + exc = ModuleNotFoundError('test', name='somename', path='somepath') self.assertEqual(repr(exc), "ModuleNotFoundError('test', name='somename', path='somepath')") From 5b6d626086fcb9996172361cf4f62e679c7be670 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 11:23:15 +0200 Subject: [PATCH 12/26] use ComplexExtendsException --- Objects/exceptions.c | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 344d961b09b357..3ce822b0365ff4 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1905,23 +1905,12 @@ static PyMethodDef ImportError_methods[] = { {NULL} }; -static PyTypeObject _PyExc_ImportError = { - PyVarObject_HEAD_INIT(NULL, 0) - "ImportError", - sizeof(PyImportErrorObject), 0, - (destructor)ImportError_dealloc, 0, 0, 0, 0, - (reprfunc)ImportError_repr, 0, 0, 0, 0, 0, - (reprfunc)ImportError_str, 0, 0, 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - PyDoc_STR("Import can't find module, or can't find name in " - "module."), - (traverseproc)ImportError_traverse, - (inquiry)ImportError_clear, 0, 0, 0, 0, ImportError_methods, - ImportError_members, 0, &_PyExc_Exception, - 0, 0, 0, offsetof(PyImportErrorObject, dict), - (initproc)ImportError_init, -}; -PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; +ComplexExtendsException(PyExc_Exception, ImportError, + ImportError, 0 /* new */, + ImportError_methods, ImportError_members, + 0 /* getset */, ImportError_str, + "Import can't find module, or can't find name in " + "module."); /* * ModuleNotFoundError extends ImportError From 951e0a112e3e8b97e984f0a833537e984bbd2b53 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 11:56:40 +0200 Subject: [PATCH 13/26] return to non-macro style --- Objects/exceptions.c | 39 ++++++++++++++++++++++++++------------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 3ce822b0365ff4..0419471e0efa85 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1865,21 +1865,23 @@ ImportError_reduce(PyObject *self, PyObject *Py_UNUSED(ignored)) } static PyObject * -ImportError_repr(PyImportErrorObject *self) +ImportError_repr(PyObject *self) { int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; - PyObject *r = BaseException_repr((PyObject *)self); - if (r && (self->name || self->path)) { + PyObject *r = BaseException_repr(self); + PyImportErrorObject *exc = PyImportErrorObject_CAST(self); + + if (r && (exc->name || exc->path)) { /* remove ')' */ Py_SETREF(r, PyUnicode_Substring(r, 0, PyUnicode_GET_LENGTH(r) - 1)); - if (r && self->name) { + if (r && exc->name) { Py_SETREF(r, PyUnicode_FromFormat("%U%sname=%R", - r, hasargs ? ", " : "", self->name)); + r, hasargs ? ", " : "", exc->name)); hasargs = 1; } - if (r && self->path) { + if (r && exc->path) { Py_SETREF(r, PyUnicode_FromFormat("%U%spath=%R", - r, hasargs ? ", " : "", self->path)); + r, hasargs ? ", " : "", exc->path)); } if (r) { Py_SETREF(r, PyUnicode_FromFormat("%U)", r)); @@ -1905,12 +1907,23 @@ static PyMethodDef ImportError_methods[] = { {NULL} }; -ComplexExtendsException(PyExc_Exception, ImportError, - ImportError, 0 /* new */, - ImportError_methods, ImportError_members, - 0 /* getset */, ImportError_str, - "Import can't find module, or can't find name in " - "module."); +static PyTypeObject _PyExc_ImportError = { + PyVarObject_HEAD_INIT(NULL, 0) + "ImportError", + sizeof(PyImportErrorObject), 0, + (destructor)ImportError_dealloc, 0, 0, 0, 0, + ImportError_repr, 0, 0, 0, 0, 0, + ImportError_str, 0, 0, 0, + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + PyDoc_STR("Import can't find module, or can't find name in " + "module."), + (traverseproc)ImportError_traverse, + (inquiry)ImportError_clear, 0, 0, 0, 0, ImportError_methods, + ImportError_members, 0, &_PyExc_Exception, + 0, 0, 0, offsetof(PyImportErrorObject, dict), + (initproc)ImportError_init, +}; +PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; /* * ModuleNotFoundError extends ImportError From 8fbfd18afc103bb12278f70d4dcbb308cc9367dc Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 12:11:23 +0200 Subject: [PATCH 14/26] use modern field initializers --- Objects/exceptions.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 0419471e0efa85..07309c2c911298 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1909,19 +1909,20 @@ static PyMethodDef ImportError_methods[] = { static PyTypeObject _PyExc_ImportError = { PyVarObject_HEAD_INIT(NULL, 0) - "ImportError", - sizeof(PyImportErrorObject), 0, - (destructor)ImportError_dealloc, 0, 0, 0, 0, - ImportError_repr, 0, 0, 0, 0, 0, - ImportError_str, 0, 0, 0, - Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - PyDoc_STR("Import can't find module, or can't find name in " - "module."), - (traverseproc)ImportError_traverse, - (inquiry)ImportError_clear, 0, 0, 0, 0, ImportError_methods, - ImportError_members, 0, &_PyExc_Exception, - 0, 0, 0, offsetof(PyImportErrorObject, dict), - (initproc)ImportError_init, + .tp_name = "ImportError", + .tp_basicsize = sizeof(PyImportErrorObject), + .tp_dealloc = (destructor)ImportError_dealloc, + .tp_repr = ImportError_repr, + .tp_str = (reprfunc)ImportError_str, + .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, + .tp_doc = PyDoc_STR("Import can't find module, or can't find name in module."), + .tp_traverse = (traverseproc)ImportError_traverse, + .tp_clear = (inquiry)ImportError_clear, + .tp_methods = ImportError_methods, + .tp_members = ImportError_members, + .tp_base = &_PyExc_Exception, + .tp_dictoffset = offsetof(PyImportErrorObject, dict), + .tp_init = (initproc)ImportError_init, }; PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; From 458bcd05d22e2b7f75005dc693ecf190ffadd7b3 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 12:12:48 +0200 Subject: [PATCH 15/26] remove un-necessary casts --- Objects/exceptions.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 07309c2c911298..65a3ee1626ebf9 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1911,18 +1911,18 @@ static PyTypeObject _PyExc_ImportError = { PyVarObject_HEAD_INIT(NULL, 0) .tp_name = "ImportError", .tp_basicsize = sizeof(PyImportErrorObject), - .tp_dealloc = (destructor)ImportError_dealloc, + .tp_dealloc = ImportError_dealloc, .tp_repr = ImportError_repr, - .tp_str = (reprfunc)ImportError_str, + .tp_str = ImportError_str, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, .tp_doc = PyDoc_STR("Import can't find module, or can't find name in module."), - .tp_traverse = (traverseproc)ImportError_traverse, - .tp_clear = (inquiry)ImportError_clear, + .tp_traverse = ImportError_traverse, + .tp_clear = ImportError_clear, .tp_methods = ImportError_methods, .tp_members = ImportError_members, .tp_base = &_PyExc_Exception, .tp_dictoffset = offsetof(PyImportErrorObject, dict), - .tp_init = (initproc)ImportError_init, + .tp_init = ImportError_init, }; PyObject *PyExc_ImportError = (PyObject *)&_PyExc_ImportError; From fa2f884b23ca86a178b9e89c69a914269635b39c Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 12:52:40 +0200 Subject: [PATCH 16/26] use PyUnicodeWriter --- Objects/exceptions.c | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 65a3ee1626ebf9..af39b3470af04e 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1870,24 +1870,35 @@ ImportError_repr(PyObject *self) int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; PyObject *r = BaseException_repr(self); PyImportErrorObject *exc = PyImportErrorObject_CAST(self); - + PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); + if (writer == NULL) goto error; + if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) goto error; if (r && (exc->name || exc->path)) { - /* remove ')' */ - Py_SETREF(r, PyUnicode_Substring(r, 0, PyUnicode_GET_LENGTH(r) - 1)); - if (r && exc->name) { - Py_SETREF(r, PyUnicode_FromFormat("%U%sname=%R", - r, hasargs ? ", " : "", exc->name)); + if (exc->name) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; + } + if (PyUnicodeWriter_WriteASCII(writer, "name='", 6) < 0) goto error; + if (PyUnicodeWriter_WriteSubstring(writer, exc->name, 0, PyUnicode_GET_LENGTH(exc->name)) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; hasargs = 1; } - if (r && exc->path) { - Py_SETREF(r, PyUnicode_FromFormat("%U%spath=%R", - r, hasargs ? ", " : "", exc->path)); - } - if (r) { - Py_SETREF(r, PyUnicode_FromFormat("%U)", r)); + if (exc->path) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; + } + if (PyUnicodeWriter_WriteASCII(writer, "path='", 6) < 0) goto error; + if (PyUnicodeWriter_WriteSubstring(writer, exc->path, 0, PyUnicode_GET_LENGTH(exc->path)) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; } } - return r; + if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) goto error; + return PyUnicodeWriter_Finish(writer); + + error: + Py_XDECREF(r); + PyUnicodeWriter_Discard(writer); + return NULL; } static PyMemberDef ImportError_members[] = { From 35a6a292f49e56481be042596540d8f92dcc55a6 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 12:55:15 +0200 Subject: [PATCH 17/26] fix trailing whitespace --- Objects/exceptions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index af39b3470af04e..c0bdc07649b0c7 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1874,7 +1874,7 @@ ImportError_repr(PyObject *self) if (writer == NULL) goto error; if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) goto error; if (r && (exc->name || exc->path)) { - if (exc->name) { + if (exc->name) { if (hasargs) { if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; } From 6e416c650eb8153a3bacf13f95ea4dcd7e9cec02 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 13:01:43 +0200 Subject: [PATCH 18/26] reduce nesting --- Objects/exceptions.c | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index c0bdc07649b0c7..e7ecfff1abe154 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1873,24 +1873,22 @@ ImportError_repr(PyObject *self) PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); if (writer == NULL) goto error; if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) goto error; - if (r && (exc->name || exc->path)) { - if (exc->name) { - if (hasargs) { - if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; - } - if (PyUnicodeWriter_WriteASCII(writer, "name='", 6) < 0) goto error; - if (PyUnicodeWriter_WriteSubstring(writer, exc->name, 0, PyUnicode_GET_LENGTH(exc->name)) < 0) goto error; - if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; - hasargs = 1; + if (exc->name) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; } - if (exc->path) { - if (hasargs) { - if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; - } - if (PyUnicodeWriter_WriteASCII(writer, "path='", 6) < 0) goto error; - if (PyUnicodeWriter_WriteSubstring(writer, exc->path, 0, PyUnicode_GET_LENGTH(exc->path)) < 0) goto error; - if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, "name='", 6) < 0) goto error; + if (PyUnicodeWriter_WriteSubstring(writer, exc->name, 0, PyUnicode_GET_LENGTH(exc->name)) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; + hasargs = 1; + } + if (exc->path) { + if (hasargs) { + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; } + if (PyUnicodeWriter_WriteASCII(writer, "path='", 6) < 0) goto error; + if (PyUnicodeWriter_WriteSubstring(writer, exc->path, 0, PyUnicode_GET_LENGTH(exc->path)) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; } if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) goto error; return PyUnicodeWriter_Finish(writer); From aa75f6aad0875698531b182b3912324705e68833 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 13:14:39 +0200 Subject: [PATCH 19/26] use PyUnicdodeWriter_Format and if statement braces --- Objects/exceptions.c | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index e7ecfff1abe154..9c19139681f35c 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1871,24 +1871,32 @@ ImportError_repr(PyObject *self) PyObject *r = BaseException_repr(self); PyImportErrorObject *exc = PyImportErrorObject_CAST(self); PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); - if (writer == NULL) goto error; - if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) goto error; + if (writer == NULL) { + goto error; + } + if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) { + goto error; + } if (exc->name) { if (hasargs) { - if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) { + goto error; + } + } + if (PyUnicodeWriter_Format(writer, "name=%R", exc->name) < 0) { + goto error; } - if (PyUnicodeWriter_WriteASCII(writer, "name='", 6) < 0) goto error; - if (PyUnicodeWriter_WriteSubstring(writer, exc->name, 0, PyUnicode_GET_LENGTH(exc->name)) < 0) goto error; - if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; hasargs = 1; } if (exc->path) { if (hasargs) { - if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) goto error; + if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) { + goto error; + } + } + if (PyUnicodeWriter_Format(writer, "path=%R", exc->path) < 0) { + goto error; } - if (PyUnicodeWriter_WriteASCII(writer, "path='", 6) < 0) goto error; - if (PyUnicodeWriter_WriteSubstring(writer, exc->path, 0, PyUnicode_GET_LENGTH(exc->path)) < 0) goto error; - if (PyUnicodeWriter_WriteASCII(writer, "'", 1) < 0) goto error; } if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) goto error; return PyUnicodeWriter_Finish(writer); From 8b5e4edb254e54def0bba8ddbbf5eb5a3a89f0d7 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 13:22:23 +0200 Subject: [PATCH 20/26] indentation --- Lib/test/test_exceptions.py | 14 ++++++-------- Objects/exceptions.c | 8 ++++---- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 9f1c3b545bda6d..b0ecf6de4dccec 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2631,16 +2631,14 @@ def test_repr(self): "ModuleNotFoundError('test', name='somename', path='somepath')") def test_ModuleNotFoundError_repr_with_failed_import(self): - try: + with self.assertRaises(ModuleNotFoundError) as cm: import does_not_exist # type: ignore[import] # noqa: F401 - except ModuleNotFoundError as e: - self.assertEqual(e.name, "does_not_exist") - self.assertIsNone(e.path) - self.assertEqual(repr(e), - "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") - else: - self.fail("Expected ModuleNotFoundError was not raised") + self.assertEqual(cm.exception.name, "does_not_exist") + self.assertIsNone(cm.exception.path) + + self.assertEqual(repr(cm.exception), + "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") if __name__ == '__main__': unittest.main() diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 9c19139681f35c..337c11d2a81f29 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1901,10 +1901,10 @@ ImportError_repr(PyObject *self) if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) goto error; return PyUnicodeWriter_Finish(writer); - error: - Py_XDECREF(r); - PyUnicodeWriter_Discard(writer); - return NULL; +error: + Py_XDECREF(r); + PyUnicodeWriter_Discard(writer); + return NULL; } static PyMemberDef ImportError_members[] = { From 28545e50f6ca0ab56fd6b6480aa9892296e09e91 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sat, 19 Jul 2025 14:55:17 +0200 Subject: [PATCH 21/26] move decref --- Objects/exceptions.c | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 337c11d2a81f29..4158d94262bc1f 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1868,15 +1868,19 @@ static PyObject * ImportError_repr(PyObject *self) { int hasargs = PyTuple_GET_SIZE(((PyBaseExceptionObject *)self)->args) != 0; - PyObject *r = BaseException_repr(self); PyImportErrorObject *exc = PyImportErrorObject_CAST(self); PyUnicodeWriter *writer = PyUnicodeWriter_Create(0); if (writer == NULL) { goto error; } - if (PyUnicodeWriter_WriteSubstring(writer, r, 0, PyUnicode_GET_LENGTH(r)-1) < 0) { + PyObject *r = BaseException_repr(self); + if (PyUnicodeWriter_WriteSubstring( + writer, r, 0, PyUnicode_GET_LENGTH(r) - 1) < 0) + { + Py_XDECREF(r); goto error; } + Py_XDECREF(r); if (exc->name) { if (hasargs) { if (PyUnicodeWriter_WriteASCII(writer, ", ", 2) < 0) { @@ -1898,11 +1902,14 @@ ImportError_repr(PyObject *self) goto error; } } - if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) goto error; + + if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) { + goto error; + } + return PyUnicodeWriter_Finish(writer); error: - Py_XDECREF(r); PyUnicodeWriter_Discard(writer); return NULL; } @@ -1932,7 +1939,9 @@ static PyTypeObject _PyExc_ImportError = { .tp_repr = ImportError_repr, .tp_str = ImportError_str, .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, - .tp_doc = PyDoc_STR("Import can't find module, or can't find name in module."), + .tp_doc = PyDoc_STR( + "Import can't find module, " + "or can't find name in module."), .tp_traverse = ImportError_traverse, .tp_clear = ImportError_clear, .tp_methods = ImportError_methods, From 4f63193f7d86b40f6ec10065a59652246421db58 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sun, 20 Jul 2025 10:11:07 +0200 Subject: [PATCH 22/26] check baseException_repr failure, add 3.15rst --- Doc/whatsnew/3.15.rst | 4 ++++ Lib/test/test_exceptions.py | 1 + .../2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 3 ++- Objects/exceptions.c | 5 ++++- 4 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index fe3d45b83a512e..c7e707e5d0dc32 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -200,6 +200,10 @@ Other language changes * Several error messages incorrectly using the term "argument" have been corrected. (Contributed by Stan Ulbrych in :gh:`133382`.) +* The :meth:`repr` of :class:`ImportError` and :class:`ModuleNotFoundError` now + contains attributes name and path. + (Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.) + New modules =========== diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index b0ecf6de4dccec..92220925641a34 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2640,5 +2640,6 @@ def test_ModuleNotFoundError_repr_with_failed_import(self): self.assertEqual(repr(cm.exception), "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") + if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst index d5fef8dc6e1c0b..6f9715987e3fac 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst @@ -1,2 +1,3 @@ -:meth:`repr` of :class:`ImportError` now contains attributes name and path. +The :meth:`repr` of :class:`ImportError` and :class:`ModuleNotFoundError` now +contains attributes name and path. Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 4158d94262bc1f..a2e26174282f23 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1874,6 +1874,9 @@ ImportError_repr(PyObject *self) goto error; } PyObject *r = BaseException_repr(self); + if (r == NULL) { + goto error; + } if (PyUnicodeWriter_WriteSubstring( writer, r, 0, PyUnicode_GET_LENGTH(r) - 1) < 0) { @@ -1903,7 +1906,7 @@ ImportError_repr(PyObject *self) } } - if (PyUnicodeWriter_WriteASCII(writer, ")", 1) < 0) { + if (PyUnicodeWriter_WriteChar(writer, ")") < 0) { goto error; } From 25f3d429f3cb1275da2dc5a6dfa2c4ba11468a26 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sun, 20 Jul 2025 10:15:34 +0200 Subject: [PATCH 23/26] fix chr c input --- Objects/exceptions.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index a2e26174282f23..c41a8a0b37037f 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1906,7 +1906,7 @@ ImportError_repr(PyObject *self) } } - if (PyUnicodeWriter_WriteChar(writer, ")") < 0) { + if (PyUnicodeWriter_WriteChar(writer, ')') < 0) { goto error; } From a45ed7561d129998fb41964b454a97f9939b27f1 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sun, 20 Jul 2025 11:16:21 +0200 Subject: [PATCH 24/26] move tests, edit rst wording --- Doc/whatsnew/3.15.rst | 5 +- Lib/test/test_exceptions.py | 89 +++++++++---------- ...5-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 7 +- 3 files changed, 51 insertions(+), 50 deletions(-) diff --git a/Doc/whatsnew/3.15.rst b/Doc/whatsnew/3.15.rst index 116b0c64d8d234..736a8fb20e52fb 100644 --- a/Doc/whatsnew/3.15.rst +++ b/Doc/whatsnew/3.15.rst @@ -200,8 +200,9 @@ Other language changes * Several error messages incorrectly using the term "argument" have been corrected. (Contributed by Stan Ulbrych in :gh:`133382`.) -* The :meth:`repr` of :class:`ImportError` and :class:`ModuleNotFoundError` now - contains attributes name and path. +* The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError` + now shows "name" and "path" as ``name=`` and ``path=`` if they were given + as keyword arguments at construction time. (Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.) diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 92220925641a34..59f77f91d85e5c 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -2079,6 +2079,50 @@ def test_copy_pickle(self): self.assertEqual(exc.name, orig.name) self.assertEqual(exc.path, orig.path) + def test_repr(self): + exc = ImportError() + self.assertEqual(repr(exc), "ImportError()") + + exc = ImportError('test') + self.assertEqual(repr(exc), "ImportError('test')") + + exc = ImportError('test', 'case') + self.assertEqual(repr(exc), "ImportError('test', 'case')") + + exc = ImportError(name='somemodule') + self.assertEqual(repr(exc), "ImportError(name='somemodule')") + + exc = ImportError('test', name='somemodule') + self.assertEqual(repr(exc), "ImportError('test', name='somemodule')") + + exc = ImportError(path='somepath') + self.assertEqual(repr(exc), "ImportError(path='somepath')") + + exc = ImportError('test', path='somepath') + self.assertEqual(repr(exc), "ImportError('test', path='somepath')") + + exc = ImportError(name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError(name='somename', path='somepath')") + + exc = ImportError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ImportError('test', name='somename', path='somepath')") + + exc = ModuleNotFoundError('test', name='somename', path='somepath') + self.assertEqual(repr(exc), + "ModuleNotFoundError('test', name='somename', path='somepath')") + + def test_ModuleNotFoundError_repr_with_failed_import(self): + with self.assertRaises(ModuleNotFoundError) as cm: + import does_not_exist # type: ignore[import] # noqa: F401 + + self.assertEqual(cm.exception.name, "does_not_exist") + self.assertIsNone(cm.exception.path) + + self.assertEqual(repr(cm.exception), + "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") + def run_script(source): if isinstance(source, str): @@ -2596,50 +2640,5 @@ def after_with(): 1/0 self.lineno_after_raise(after_with, 1, 1) - def test_repr(self): - exc = ImportError() - self.assertEqual(repr(exc), "ImportError()") - - exc = ImportError('test') - self.assertEqual(repr(exc), "ImportError('test')") - - exc = ImportError('test', 'case') - self.assertEqual(repr(exc), "ImportError('test', 'case')") - - exc = ImportError(name='somemodule') - self.assertEqual(repr(exc), "ImportError(name='somemodule')") - - exc = ImportError('test', name='somemodule') - self.assertEqual(repr(exc), "ImportError('test', name='somemodule')") - - exc = ImportError(path='somepath') - self.assertEqual(repr(exc), "ImportError(path='somepath')") - - exc = ImportError('test', path='somepath') - self.assertEqual(repr(exc), "ImportError('test', path='somepath')") - - exc = ImportError(name='somename', path='somepath') - self.assertEqual(repr(exc), - "ImportError(name='somename', path='somepath')") - - exc = ImportError('test', name='somename', path='somepath') - self.assertEqual(repr(exc), - "ImportError('test', name='somename', path='somepath')") - - exc = ModuleNotFoundError('test', name='somename', path='somepath') - self.assertEqual(repr(exc), - "ModuleNotFoundError('test', name='somename', path='somepath')") - - def test_ModuleNotFoundError_repr_with_failed_import(self): - with self.assertRaises(ModuleNotFoundError) as cm: - import does_not_exist # type: ignore[import] # noqa: F401 - - self.assertEqual(cm.exception.name, "does_not_exist") - self.assertIsNone(cm.exception.path) - - self.assertEqual(repr(cm.exception), - "ModuleNotFoundError(\"No module named 'does_not_exist'\", name='does_not_exist')") - - if __name__ == '__main__': unittest.main() diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst index 6f9715987e3fac..128ea4049b746b 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst @@ -1,3 +1,4 @@ -The :meth:`repr` of :class:`ImportError` and :class:`ModuleNotFoundError` now -contains attributes name and path. -Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir +The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError` +now shows "name" and "path" as ``name=`` and ``path=`` if they were given +as keyword arguments at construction time. +(Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.) From 9c09cd03a51352d3b0df7fe3f015c9a6c81cfce5 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sun, 20 Jul 2025 11:21:40 +0200 Subject: [PATCH 25/26] edit blurb --- .../2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst index 128ea4049b746b..9f69a2a5c0a354 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst @@ -1,4 +1,4 @@ The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError` now shows "name" and "path" as ``name=`` and ``path=`` if they were given as keyword arguments at construction time. -(Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir in :gh:`74185`.) +(Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir.) From ca0e2a4d1201bf83718aedd2c3b8ba7fe42d9705 Mon Sep 17 00:00:00 2001 From: ynir3 Date: Sun, 20 Jul 2025 11:45:52 +0200 Subject: [PATCH 26/26] edit blurb --- .../2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst index 9f69a2a5c0a354..d149e7b2878574 100644 --- a/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst +++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-07-19-10-35-31.gh-issue-74185.7hPCA5.rst @@ -1,4 +1,4 @@ The :meth:`~object.__repr__` of :class:`ImportError` and :class:`ModuleNotFoundError` now shows "name" and "path" as ``name=`` and ``path=`` if they were given as keyword arguments at construction time. -(Contributed by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir.) +Patch by Serhiy Storchaka, Oleg Iarygin, and Yoav Nir pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy