diff --git a/Lib/test/test_type_aliases.py b/Lib/test/test_type_aliases.py index 56d150d67afb6f..c43499609aaa56 100644 --- a/Lib/test/test_type_aliases.py +++ b/Lib/test/test_type_aliases.py @@ -159,6 +159,15 @@ def test_basic(self): self.assertEqual(TA.__type_params__, ()) self.assertEqual(TA.__module__, __name__) + def test_attributes_with_exec(self): + ns = {} + exec("type TA = int", ns, ns) + TA = ns["TA"] + self.assertEqual(TA.__name__, "TA") + self.assertIs(TA.__value__, int) + self.assertEqual(TA.__type_params__, ()) + self.assertIs(TA.__module__, None) + def test_generic(self): T = TypeVar("T") TA = TypeAliasType("TA", list[T], type_params=(T,)) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index 098933b7cb434f..ae924a2192b265 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -373,6 +373,20 @@ def test_basic_plain(self): self.assertIs(T.__covariant__, False) self.assertIs(T.__contravariant__, False) self.assertIs(T.__infer_variance__, False) + self.assertEqual(T.__module__, __name__) + + def test_basic_with_exec(self): + ns = {} + exec('from typing import TypeVar; T = TypeVar("T", bound=float)', ns, ns) + T = ns['T'] + self.assertIsInstance(T, TypeVar) + self.assertEqual(T.__name__, 'T') + self.assertEqual(T.__constraints__, ()) + self.assertIs(T.__bound__, float) + self.assertIs(T.__covariant__, False) + self.assertIs(T.__contravariant__, False) + self.assertIs(T.__infer_variance__, False) + self.assertIs(T.__module__, None) def test_attributes(self): T_bound = TypeVar('T_bound', bound=int) @@ -939,6 +953,17 @@ def test_name(self): Ts2 = TypeVarTuple('Ts2') self.assertEqual(Ts2.__name__, 'Ts2') + def test_module(self): + Ts = TypeVarTuple('Ts') + self.assertEqual(Ts.__module__, __name__) + + def test_exec(self): + ns = {} + exec('from typing import TypeVarTuple; Ts = TypeVarTuple("Ts")', ns) + Ts = ns['Ts'] + self.assertEqual(Ts.__name__, 'Ts') + self.assertIs(Ts.__module__, None) + def test_instance_is_equal_to_itself(self): Ts = TypeVarTuple('Ts') self.assertEqual(Ts, Ts) @@ -7966,6 +7991,15 @@ def test_basic_plain(self): self.assertEqual(P, P) self.assertIsInstance(P, ParamSpec) self.assertEqual(P.__name__, 'P') + self.assertEqual(P.__module__, __name__) + + def test_basic_with_exec(self): + ns = {} + exec('from typing import ParamSpec; P = ParamSpec("P")', ns, ns) + P = ns['P'] + self.assertIsInstance(P, ParamSpec) + self.assertEqual(P.__name__, 'P') + self.assertIs(P.__module__, None) def test_valid_uses(self): P = ParamSpec('P') diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-05-24-10-19-35.gh-issue-104879.v-29NL.rst b/Misc/NEWS.d/next/Core and Builtins/2023-05-24-10-19-35.gh-issue-104879.v-29NL.rst new file mode 100644 index 00000000000000..235f4180642be6 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-05-24-10-19-35.gh-issue-104879.v-29NL.rst @@ -0,0 +1,2 @@ +Fix crash when accessing the ``__module__`` attribute of type aliases +defined outside a module. Patch by Jelle Zijlstra. diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 6730ebfc064e35..6aa0d8a3bc53be 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -1319,8 +1319,13 @@ typealias_module(PyObject *self, void *unused) return Py_NewRef(ta->module); } if (ta->compute_value != NULL) { - // PyFunction_GetModule() returns a borrowed reference - return Py_NewRef(PyFunction_GetModule(ta->compute_value)); + PyObject* mod = PyFunction_GetModule(ta->compute_value); + if (mod != NULL) { + // PyFunction_GetModule() returns a borrowed reference, + // and it may return NULL (e.g., for functions defined + // in an exec()'ed block). + return Py_NewRef(mod); + } } Py_RETURN_NONE; }