Skip to content
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

gh-104879: Fix TypeAliasType.__module__ in exec() #104881

Merged
merged 1 commit into from
May 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Lib/test/test_type_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,))
Expand Down
34 changes: 34 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests already passed, but adding them here for completeness

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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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')
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix crash when accessing the ``__module__`` attribute of type aliases
defined outside a module. Patch by Jelle Zijlstra.
9 changes: 7 additions & 2 deletions Objects/typevarobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down