Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 138
Add __orig_bases__ to TypedDict#150
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
Uh oh!
There was an error while loading. Please reload this page.
Changes from all commits
c3a9e552e59a70c94b79dcdcccdded6d0cf2feff1e42606d66365ab1229df6eFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading. Please reload this page.
Jump to
Uh oh!
There was an error while loading. Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -63,13 +63,6 @@ def assertNotIsSubclass(self, cls, class_or_tuple, msg=None): | ||
| message += f' : {msg}' | ||
| raise self.failureException(message) | ||
| @contextlib.contextmanager | ||
| def assertWarnsIf(self, condition: bool, expected_warning: Type[Warning]): | ||
| with contextlib.ExitStack() as stack: | ||
| if condition: | ||
| stack.enter_context(self.assertWarns(expected_warning)) | ||
| yield | ||
| class Employee: | ||
| pass | ||
| @@ -2260,7 +2253,7 @@ def test_basics_iterable_syntax(self): | ||
| self.assertEqual(Emp.__total__, True) | ||
| def test_basics_keywords_syntax(self): | ||
| with self.assertWarnsIf(sys.version_info >= (3, 11), DeprecationWarning): | ||
| with self.assertWarns(DeprecationWarning): | ||
JelleZijlstra marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| Emp = TypedDict('Emp', name=str, id=int) | ||
| self.assertIsSubclass(Emp, dict) | ||
| self.assertIsSubclass(Emp, typing.MutableMapping) | ||
| @@ -2276,7 +2269,7 @@ def test_basics_keywords_syntax(self): | ||
| self.assertEqual(Emp.__total__, True) | ||
| def test_typeddict_special_keyword_names(self): | ||
| with self.assertWarnsIf(sys.version_info >= (3, 11), DeprecationWarning): | ||
| with self.assertWarns(DeprecationWarning): | ||
| TD = TypedDict("TD", cls=type, self=object, typename=str, _typename=int, | ||
| fields=list, _fields=dict) | ||
| self.assertEqual(TD.__name__, 'TD') | ||
| @@ -2312,7 +2305,7 @@ def test_typeddict_create_errors(self): | ||
| def test_typeddict_errors(self): | ||
| Emp = TypedDict('Emp', {'name': str, 'id': int}) | ||
| if hasattr(typing, "Required"): | ||
| if sys.version_info >= (3, 12): | ||
| self.assertEqual(TypedDict.__module__, 'typing') | ||
| else: | ||
| self.assertEqual(TypedDict.__module__, 'typing_extensions') | ||
| @@ -2325,7 +2318,7 @@ def test_typeddict_errors(self): | ||
| issubclass(dict, Emp) | ||
| if not TYPING_3_11_0: | ||
| with self.assertRaises(TypeError): | ||
| with self.assertRaises(TypeError), self.assertWarns(DeprecationWarning): | ||
| TypedDict('Hi', x=1) | ||
| with self.assertRaises(TypeError): | ||
| TypedDict('Hi', [('x', int), ('y', 1)]) | ||
| @@ -2829,6 +2822,49 @@ def test_get_type_hints_typeddict(self): | ||
| 'year': NotRequired[Annotated[int, 2000]], | ||
| } | ||
| 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,)) | ||
JelleZijlstra marked this conversation as resolved.
Uh oh!There was an error while loading. Please reload this page. | ||
| class TypeAliasTests(BaseTestCase): | ||
| def test_canonical_usage_with_variable_annotation(self): | ||
| @@ -3595,22 +3631,23 @@ def test_typing_extensions_defers_when_possible(self): | ||
| 'overload', | ||
| 'ParamSpec', | ||
| 'Text', | ||
| 'TypedDict', | ||
| 'TypeVar', | ||
| 'TypeVarTuple', | ||
| 'TYPE_CHECKING', | ||
| 'Final', | ||
| 'get_type_hints', | ||
| 'is_typeddict', | ||
| } | ||
| if sys.version_info < (3, 10): | ||
| exclude |= {'get_args', 'get_origin'} | ||
| if sys.version_info < (3, 10, 1): | ||
| exclude |= {"Literal"} | ||
| if sys.version_info < (3, 11): | ||
| exclude |= {'final', 'NamedTuple', 'Any'} | ||
| exclude |= {'final', 'Any'} | ||
| if sys.version_info < (3, 12): | ||
| exclude |= {'Protocol', 'runtime_checkable', 'SupportsIndex'} | ||
| exclude |= { | ||
| 'Protocol', 'runtime_checkable', 'SupportsIndex', 'TypedDict', | ||
| 'is_typeddict', 'NamedTuple', | ||
| } | ||
| for item in typing_extensions.__all__: | ||
| if item not in exclude and hasattr(typing, item): | ||
| self.assertIs( | ||
| @@ -3656,7 +3693,6 @@ def __add__(self, other): | ||
| return 0 | ||
| @skipIf(TYPING_3_11_0, "These invariants should all be tested upstream on 3.11+") | ||
| class NamedTupleTests(BaseTestCase): | ||
| class NestedEmployee(NamedTuple): | ||
| name: str | ||
| @@ -3796,7 +3832,9 @@ class Y(Generic[T], NamedTuple): | ||
| self.assertIs(type(a), G) | ||
| self.assertEqual(a.x, 3) | ||
| with self.assertRaisesRegex(TypeError, 'Too many parameters'): | ||
| things = "arguments" if sys.version_info >= (3, 11) else "parameters" | ||
| with self.assertRaisesRegex(TypeError, f'Too many {things}'): | ||
| G[int, str] | ||
| @skipUnless(TYPING_3_9_0, "tuple.__class_getitem__ was added in 3.9") | ||
| @@ -3927,6 +3965,22 @@ def test_same_as_typing_NamedTuple_38_minus(self): | ||
| self.NestedEmployee._field_types | ||
| ) | ||
| 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 TypeVarLikeDefaultsTests(BaseTestCase): | ||
| def test_typevar(self): | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -749,14 +749,16 @@ def __index__(self) -> int: | ||||||||||||||
| pass | ||||||||||||||
| if hasattr(typing, "Required"): | ||||||||||||||
| if sys.version_info >= (3, 12): | ||||||||||||||
| # The standard library TypedDict in Python 3.8 does not store runtime information | ||||||||||||||
| # about which (if any) keys are optional. See https://bugs.python.org/issue38834 | ||||||||||||||
| # The standard library TypedDict in Python 3.9.0/1 does not honour the "total" | ||||||||||||||
| # keyword with old-style TypedDict(). See https://bugs.python.org/issue42059 | ||||||||||||||
| # The standard library TypedDict below Python 3.11 does not store runtime | ||||||||||||||
| # information about optional and required keys when using Required or NotRequired. | ||||||||||||||
| # Generic TypedDicts are also impossible using typing.TypedDict on Python <3.11. | ||||||||||||||
| # Aaaand on 3.12 we add __orig_bases__ to TypedDict | ||||||||||||||
| # to enable better runtime introspection. | ||||||||||||||
| TypedDict = typing.TypedDict | ||||||||||||||
| _TypedDictMeta = typing._TypedDictMeta | ||||||||||||||
| is_typeddict = typing.is_typeddict | ||||||||||||||
| @@ -786,7 +788,6 @@ def _typeddict_new(*args, total=True, **kwargs): | ||||||||||||||
| typename, args = args[0], args[1:] # allow the "_typename" keyword be passed | ||||||||||||||
| elif '_typename' in kwargs: | ||||||||||||||
| typename = kwargs.pop('_typename') | ||||||||||||||
| import warnings | ||||||||||||||
| warnings.warn("Passing '_typename' as keyword argument is deprecated", | ||||||||||||||
| DeprecationWarning, stacklevel=2) | ||||||||||||||
| else: | ||||||||||||||
| @@ -801,7 +802,6 @@ def _typeddict_new(*args, total=True, **kwargs): | ||||||||||||||
| 'were given') | ||||||||||||||
| elif '_fields' in kwargs and len(kwargs) == 1: | ||||||||||||||
| fields = kwargs.pop('_fields') | ||||||||||||||
| import warnings | ||||||||||||||
| warnings.warn("Passing '_fields' as keyword argument is deprecated", | ||||||||||||||
| DeprecationWarning, stacklevel=2) | ||||||||||||||
| else: | ||||||||||||||
| @@ -813,6 +813,15 @@ def _typeddict_new(*args, total=True, **kwargs): | ||||||||||||||
| raise TypeError("TypedDict takes either a dict or keyword arguments," | ||||||||||||||
| " but not both") | ||||||||||||||
| if kwargs: | ||||||||||||||
| warnings.warn( | ||||||||||||||
| ||||||||||||||
| importwarnings | |
| warnings.warn("Passing '_typename' as keyword argument is deprecated", | |
| DeprecationWarning, stacklevel=2) |
typing_extensions/src/typing_extensions.py
Lines 804 to 806 in fb37b2e
| importwarnings | |
| warnings.warn("Passing '_fields' as keyword argument is deprecated", | |
| DeprecationWarning, stacklevel=2) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think eternal deprecation warnings might be the right answer, actually. (Or eternal until we ever make typing-extensions 5 in the distant uncertain future.)
AlexWaygoodApr 23, 2023 •
edited
Loading Uh oh!
There was an error while loading. Please reload this page.
edited
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So maybe we do want the change in #150 (comment)? ;)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, let's do that. I commented there because the change looked unrelated to this PR.
Uh oh!
There was an error while loading. Please reload this page.