Skip to content

GH-103699: Add __orig_bases__ to various typing classes #103698

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

Merged
merged 11 commits into from
Apr 23, 2023
59 changes: 59 additions & 0 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6695,6 +6695,22 @@ def test_copy_and_pickle(self):
self.assertEqual(jane2, jane)
self.assertIsInstance(jane2, cls)

def test_orig_bases(self):
T = TypeVar('T')

class SimpleNamedTuple(NamedTuple):
pass

class GenericNamedTuple(NamedTuple, Generic[T]):
pass

self.assertEqual(SimpleNamedTuple.__orig_bases__, (NamedTuple,))
self.assertEqual(GenericNamedTuple.__orig_bases__, (NamedTuple, Generic[T]))

CallNamedTuple = NamedTuple('CallNamedTuple', [])

self.assertEqual(CallNamedTuple.__orig_bases__, (NamedTuple,))


class TypedDictTests(BaseTestCase):
def test_basics_functional_syntax(self):
Expand Down Expand Up @@ -7126,6 +7142,49 @@ class TD(TypedDict):
self.assertIs(type(a), dict)
self.assertEqual(a, {'a': 1})

def test_orig_bases(self):
T = TypeVar('T')

class Parent(TypedDict):
pass

class Child(Parent):
pass

class OtherChild(Parent):
pass

class MixedChild(Child, OtherChild, Parent):
pass

class GenericParent(TypedDict, Generic[T]):
pass

class GenericChild(GenericParent[int]):
pass

class OtherGenericChild(GenericParent[str]):
pass

class MixedGenericChild(GenericChild, OtherGenericChild, GenericParent[float]):
pass

class MultipleGenericBases(GenericParent[int], GenericParent[float]):
pass

CallTypedDict = TypedDict('CallTypedDict', {})

self.assertEqual(Parent.__orig_bases__, (TypedDict,))
self.assertEqual(Child.__orig_bases__, (Parent,))
self.assertEqual(OtherChild.__orig_bases__, (Parent,))
self.assertEqual(MixedChild.__orig_bases__, (Child, OtherChild, Parent,))
self.assertEqual(GenericParent.__orig_bases__, (TypedDict, Generic[T]))
self.assertEqual(GenericChild.__orig_bases__, (GenericParent[int],))
self.assertEqual(OtherGenericChild.__orig_bases__, (GenericParent[str],))
self.assertEqual(MixedGenericChild.__orig_bases__, (GenericChild, OtherGenericChild, GenericParent[float]))
self.assertEqual(MultipleGenericBases.__orig_bases__, (GenericParent[int], GenericParent[float]))
self.assertEqual(CallTypedDict.__orig_bases__, (TypedDict,))


class RequiredTests(BaseTestCase):

Expand Down
11 changes: 9 additions & 2 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -2962,7 +2962,9 @@ class Employee(NamedTuple):
elif kwargs:
raise TypeError("Either list of fields or keywords"
" can be provided to NamedTuple, not both")
return _make_nmtuple(typename, fields, module=_caller())
nt = _make_nmtuple(typename, fields, module=_caller())
nt.__orig_bases__ = (NamedTuple,)
return nt

_NamedTuple = type.__new__(NamedTupleMeta, 'NamedTuple', (), {})

Expand Down Expand Up @@ -2994,6 +2996,9 @@ def __new__(cls, name, bases, ns, total=True):

tp_dict = type.__new__(_TypedDictMeta, name, (*generic_base, dict), ns)

if not hasattr(tp_dict, '__orig_bases__'):
tp_dict.__orig_bases__ = bases

annotations = {}
own_annotations = ns.get('__annotations__', {})
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
Expand Down Expand Up @@ -3104,7 +3109,9 @@ class body be required.
# Setting correct module is necessary to make typed dict classes pickleable.
ns['__module__'] = module

return _TypedDictMeta(typename, (), ns, total=total)
td = _TypedDictMeta(typename, (), ns, total=total)
td.__orig_bases__ = (TypedDict,)
return td

_TypedDict = type.__new__(_TypedDictMeta, 'TypedDict', (), {})
TypedDict.__mro_entries__ = lambda bases: (_TypedDict,)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add ``__orig_bases__`` to non-generic TypedDicts, call-based TypedDicts, and
call-based NamedTuples. Other TypedDicts and NamedTuples already had the attribute.