typing upgrade to 3.13.2 (#5590)

This commit is contained in:
Ashwin Naren
2025-04-26 23:26:37 -07:00
committed by GitHub
parent ca496fb3b1
commit d46bcd9291
13 changed files with 7740 additions and 1895 deletions

6
Lib/_py_abc.py vendored
View File

@@ -33,6 +33,8 @@ class ABCMeta(type):
_abc_invalidation_counter = 0
def __new__(mcls, name, bases, namespace, /, **kwargs):
# TODO: RUSTPYTHON remove this line (prevents duplicate bases)
bases = tuple(dict.fromkeys(bases))
cls = super().__new__(mcls, name, bases, namespace, **kwargs)
# Compute set of abstract method names
abstracts = {name
@@ -98,8 +100,8 @@ class ABCMeta(type):
subtype = type(instance)
if subtype is subclass:
if (cls._abc_negative_cache_version ==
ABCMeta._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
ABCMeta._abc_invalidation_counter and
subclass in cls._abc_negative_cache):
return False
# Fall back to the subclass check.
return cls.__subclasscheck__(subclass)

View File

@@ -1,6 +1,63 @@
from math import copysign, isnan
class ExtraAssertions:
def assertIsSubclass(self, cls, superclass, msg=None):
if issubclass(cls, superclass):
return
standardMsg = f'{cls!r} is not a subclass of {superclass!r}'
self.fail(self._formatMessage(msg, standardMsg))
def assertNotIsSubclass(self, cls, superclass, msg=None):
if not issubclass(cls, superclass):
return
standardMsg = f'{cls!r} is a subclass of {superclass!r}'
self.fail(self._formatMessage(msg, standardMsg))
def assertHasAttr(self, obj, name, msg=None):
if not hasattr(obj, name):
if isinstance(obj, types.ModuleType):
standardMsg = f'module {obj.__name__!r} has no attribute {name!r}'
elif isinstance(obj, type):
standardMsg = f'type object {obj.__name__!r} has no attribute {name!r}'
else:
standardMsg = f'{type(obj).__name__!r} object has no attribute {name!r}'
self.fail(self._formatMessage(msg, standardMsg))
def assertNotHasAttr(self, obj, name, msg=None):
if hasattr(obj, name):
if isinstance(obj, types.ModuleType):
standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}'
elif isinstance(obj, type):
standardMsg = f'type object {obj.__name__!r} has unexpected attribute {name!r}'
else:
standardMsg = f'{type(obj).__name__!r} object has unexpected attribute {name!r}'
self.fail(self._formatMessage(msg, standardMsg))
def assertStartsWith(self, s, prefix, msg=None):
if s.startswith(prefix):
return
standardMsg = f"{s!r} doesn't start with {prefix!r}"
self.fail(self._formatMessage(msg, standardMsg))
def assertNotStartsWith(self, s, prefix, msg=None):
if not s.startswith(prefix):
return
self.fail(self._formatMessage(msg, f"{s!r} starts with {prefix!r}"))
def assertEndsWith(self, s, suffix, msg=None):
if s.endswith(suffix):
return
standardMsg = f"{s!r} doesn't end with {suffix!r}"
self.fail(self._formatMessage(msg, standardMsg))
def assertNotEndsWith(self, s, suffix, msg=None):
if not s.endswith(suffix):
return
self.fail(self._formatMessage(msg, f"{s!r} ends with {suffix!r}"))
class ExceptionIsLikeMixin:
def assertExceptionIsLike(self, exc, template):
"""

View File

@@ -1906,6 +1906,8 @@ class TestCase(unittest.TestCase):
c = Alias(10, 1.0)
self.assertEqual(c.new_method(), 1.0)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_generic_dynamic(self):
T = TypeVar('T')
@@ -3250,6 +3252,8 @@ class TestStringAnnotations(unittest.TestCase):
# won't exist on the instance.
self.assertNotIn('not_iv4', c.__dict__)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_text_annotations(self):
from test import dataclass_textanno

View File

@@ -15,8 +15,6 @@ class TestExceptionGroupTypeHierarchy(unittest.TestCase):
with self.assertRaisesRegex(TypeError, 'Exception'):
Exception[OSError]
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_exception_group_is_generic_type(self):
E = OSError
self.assertIsInstance(ExceptionGroup[E], types.GenericAlias)

View File

@@ -442,8 +442,6 @@ class AnnotationsFutureTestCase(unittest.TestCase):
def bar(arg: (yield)): pass
"""))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_get_type_hints_on_func_with_variadic_arg(self):
# `typing.get_type_hints` might break on a function with a variadic
# annotation (e.g. `f(*args: *Ts)`) if `from __future__ import

