partially patch Lib/typing to 3.14

This commit is contained in:
Jeong, YunWon
2026-01-15 22:40:22 +09:00
parent 96038e48c5
commit 346481d95e

85
Lib/typing.py vendored
View File

@@ -161,7 +161,17 @@ __all__ = [
]
def _type_convert(arg, module=None, *, allow_special_forms=False):
class _LazyAnnotationLib:
def __getattr__(self, attr):
global _lazy_annotationlib
import annotationlib
_lazy_annotationlib = annotationlib
return getattr(annotationlib, attr)
_lazy_annotationlib = _LazyAnnotationLib()
def _type_convert(arg, module=None, *, allow_special_forms=False, owner=None):
"""For converting None to type(None), and strings to ForwardRef."""
if arg is None:
return type(None)
@@ -170,7 +180,7 @@ def _type_convert(arg, module=None, *, allow_special_forms=False):
return arg
def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False):
def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=False, owner=None):
"""Check that the argument is a type, and return it (internal helper).
As a special case, accept None and return type(None) instead. Also wrap strings
@@ -188,7 +198,7 @@ def _type_check(arg, msg, is_argument=True, module=None, *, allow_special_forms=
if is_argument:
invalid_generic_forms += (Final,)
arg = _type_convert(arg, module=module, allow_special_forms=allow_special_forms)
arg = _type_convert(arg, module=module, allow_special_forms=allow_special_forms, owner=owner)
if (isinstance(arg, _GenericAlias) and
arg.__origin__ in invalid_generic_forms):
raise TypeError(f"{arg} is not valid as type argument")
@@ -2443,7 +2453,7 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
else:
base_globals = globalns
ann = base.__dict__.get('__annotations__', {})
ann = _lazy_annotationlib.get_annotations(base)
if isinstance(ann, types.GetSetDescriptorType):
ann = {}
base_locals = dict(vars(base)) if localns is None else localns
@@ -2477,7 +2487,10 @@ def get_type_hints(obj, globalns=None, localns=None, include_extras=False):
localns = globalns
elif localns is None:
localns = globalns
hints = getattr(obj, '__annotations__', None)
try:
hints = _lazy_annotationlib.get_annotations(obj)
except TypeError:
hints = getattr(obj, '__annotations__', None)
if hints is None:
# Return empty annotations for something that _could_ have them.
if isinstance(obj, _allowed_types):
@@ -3005,7 +3018,13 @@ class NamedTupleMeta(type):
raise TypeError(
'can only inherit from a NamedTuple type and Generic')
bases = tuple(tuple if base is _NamedTuple else base for base in bases)
types = ns.get('__annotations__', {})
if "__annotations__" in ns:
types = ns["__annotations__"]
elif (annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None:
types = _lazy_annotationlib.call_annotate_function(
annotate, _lazy_annotationlib.Format.VALUE)
else:
types = {}
default_names = []
for field_name in types:
if field_name in ns:
@@ -3160,16 +3179,26 @@ class _TypedDictMeta(type):
else:
generic_base = ()
ns_annotations = ns.pop('__annotations__', None)
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__', {})
if ns_annotations is not None:
own_annotate = None
own_annotations = ns_annotations
elif (own_annotate := _lazy_annotationlib.get_annotate_from_class_namespace(ns)) is not None:
own_annotations = _lazy_annotationlib.call_annotate_function(
own_annotate, _lazy_annotationlib.Format.FORWARDREF, owner=tp_dict
)
else:
own_annotate = None
own_annotations = {}
msg = "TypedDict('Name', {f0: t0, f1: t1, ...}); each t must be a type"
own_annotations = {
n: _type_check(tp, msg, module=tp_dict.__module__)
own_checked_annotations = {
n: _type_check(tp, msg, owner=tp_dict, module=tp_dict.__module__)
for n, tp in own_annotations.items()
}
required_keys = set()
@@ -3178,8 +3207,6 @@ class _TypedDictMeta(type):
mutable_keys = set()
for base in bases:
annotations.update(base.__dict__.get('__annotations__', {}))
base_required = base.__dict__.get('__required_keys__', set())
required_keys |= base_required
optional_keys -= base_required
@@ -3191,8 +3218,7 @@ class _TypedDictMeta(type):
readonly_keys.update(base.__dict__.get('__readonly_keys__', ()))
mutable_keys.update(base.__dict__.get('__mutable_keys__', ()))
annotations.update(own_annotations)
for annotation_key, annotation_type in own_annotations.items():
for annotation_key, annotation_type in own_checked_annotations.items():
qualifiers = set(_get_typeddict_qualifiers(annotation_type))
if Required in qualifiers:
is_required = True
@@ -3223,7 +3249,36 @@ class _TypedDictMeta(type):
f"Required keys overlap with optional keys in {name}:"
f" {required_keys=}, {optional_keys=}"
)
tp_dict.__annotations__ = annotations
def __annotate__(format):
annos = {}
for base in bases:
if base is Generic:
continue
base_annotate = base.__annotate__
if base_annotate is None:
continue
base_annos = _lazy_annotationlib.call_annotate_function(
base_annotate, format, owner=base)
annos.update(base_annos)
if own_annotate is not None:
own = _lazy_annotationlib.call_annotate_function(
own_annotate, format, owner=tp_dict)
if format != _lazy_annotationlib.Format.STRING:
own = {
n: _type_check(tp, msg, module=tp_dict.__module__)
for n, tp in own.items()
}
elif format == _lazy_annotationlib.Format.STRING:
own = _lazy_annotationlib.annotations_to_string(own_annotations)
elif format in (_lazy_annotationlib.Format.FORWARDREF, _lazy_annotationlib.Format.VALUE):
own = own_checked_annotations
else:
raise NotImplementedError(format)
annos.update(own)
return annos
tp_dict.__annotate__ = __annotate__
tp_dict.__required_keys__ = frozenset(required_keys)
tp_dict.__optional_keys__ = frozenset(optional_keys)
tp_dict.__readonly_keys__ = frozenset(readonly_keys)