Skip to content

Commit e7c18f1

Browse files
authored
Merge pull request #5850 from youknowone/typing
Upgrade typing to 3.13.5
2 parents c764126 + 3f5566d commit e7c18f1

File tree

4 files changed

+115
-71
lines changed

4 files changed

+115
-71
lines changed

Lib/test/test_typing.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,8 +3351,6 @@ def x(self): ...
33513351
self.assertNotIsSubclass(C, Protocol)
33523352
self.assertNotIsInstance(C(), Protocol)
33533353

3354-
# TODO: RUSTPYTHON
3355-
@unittest.expectedFailure
33563354
def test_protocols_issubclass_non_callable(self):
33573355
class C:
33583356
x = 1
@@ -3412,8 +3410,6 @@ def __init__(self) -> None:
34123410
):
34133411
issubclass(Eggs, Spam)
34143412

3415-
# TODO: RUSTPYTHON
3416-
@unittest.expectedFailure
34173413
def test_no_weird_caching_with_issubclass_after_isinstance_2(self):
34183414
@runtime_checkable
34193415
class Spam(Protocol):
@@ -3434,8 +3430,6 @@ class Eggs: ...
34343430
):
34353431
issubclass(Eggs, Spam)
34363432

3437-
# TODO: RUSTPYTHON
3438-
@unittest.expectedFailure
34393433
def test_no_weird_caching_with_issubclass_after_isinstance_3(self):
34403434
@runtime_checkable
34413435
class Spam(Protocol):
@@ -4091,8 +4085,6 @@ class MyChain(typing.ChainMap[str, T]): ...
40914085
self.assertIs(MyChain[int]().__class__, MyChain)
40924086
self.assertEqual(MyChain[int]().__orig_class__, MyChain[int])
40934087

4094-
# TODO: RUSTPYTHON
4095-
@unittest.expectedFailure
40964088
def test_all_repr_eq_any(self):
40974089
objs = (getattr(typing, el) for el in typing.__all__)
40984090
for obj in objs:
@@ -9591,8 +9583,6 @@ def test_all(self):
95919583
self.assertIn('SupportsBytes', a)
95929584
self.assertIn('SupportsComplex', a)
95939585

9594-
# TODO: RUSTPYTHON
9595-
@unittest.expectedFailure
95969586
def test_all_exported_names(self):
95979587
# ensure all dynamically created objects are actualised
95989588
for name in typing.__all__:

Lib/typing.py

Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,8 @@ def _should_unflatten_callable_args(typ, args):
220220
>>> P = ParamSpec('P')
221221
>>> collections.abc.Callable[[int, int], str].__args__ == (int, int, str)
222222
True
223+
>>> collections.abc.Callable[P, str].__args__ == (P, str)
224+
True
223225
224226
As a result, if we need to reconstruct the Callable from its __args__,
225227
we need to unflatten it.
@@ -263,6 +265,8 @@ def _collect_type_parameters(args, *, enforce_default_ordering: bool = True):
263265
264266
>>> P = ParamSpec('P')
265267
>>> T = TypeVar('T')
268+
>>> _collect_type_parameters((T, Callable[P, T]))
269+
(~T, ~P)
266270
"""
267271
# required type parameter cannot appear after parameter with default
268272
default_encountered = False
@@ -1983,7 +1987,8 @@ def _allow_reckless_class_checks(depth=2):
19831987
The abc and functools modules indiscriminately call isinstance() and
19841988
issubclass() on the whole MRO of a user class, which may contain protocols.
19851989
"""
1986-
return _caller(depth) in {'abc', 'functools', None}
1990+
# XXX: RUSTPYTHON; https://github.com/python/cpython/pull/136115
1991+
return _caller(depth) in {'abc', '_py_abc', 'functools', None}
19871992

19881993

19891994
_PROTO_ALLOWLIST = {
@@ -2090,11 +2095,11 @@ def __subclasscheck__(cls, other):
20902095
and cls.__dict__.get("__subclasshook__") is _proto_hook
20912096
):
20922097
_type_check_issubclass_arg_1(other)
2093-
# non_method_attrs = sorted(cls.__non_callable_proto_members__)
2094-
# raise TypeError(
2095-
# "Protocols with non-method members don't support issubclass()."
2096-
# f" Non-method members: {str(non_method_attrs)[1:-1]}."
2097-
# )
2098+
non_method_attrs = sorted(cls.__non_callable_proto_members__)
2099+
raise TypeError(
2100+
"Protocols with non-method members don't support issubclass()."
2101+
f" Non-method members: {str(non_method_attrs)[1:-1]}."
2102+
)
20982103
return _abc_subclasscheck(cls, other)
20992104