View File

@@ -173,6 +173,8 @@ class BaseTest(unittest.TestCase):
self.assertEqual(a.__args__, (int,))
self.assertEqual(a.__parameters__, ())
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_parameters(self):
from typing import List, Dict, Callable
D0 = dict[str, int]
@@ -212,6 +214,8 @@ class BaseTest(unittest.TestCase):
self.assertEqual(L5.__args__, (Callable[[K, V], K],))
self.assertEqual(L5.__parameters__, (K, V))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_parameter_chaining(self):
from typing import List, Dict, Union, Callable
self.assertEqual(list[T][int], list[int])
@@ -271,6 +275,8 @@ class BaseTest(unittest.TestCase):
with self.assertRaises(TypeError):
MyType[int]
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_pickle(self):
alias = GenericAlias(list, T)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
@@ -280,6 +286,8 @@ class BaseTest(unittest.TestCase):
self.assertEqual(loaded.__args__, alias.__args__)
self.assertEqual(loaded.__parameters__, alias.__parameters__)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_copy(self):
class X(list):
def __copy__(self):
@@ -303,6 +311,8 @@ class BaseTest(unittest.TestCase):
self.assertEqual(a.__args__, (list[int], list[str]))
self.assertEqual(a.__parameters__, ())
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_generic(self):
a = typing.Union[list[T], tuple[T, ...]]
self.assertEqual(a.__args__, (list[T], tuple[T, ...]))

View File

@@ -742,6 +742,8 @@ class UnionTests(unittest.TestCase):
self.assertTrue(issubclass(dict, x))
self.assertFalse(issubclass(list, x))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_instancecheck_and_subclasscheck_order(self):
T = typing.TypeVar('T')
@@ -788,6 +790,8 @@ class UnionTests(unittest.TestCase):
self.assertTrue(issubclass(int, x))
self.assertRaises(ZeroDivisionError, issubclass, list, x)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_or_type_operator_with_TypeVar(self):
TV = typing.TypeVar('T')
assert TV | str == typing.Union[TV, str]
@@ -795,6 +799,8 @@ class UnionTests(unittest.TestCase):
self.assertIs((int | TV)[int], int)
self.assertIs((TV | int)[int], int)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_args(self):
def check(arg, expected):
clear_typing_caches()
@@ -825,6 +831,8 @@ class UnionTests(unittest.TestCase):
check(x | None, (x, type(None)))
check(None | x, (type(None), x))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_parameter_chaining(self):
T = typing.TypeVar("T")
S = typing.TypeVar("S")
@@ -869,6 +877,8 @@ class UnionTests(unittest.TestCase):
eq(x[NT], int | NT | bytes)
eq(x[S], int | S | bytes)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_pickle(self):
orig = list[T] | int
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
@@ -878,6 +888,8 @@ class UnionTests(unittest.TestCase):
self.assertEqual(loaded.__args__, orig.__args__)
self.assertEqual(loaded.__parameters__, orig.__parameters__)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_copy(self):
orig = list[T] | int
for copied in (copy.copy(orig), copy.deepcopy(orig)):
@@ -885,12 +897,16 @@ class UnionTests(unittest.TestCase):
self.assertEqual(copied.__args__, orig.__args__)
self.assertEqual(copied.__parameters__, orig.__parameters__)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_union_parameter_substitution_errors(self):
T = typing.TypeVar("T")
x = int | T
with self.assertRaises(TypeError):
x[int, str]
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_or_type_operator_with_forward(self):
T = typing.TypeVar('T')
ForwardAfter = T | 'Forward'

