From 9e6fd0d2c640e2825e9fea3135c690839259929b Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Thu, 8 Feb 2024 10:24:55 +0300 Subject: [PATCH 1/6] gh-82062: correctly set module for built-in instance methods in inspect.signature() The ``__module__`` attribute is missing for instance methods in extension modules, so instead we use in this case ``__objclass__.__module__``. --- Lib/inspect.py | 4 +++- Lib/test/test_capi/test_misc.py | 7 +++++++ .../2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst | 2 ++ Modules/_testcapi/docstring.c | 10 ++++++++++ 4 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst diff --git a/Lib/inspect.py b/Lib/inspect.py index f0b72662a9a0b2..c405b4995cde10 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2267,7 +2267,9 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True): module = None module_dict = {} - module_name = getattr(obj, '__module__', None) + module_name = (getattr(obj, '__module__', None) or + getattr(getattr(obj, '__objclass__', None), + '__module__', None)) if module_name: module = sys.modules.get(module_name, None) if module: diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 67fbef4f269814..738b5d6c991583 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -6,6 +6,7 @@ import contextlib import importlib.machinery import importlib.util +import inspect import json import os import pickle @@ -180,6 +181,12 @@ def test_docstring_signature_parsing(self): self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, "($module, /, parameter)") + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_parsing_with_defaults(self): + meth = _testcapi.DocStringUnrepresentableSignatureTest.with_default + self.assertEqual(str(inspect.signature(meth)), '(self, /, x=1)') + def test_c_type_with_matrix_multiplication(self): M = _testcapi.matmulType m1 = M() diff --git a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst new file mode 100644 index 00000000000000..b93de45b1d66a1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst @@ -0,0 +1,2 @@ +Fix :func:`inspect.signature()` to correctly set extension module for +built-in instance methods. diff --git a/Modules/_testcapi/docstring.c b/Modules/_testcapi/docstring.c index d99fbdd904b594..3f7acbae1b181b 100644 --- a/Modules/_testcapi/docstring.c +++ b/Modules/_testcapi/docstring.c @@ -169,6 +169,13 @@ static PyMethodDef DocStringUnrepresentableSignatureTest_methods[] = { "--\n\n" "This docstring has a signature with unrepresentable default." )}, + {"with_default", + (PyCFunction)test_with_docstring, METH_VARARGS, + PyDoc_STR( + "with_default($self, /, x=ONE)\n" + "--\n\n" + "This instance method has a default parameter value from the module scope." + )}, {NULL}, }; @@ -193,5 +200,8 @@ _PyTestCapi_Init_Docstring(PyObject *mod) if (PyModule_AddType(mod, &DocStringUnrepresentableSignatureTest) < 0) { return -1; } + if (PyModule_AddObject(mod, "ONE", PyLong_FromLong(1)) < 0) { + return -1; + } return 0; } From 261fda3c79e36ce8bf41e852dcaa17c24964bb24 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 30 Apr 2024 16:13:43 +0300 Subject: [PATCH 2/6] Address review: split statement, that fill module_name --- Lib/inspect.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Lib/inspect.py b/Lib/inspect.py index 41a3d88a3187a2..a0c80bd5c8b601 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2285,9 +2285,12 @@ def _signature_fromstr(cls, obj, s, skip_bound_arg=True): module = None module_dict = {} - module_name = (getattr(obj, '__module__', None) or - getattr(getattr(obj, '__objclass__', None), - '__module__', None)) + + module_name = getattr(obj, '__module__', None) + if not module_name: + objclass = getattr(obj, '__objclass__', None) + module_name = getattr(objclass, '__module__', None) + if module_name: module = sys.modules.get(module_name, None) if module: From 62faa574da43d38108e748105a0da141c8c9052a Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 30 Apr 2024 16:17:30 +0300 Subject: [PATCH 3/6] Address review: expand news entry --- .../Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst index b93de45b1d66a1..312b76edafd006 100644 --- a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst +++ b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst @@ -1,2 +1,3 @@ -Fix :func:`inspect.signature()` to correctly set extension module for -built-in instance methods. +Fix :func:`inspect.signature()` to correctly set extension module for built-in +instance methods. They lacks ``__module__`` attribute, so we use instead +``__objclass__.__module__`` attribute. From af3532792bb7c910b90875260b0b82b24b7c6f00 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 30 Apr 2024 16:45:46 +0300 Subject: [PATCH 4/6] address review: move test --- Lib/test/test_capi/test_misc.py | 7 ------- Lib/test/test_inspect/test_inspect.py | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Lib/test/test_capi/test_misc.py b/Lib/test/test_capi/test_misc.py index 7a40d8629709ab..020e8493e57c0c 100644 --- a/Lib/test/test_capi/test_misc.py +++ b/Lib/test/test_capi/test_misc.py @@ -6,7 +6,6 @@ import contextlib import importlib.machinery import importlib.util -import inspect import json import os import pickle @@ -184,12 +183,6 @@ def test_docstring_signature_parsing(self): self.assertEqual(_testcapi.docstring_with_signature_and_extra_newlines.__text_signature__, "($module, /, parameter)") - @unittest.skipIf(MISSING_C_DOCSTRINGS, - "Signature information for builtins requires docstrings") - def test_signature_parsing_with_defaults(self): - meth = _testcapi.DocStringUnrepresentableSignatureTest.with_default - self.assertEqual(str(inspect.signature(meth)), '(self, /, x=1)') - def test_c_type_with_matrix_multiplication(self): M = _testcapi.matmulType m1 = M() diff --git a/Lib/test/test_inspect/test_inspect.py b/Lib/test/test_inspect/test_inspect.py index fbef34eddacb3a..d12240353ff832 100644 --- a/Lib/test/test_inspect/test_inspect.py +++ b/Lib/test/test_inspect/test_inspect.py @@ -3069,6 +3069,13 @@ def test_signature_on_builtins_no_signature(self): self.assertEqual(inspect.signature(builtin), inspect.signature(template)) + @unittest.skipIf(MISSING_C_DOCSTRINGS, + "Signature information for builtins requires docstrings") + def test_signature_parsing_with_defaults(self): + _testcapi = import_helper.import_module("_testcapi") + meth = _testcapi.DocStringUnrepresentableSignatureTest.with_default + self.assertEqual(str(inspect.signature(meth)), '(self, /, x=1)') + def test_signature_on_non_function(self): with self.assertRaisesRegex(TypeError, 'is not a callable object'): inspect.signature(42) From ce4dbf476399c564e09a42838a3cdf6de57f9cd2 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 30 Apr 2024 18:21:25 +0300 Subject: [PATCH 5/6] Address review: redo news --- .../Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst index 312b76edafd006..543d117ad79b3d 100644 --- a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst +++ b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst @@ -1,3 +1,3 @@ -Fix :func:`inspect.signature()` to correctly set extension module for built-in -instance methods. They lacks ``__module__`` attribute, so we use instead -``__objclass__.__module__`` attribute. +Fix :func:`inspect.signature()` to correctly handle globals in extension +modules. Now signatures are correct if globals are use to define default +values in extension functions and/or methods. From b5e83fa6d6fccbea6f4228aaf00b539d6e715bb5 Mon Sep 17 00:00:00 2001 From: Sergey B Kirpichev Date: Tue, 30 Apr 2024 19:24:54 +0300 Subject: [PATCH 6/6] Update Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst Co-authored-by: Jelle Zijlstra --- .../Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst index 543d117ad79b3d..a57a5918b135bb 100644 --- a/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst +++ b/Misc/NEWS.d/next/Library/2024-02-11-07-31-43.gh-issue-82062.eeS6w7.rst @@ -1,3 +1,3 @@ -Fix :func:`inspect.signature()` to correctly handle globals in extension -modules. Now signatures are correct if globals are use to define default -values in extension functions and/or methods. +Fix :func:`inspect.signature()` to correctly handle parameter defaults +on methods in extension modules that use names defined in the module +namespace.