21002105
def __instancecheck__(cls, instance):
@@ -2526,6 +2531,18 @@ def get_origin(tp):
25262531
25272532
This supports generic types, Callable, Tuple, Union, Literal, Final, ClassVar,
25282533
Annotated, and others. Return None for unsupported types.
2534+
2535+
Examples::
2536+
2537+
>>> P = ParamSpec('P')
2538+
>>> assert get_origin(Literal[42]) is Literal
2539+
>>> assert get_origin(int) is None
2540+
>>> assert get_origin(ClassVar[int]) is ClassVar
2541+
>>> assert get_origin(Generic) is Generic
2542+
>>> assert get_origin(Generic[T]) is Generic
2543+
>>> assert get_origin(Union[T, int]) is Union
2544+
>>> assert get_origin(List[Tuple[T, T]][int]) is list
2545+
>>> assert get_origin(P.args) is P
25292546
"""
25302547
if isinstance(tp, _AnnotatedAlias):
25312548
return Annotated
@@ -2548,6 +2565,10 @@ def get_args(tp):
25482565
25492566
>>> T = TypeVar('T')
25502567
>>> assert get_args(Dict[str, int]) == (str, int)
2568+
>>> assert get_args(int) == ()
2569+
>>> assert get_args(Union[int, Union[T, int], str][int]) == (int, str)
2570+
>>> assert get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
2571+
>>> assert get_args(Callable[[], T][int]) == ([], int)
25512572
"""
25522573
if isinstance(tp, _AnnotatedAlias):
25532574
return (tp.__origin__,) + tp.__metadata__
@@ -3225,6 +3246,18 @@ def TypedDict(typename, fields=_sentinel, /, *, total=True):
32253246
associated with a value of a consistent type. This expectation
32263247
is not checked at runtime.
32273248
3249+
Usage::
3250+
3251+
>>> class Point2D(TypedDict):
3252+
... x: int
3253+
... y: int
3254+
... label: str
3255+
...
3256+
>>> a: Point2D = {'x': 1, 'y': 2, 'label': 'good'} # OK
3257+
>>> b: Point2D = {'z': 3, 'label': 'bad'} # Fails type check
3258+
>>> Point2D(x=1, y=2, label='first') == dict(x=1, y=2, label='first')
3259+
True
3260+
32283261
The type info can be accessed via the Point2D.__annotations__ dict, and
32293262
the Point2D.__required_keys__ and Point2D.__optional_keys__ frozensets.
32303263
TypedDict supports an additional equivalent form::
@@ -3680,44 +3713,43 @@ def decorator(cls_or_fn):
36803713
return cls_or_fn
36813714
return decorator
36823715

3683-
# TODO: RUSTPYTHON
3684-
3685-
# type _Func = Callable[..., Any]
3686-
3687-
3688-
# def override[F: _Func](method: F, /) -> F:
3689-
# """Indicate that a method is intended to override a method in a base class.
3690-
#
3691-
# Usage::
3692-
#
3693-
# class Base:
3694-
# def method(self) -> None:
3695-
# pass
3696-
#
3697-
# class Child(Base):
3698-
# @override
3699-
# def method(self) -> None:
3700-
# super().method()
3701-
#
3702-
# When this decorator is applied to a method, the type checker will
3703-
# validate that it overrides a method or attribute with the same name on a
3704-
# base class. This helps prevent bugs that may occur when a base class is
3705-
# changed without an equivalent change to a child class.
3706-
#
3707-
# There is no runtime checking of this property. The decorator attempts to
3708-
# set the ``__override__`` attribute to ``True`` on the decorated object to
3709-
# allow runtime introspection.
3710-
#
3711-
# See PEP 698 for details.
3712-
# """
3713-
# try:
3714-
# method.__override__ = True
3715-
# except (AttributeError, TypeError):
3716-
# # Skip the attribute silently if it is not writable.
3717-
# # AttributeError happens if the object has __slots__ or a
3718-
# # read-only property, TypeError if it's a builtin class.
3719-
# pass
3720-
# return method
3716+
3717+
type _Func = Callable[..., Any]
3718+
3719+
3720+
def override[F: _Func](method: F, /) -> F:
3721+
"""Indicate that a method is intended to override a method in a base class.
3722+
3723+
Usage::
3724+
3725+
class Base:
3726+
def method(self) -> None:
3727+
pass
3728+
3729+
class Child(Base):
3730+
@override
3731+
def method(self) -> None:
3732+
super().method()
3733+
3734+
When this decorator is applied to a method, the type checker will
3735+
validate that it overrides a method or attribute with the same name on a
3736+
base class. This helps prevent bugs that may occur when a base class is
3737+
changed without an equivalent change to a child class.
3738+
3739+
There is no runtime checking of this property. The decorator attempts to
3740+
set the ``__override__`` attribute to ``True`` on the decorated object to
3741+
allow runtime introspection.
3742+
3743+
See PEP 698 for details.
3744+
"""
3745+
try:
3746+
method.__override__ = True
3747+
except (AttributeError, TypeError):
3748+
# Skip the attribute silently if it is not writable.
3749+
# AttributeError happens if the object has __slots__ or a
3750+
# read-only property, TypeError if it's a builtin class.
3751+
pass
3752+
return method
37213753

37223754

37233755
def is_protocol(tp: type, /) -> bool:
@@ -3740,8 +3772,19 @@ def is_protocol(tp: type, /) -> bool:
37403772
and tp != Protocol
37413773
)
37423774