6479
Lib/test/test_typing.py vendored

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,30 @@
"""Used to test `get_type_hints()` on a cross-module inherited `TypedDict` class
This script uses future annotations to postpone a type that won't be available
on the module inheriting from to `Foo`. The subclass in the other module should
look something like this:
class Bar(_typed_dict_helper.Foo, total=False):
b: int
In addition, it uses multiple levels of Annotated to test the interaction
between the __future__ import, Annotated, and Required.
"""
from __future__ import annotations
from typing import Annotated, Generic, Optional, Required, TypedDict, TypeVar
OptionalIntType = Optional[int]
class Foo(TypedDict):
a: OptionalIntType
T = TypeVar("T")
class FooGeneric(TypedDict, Generic[T]):
a: Optional[T]
class VeryAnnotated(TypedDict, total=False):
a: Annotated[Annotated[Annotated[Required[int], "a"], "b"], "c"]

72
Lib/test/typinganndata/ann_module695.py vendored Normal file
View File

@@ -0,0 +1,72 @@
from __future__ import annotations
from typing import Callable
class A[T, *Ts, **P]:
x: T
y: tuple[*Ts]
z: Callable[P, str]
class B[T, *Ts, **P]:
T = int
Ts = str
P = bytes
x: T
y: Ts
z: P
Eggs = int
Spam = str
class C[Eggs, **Spam]:
x: Eggs
y: Spam
def generic_function[T, *Ts, **P](
x: T, *y: *Ts, z: P.args, zz: P.kwargs
) -> None: ...
def generic_function_2[Eggs, **Spam](x: Eggs, y: Spam): pass
class D:
Foo = int
Bar = str
def generic_method[Foo, **Bar](
self, x: Foo, y: Bar
) -> None: ...
def generic_method_2[Eggs, **Spam](self, x: Eggs, y: Spam): pass
def nested():
from types import SimpleNamespace
from typing import get_type_hints
Eggs = bytes
Spam = memoryview
class E[Eggs, **Spam]:
x: Eggs
y: Spam
def generic_method[Eggs, **Spam](self, x: Eggs, y: Spam): pass
def generic_function[Eggs, **Spam](x: Eggs, y: Spam): pass
return SimpleNamespace(
E=E,
hints_for_E=get_type_hints(E),
hints_for_E_meth=get_type_hints(E.generic_method),
generic_func=generic_function,
hints_for_generic_func=get_type_hints(generic_function)
)

View File

@@ -0,0 +1,26 @@
"""Module for testing the behavior of generics across different modules."""
from typing import TypeVar, Generic, Optional, TypeAliasType
# TODO: RUSTPYTHON
# default_a: Optional['A'] = None
# default_b: Optional['B'] = None
# T = TypeVar('T')
# class A(Generic[T]):
# some_b: 'B'
# class B(Generic[T]):
# class A(Generic[T]):
# pass
# my_inner_a1: 'B.A'
# my_inner_a2: A
# my_outer_a: 'A' # unless somebody calls get_type_hints with localns=B.__dict__
# type Alias = int
# OldStyle = TypeAliasType("OldStyle", int)

