Skip to content
Open
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
15 changes: 14 additions & 1 deletion Doc/library/typing.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,8 @@ without the dedicated syntax, as documented below.
.. versionadded:: 3.10


.. class:: TypeAliasType(name, value, *, type_params=(), qualname=None)
.. class:: TypeAliasType(name, value, *, type_params=(), qualname=None, \
module=None)

The type of type aliases created through the :keyword:`type` statement.

Expand All @@ -2327,8 +2328,20 @@ without the dedicated syntax, as documented below.
>>> type(Alias)
<class 'typing.TypeAliasType'>

When *module* is given, it is used as the new alias's
:attr:`~TypeAliasType.__module__`. When not given (or when ``None`` is
passed explicitly), the module is inferred from the calling frame's
globals. Inference can yield ``None`` when no ``__name__`` is in scope
(e.g. inside :func:`exec` with a fresh globals dict), or it can yield
an unwanted module name when an alias is built inside a factory helper
or generated code. Pass *module* explicitly when the inferred module
would be wrong or unavailable.

.. versionadded:: 3.12

.. versionchanged:: 3.15
Added the *module* parameter.

.. attribute:: __name__

The name of the type alias:
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_type_aliases.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,20 @@ def test_keywords(self):
self.assertEqual(TA.__type_params__, ())
self.assertEqual(TA.__module__, __name__)

def test_with_module(self):
TA = TypeAliasType("TA", int, module="my.custom.pkg")
self.assertEqual(TA.__module__, "my.custom.pkg")

def test_with_module_with_exec(self):
ns = {}
exec(
"from typing import TypeAliasType\n"
"TA = TypeAliasType('TA', int, module='x.y')",
ns,
ns,
)
self.assertEqual(ns["TA"].__module__, "x.y")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please, test repr with the custom module, we want to be sure that there will no regression there.


def test_errors(self):
with self.assertRaises(TypeError):
TypeAliasType()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Add an optional keyword-only ``module`` argument to :class:`typing.TypeAliasType`.
When supplied, it overrides the module inferred from the calling frame, which
can yield ``None`` (e.g. inside :func:`exec` with a fresh globals dict) or
an unwanted module (e.g. inside factory helpers). Mirrors the long-standing
``module`` argument of :class:`enum.Enum`'s functional API.
27 changes: 18 additions & 9 deletions Objects/clinic/typevarobject.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 14 additions & 7 deletions Objects/typevarobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -2149,14 +2149,16 @@ typealias.__new__ as typealias_new
*
type_params: object = NULL
qualname: object(c_default="NULL") = None
module: object(c_default="NULL") = None

Create a TypeAliasType.
[clinic start generated code]*/

static PyObject *
typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value,
PyObject *type_params, PyObject *qualname)
/*[clinic end generated code: output=b7f6d9f1c577cd9c input=cbec290f8c4886ef]*/
PyObject *type_params, PyObject *qualname,
PyObject *module)
/*[clinic end generated code: output=263bf69bc54213c5 input=229c9f63109a44b9]*/
{
if (type_params != NULL && !PyTuple_Check(type_params)) {
PyErr_SetString(PyExc_TypeError, "type_params must be a tuple");
Expand All @@ -2179,14 +2181,19 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value,
}
}

PyObject *module = caller();
if (module == NULL) {
return NULL;
PyObject *resolved_module;
if (module == NULL || module == Py_None) {
resolved_module = caller();
if (resolved_module == NULL) {
return NULL;
}
} else {
resolved_module = Py_NewRef(module);
}

PyObject *ta = (PyObject *)typealias_alloc(
name, qualname, checked_params, NULL, value, module);
Py_DECREF(module);
name, qualname, checked_params, NULL, value, resolved_module);
Py_DECREF(resolved_module);
return ta;
}

Expand Down
Loading