3775+
37433776
def get_protocol_members(tp: type, /) -> frozenset[str]:
37443777
"""Return the set of members defined in a Protocol.
3778+
3779+
Example::
3780+
3781+
>>> from typing import Protocol, get_protocol_members
3782+
>>> class P(Protocol):
3783+
... def a(self) -> str: ...
3784+
... b: int
3785+
>>> get_protocol_members(P) == frozenset({'a', 'b'})
3786+
True
3787+
37453788
Raise a TypeError for arguments that are not Protocols.
37463789
"""
37473790
if not is_protocol(tp):

vm/src/protocol/object.rs

Lines changed: 24 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -535,9 +535,14 @@ impl PyObject {
535535
derived.recursive_issubclass(cls, vm)
536536
}
537537

538+
// _PyObject_RealIsInstance
539+
pub(crate) fn real_is_instance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
540+
self.object_isinstance(cls, vm)
541+
}
542+
538543
/// Real isinstance check without going through __instancecheck__
539544
/// This is equivalent to CPython's _PyObject_RealIsInstance/object_isinstance
540-
pub fn real_is_instance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
545+
fn object_isinstance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
541546
if let Ok(cls) = cls.try_to_ref::<PyType>(vm) {
542547
// PyType_Check(cls) - cls is a type object
543548
let mut retval = self.class().is_subtype(cls);
@@ -576,8 +581,12 @@ impl PyObject {
576581

577582
/// Determines if `self` is an instance of `cls`, either directly, indirectly or virtually via
578583
/// the __instancecheck__ magic method.
579-
// This is object_recursive_isinstance from CPython's Objects/abstract.c
580584
pub fn is_instance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
585+
self.object_recursive_isinstance(cls, vm)
586+
}
587+
588+
// This is object_recursive_isinstance from CPython's Objects/abstract.c
589+
fn object_recursive_isinstance(&self, cls: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
581590
// PyObject_TypeCheck(inst, (PyTypeObject *)cls)
582591
// This is an exact check of the type
583592
if self.class().is(cls) {
@@ -586,29 +595,28 @@ impl PyObject {
586595

587596
// PyType_CheckExact(cls) optimization
588597
if cls.class().is(vm.ctx.types.type_type) {
589-
// When cls is exactly a type (not a subclass), use real_is_instance
598+
// When cls is exactly a type (not a subclass), use object_isinstance
590599
// to avoid going through __instancecheck__ (matches CPython behavior)
591-
return self.real_is_instance(cls, vm);
600+
return self.object_isinstance(cls, vm);
592601
}
593602

594603
// Check for Union type (e.g., int | str) - CPython checks this before tuple
595-
if cls.class().is(vm.ctx.types.union_type) {
604+
let cls = if cls.class().is(vm.ctx.types.union_type) {
596605
// Match CPython's _Py_union_args which directly accesses the args field
597606
let union = cls
598607
.try_to_ref::<crate::builtins::PyUnion>(vm)
599608
.expect("checked by is");
600-
let tuple = union.args();
601-
for typ in tuple.iter() {
602-
if vm.with_recursion("in __instancecheck__", || self.is_instance(typ, vm))? {
603-
return Ok(true);
604-
}
605-
}
606-
}
609+
union.args().as_object()
610+
} else {
611+
cls
612+
};
607613

608614
// Check if cls is a tuple
609-
if let Ok(tuple) = cls.try_to_ref::<PyTuple>(vm) {
610-
for typ in tuple {
611-
if vm.with_recursion("in __instancecheck__", || self.is_instance(typ, vm))? {
615+
if let Some(tuple) = cls.downcast_ref::<PyTuple>() {
616+
for item in tuple {
617+
if vm.with_recursion("in __instancecheck__", || {
618+
self.object_recursive_isinstance(item, vm)
619+
})? {
612620
return Ok(true);
613621
}
614622
}
@@ -624,7 +632,7 @@ impl PyObject {
624632
}
625633

626634
// Fall back to object_isinstance (without going through __instancecheck__ again)
627-
self.real_is_instance(cls, vm)
635+
self.object_isinstance(cls, vm)
628636
}
629637

630638
pub fn hash(&self, vm: &VirtualMachine) -> PyResult<PyHash> {

vm/src/stdlib/typing.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,10 @@ pub(crate) mod decl {
4848
#[pyfunction(name = "override")]
4949
pub(crate) fn r#override(func: PyObjectRef, vm: &VirtualMachine) -> PyResult {
5050
// Set __override__ attribute to True
51-
func.set_attr("__override__", vm.ctx.true_value.clone(), vm)?;
51+
// Skip the attribute silently if it is not writable.
52+
// AttributeError happens if the object has __slots__ or a
53+
// read-only property, TypeError if it's a builtin class.
54+
let _ = func.set_attr("__override__", vm.ctx.true_value.clone(), vm);
5255
Ok(func)
5356
}
5457

0 commit comments

Comments
 (0)