2826
Lib/typing.py vendored

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,7 @@ pub(crate) mod _typing {
use crate::{
PyObjectRef, PyPayload, PyResult, VirtualMachine,
builtins::{PyGenericAlias, PyTupleRef, PyTypeRef, pystr::AsPyStr},
function::IntoFuncArgs,
function::{FuncArgs, IntoFuncArgs},
};
pub(crate) fn _call_typing_func_object<'a>(
@@ -20,8 +20,10 @@ pub(crate) mod _typing {
// func.call(args, vm)
}
#[pyattr]
pub(crate) fn _idfunc(_vm: &VirtualMachine) {}
#[pyfunction]
pub(crate) fn _idfunc(args: FuncArgs, _vm: &VirtualMachine) -> PyObjectRef {
args.args[0].clone()
}
#[pyattr]
#[pyclass(name = "TypeVar")]
@@ -51,6 +53,11 @@ pub(crate) mod _typing {
Ok(vm.ctx.none())
}
}
#[pygetset(magic)]
fn name(&self) -> PyObjectRef {
self.name.clone()
}
}
pub(crate) fn make_typevar(
@@ -77,15 +84,102 @@ pub(crate) mod _typing {
#[allow(dead_code)]
pub(crate) struct ParamSpec {
name: PyObjectRef,
bound: Option<PyObjectRef>,
default_value: Option<PyObjectRef>,
evaluate_default: Option<PyObjectRef>,
covariant: bool,
contravariant: bool,
infer_variance: bool,
}
#[pyclass(flags(BASETYPE))]
impl ParamSpec {}
impl ParamSpec {
#[pygetset(magic)]
fn name(&self) -> PyObjectRef {
self.name.clone()
}
#[pygetset(magic)]
fn bound(&self, vm: &VirtualMachine) -> PyObjectRef {
if let Some(bound) = self.bound.clone() {
return bound;
}
vm.ctx.none()
}
#[pygetset(magic)]
fn covariant(&self) -> bool {
self.covariant
}
#[pygetset(magic)]
fn contravariant(&self) -> bool {
self.contravariant
}
#[pygetset(magic)]
fn infer_variance(&self) -> bool {
self.infer_variance
}
#[pygetset(magic)]
fn default(&self, vm: &VirtualMachine) -> PyResult {
if let Some(default_value) = self.default_value.clone() {
return Ok(default_value);
}
// handle evaluate_default
if let Some(evaluate_default) = self.evaluate_default.clone() {
let default_value = vm.call_method(evaluate_default.as_ref(), "__call__", ())?;
return Ok(default_value);
}
// TODO: this isn't up to spec
Ok(vm.ctx.none())
}
#[pygetset]
fn evaluate_default(&self, vm: &VirtualMachine) -> PyObjectRef {
if let Some(evaluate_default) = self.evaluate_default.clone() {
return evaluate_default;
}
// TODO: default_value case
vm.ctx.none()
}
#[pymethod(magic)]
fn reduce(&self) -> PyResult {
Ok(self.name.clone())
}
#[pymethod]
fn has_default(&self) -> PyResult<bool> {
// TODO: fix
Ok(self.evaluate_default.is_some() || self.default_value.is_some())
}
}
pub(crate) fn make_paramspec(name: PyObjectRef) -> ParamSpec {
ParamSpec { name }
ParamSpec {
name,
bound: None,
default_value: None,
evaluate_default: None,
covariant: false,
contravariant: false,
infer_variance: false,
}
}
#[pyattr]
#[pyclass(name = "NoDefault")]
#[derive(Debug, PyPayload)]
#[allow(dead_code)]
pub(crate) struct NoDefault {
name: PyObjectRef,
}
#[pyclass(flags(BASETYPE))]
impl NoDefault {}
#[pyattr]
#[pyclass(name = "TypeVarTuple")]
#[derive(Debug, PyPayload)]
@@ -161,7 +255,6 @@ pub(crate) mod _typing {
// fn as_mapping() -> &'static PyMappingMethods {
// static AS_MAPPING: Lazy<PyMappingMethods> = Lazy::new(|| PyMappingMethods {
// subscript: atomic_func!(|mapping, needle, vm| {
// println!("gigity");
// call_typing_func_object(vm, "_GenericAlias", (mapping.obj, needle))
// }),
// ..PyMappingMethods::NOT_IMPLEMENTED