-
-
Notifications
You must be signed in to change notification settings - Fork 120
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
Changes from all commits
c3a9e55
2e59a70
c94b79d
cdcccdd
ed6d0cf
2feff1e
42606d6
6365ab1
229df6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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( | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's important that we copy CPython's behaviour here, and CPython emits a deprecation warning here on 3.11+. This PR means we reimplement TypedDict on 3.11 now, so the change is needed, I think. @JelleZijlstra do you think we should only emit the deprecation warning if the user is running typing_extensions on 3.11+? (Referencing #150 (comment)) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a hard question and gets into what we would do if we ever want to break backwards compatibility for typing-extensions. My first instinct is to say we should always warn in typing-extensions, regardless of the version, but then what would we do when CPython removes support for kwargs-based TypedDicts? I'd be hesitant to remove the runtime behavior in typing-extensions and break backwards compatibility. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree it's a hard question. Elsewhere in the typing_extensions/src/typing_extensions.py Lines 789 to 791 in fb37b2e
typing_extensions/src/typing_extensions.py Lines 804 to 806 in fb37b2e
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.) There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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. |
||||||||||||||
"The kwargs-based syntax for TypedDict definitions is deprecated, " | ||||||||||||||
"may be removed in a future version, and may not be " | ||||||||||||||
"understood by third-party type checkers.", | ||||||||||||||
DeprecationWarning, | ||||||||||||||
stacklevel=2, | ||||||||||||||
) | ||||||||||||||
|
||||||||||||||
ns = {'__annotations__': dict(fields)} | ||||||||||||||
module = _caller() | ||||||||||||||
if module is not None: | ||||||||||||||
|
@@ -844,9 +853,14 @@ def __new__(cls, name, bases, ns, total=True): | |||||||||||||
# Instead, monkey-patch __bases__ onto the class after it's been created. | ||||||||||||||
tp_dict = super().__new__(cls, name, (dict,), ns) | ||||||||||||||
|
||||||||||||||
if any(issubclass(base, typing.Generic) for base in bases): | ||||||||||||||
is_generic = any(issubclass(base, typing.Generic) for base in bases) | ||||||||||||||
|
||||||||||||||
if is_generic: | ||||||||||||||
tp_dict.__bases__ = (typing.Generic, dict) | ||||||||||||||
_maybe_adjust_parameters(tp_dict) | ||||||||||||||
else: | ||||||||||||||
# generic TypedDicts get __orig_bases__ from Generic | ||||||||||||||
tp_dict.__orig_bases__ = bases or (TypedDict,) | ||||||||||||||
|
||||||||||||||
annotations = {} | ||||||||||||||
own_annotations = ns.get('__annotations__', {}) | ||||||||||||||
|
@@ -2313,10 +2327,11 @@ def wrapper(*args, **kwargs): | |||||||||||||
typing._check_generic = _check_generic | ||||||||||||||
|
||||||||||||||
|
||||||||||||||
# Backport typing.NamedTuple as it exists in Python 3.11. | ||||||||||||||
# Backport typing.NamedTuple as it exists in Python 3.12. | ||||||||||||||
# In 3.11, the ability to define generic `NamedTuple`s was supported. | ||||||||||||||
# This was explicitly disallowed in 3.9-3.10, and only half-worked in <=3.8. | ||||||||||||||
if sys.version_info >= (3, 11): | ||||||||||||||
# On 3.12, we added __orig_bases__ to call-based NamedTuples | ||||||||||||||
if sys.version_info >= (3, 12): | ||||||||||||||
NamedTuple = typing.NamedTuple | ||||||||||||||
else: | ||||||||||||||
def _make_nmtuple(name, types, module, defaults=()): | ||||||||||||||
|
@@ -2378,7 +2393,9 @@ def NamedTuple(__typename, __fields=None, **kwargs): | |||||||||||||
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.__doc__ = typing.NamedTuple.__doc__ | ||||||||||||||
_NamedTuple = type.__new__(_NamedTupleMeta, 'NamedTuple', (), {}) | ||||||||||||||
|
Uh oh!
There was an error while loading. Please reload this page.