mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Merge pull request #5909 from youknowone/typing-parameters
typing __parameters__ __type_params__
This commit is contained in:
20 files changed
+295
-322
No files matched your search
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
"""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
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Optional, TypedDict
|
||||
|
||||
OptionalIntType = Optional[int]
|
||||
|
||||
class Foo(TypedDict):
|
||||
a: OptionalIntType
|
||||
Vendored
-62
@@ -1,62 +0,0 @@
|
||||
|
||||
|
||||
"""
|
||||
The module for testing variable annotations.
|
||||
Empty lines above are for good reason (testing for correct line numbers)
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
from functools import wraps
|
||||
|
||||
__annotations__[1] = 2
|
||||
|
||||
class C:
|
||||
|
||||
x = 5; y: Optional['C'] = None
|
||||
|
||||
from typing import Tuple
|
||||
x: int = 5; y: str = x; f: Tuple[int, int]
|
||||
|
||||
class M(type):
|
||||
|
||||
__annotations__['123'] = 123
|
||||
o: type = object
|
||||
|
||||
(pars): bool = True
|
||||
|
||||
class D(C):
|
||||
j: str = 'hi'; k: str= 'bye'
|
||||
|
||||
from types import new_class
|
||||
h_class = new_class('H', (C,))
|
||||
j_class = new_class('J')
|
||||
|
||||
class F():
|
||||
z: int = 5
|
||||
def __init__(self, x):
|
||||
pass
|
||||
|
||||
class Y(F):
|
||||
def __init__(self):
|
||||
super(F, self).__init__(123)
|
||||
|
||||
class Meta(type):
|
||||
def __new__(meta, name, bases, namespace):
|
||||
return super().__new__(meta, name, bases, namespace)
|
||||
|
||||
class S(metaclass = Meta):
|
||||
x: str = 'something'
|
||||
y: str = 'something else'
|
||||
|
||||
def foo(x: int = 10):
|
||||
def bar(y: List[str]):
|
||||
x: str = 'yes'
|
||||
bar()
|
||||
|
||||
def dec(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
|
||||
u: int | float
|
||||
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
"""
|
||||
Some correct syntax for variable annotation here.
|
||||
More examples are in test_grammar and test_parser.
|
||||
"""
|
||||
|
||||
from typing import no_type_check, ClassVar
|
||||
|
||||
i: int = 1
|
||||
j: int
|
||||
x: float = i/10
|
||||
|
||||
def f():
|
||||
class C: ...
|
||||
return C()
|
||||
|
||||
f().new_attr: object = object()
|
||||
|
||||
class C:
|
||||
def __init__(self, x: int) -> None:
|
||||
self.x = x
|
||||
|
||||
c = C(5)
|
||||
c.new_attr: int = 10
|
||||
|
||||
__annotations__ = {}
|
||||
|
||||
|
||||
@no_type_check
|
||||
class NTC:
|
||||
def meth(self, param: complex) -> None:
|
||||
...
|
||||
|
||||
class CV:
|
||||
var: ClassVar['CV']
|
||||
|
||||
CV.var = CV()
|
||||
Vendored
-18
@@ -1,18 +0,0 @@
|
||||
"""
|
||||
Correct syntax for variable annotation that should fail at runtime
|
||||
in a certain manner. More examples are in test_grammar and test_parser.
|
||||
"""
|
||||
|
||||
def f_bad_ann():
|
||||
__annotations__[1] = 2
|
||||
|
||||
class C_OK:
|
||||
def __init__(self, x: int) -> None:
|
||||
self.x: no_such_name = x # This one is OK as proposed by Guido
|
||||
|
||||
class D_bad_ann:
|
||||
def __init__(self, x: int) -> None:
|
||||
sfel.y: int = 0
|
||||
|
||||
def g_bad_ann():
|
||||
no_such_name.attr: int = 0
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
# This ann_module isn't for test_typing,
|
||||
# it's for test_module
|
||||
|
||||
a:int=3
|
||||
b:str=4
|
||||
Vendored
-10
@@ -1,10 +0,0 @@
|
||||
# Used by test_typing to verify that Final wrapped in ForwardRef works.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Final
|
||||
|
||||
name: Final[str] = "final"
|
||||
|
||||
class MyClass:
|
||||
value: Final = 3000
|
||||
Vendored
-7
@@ -1,7 +0,0 @@
|
||||
# Tests that top-level ClassVar is not allowed
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import ClassVar
|
||||
|
||||
wrong: ClassVar[int] = 1
|
||||
Vendored
-11
@@ -1,11 +0,0 @@
|
||||
# Tests class have ``__text_signature__``
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
DEFAULT_BUFFER_SIZE = 8192
|
||||
|
||||
class BufferedReader(object):
|
||||
"""BufferedReader(raw, buffer_size=DEFAULT_BUFFER_SIZE)\n--\n\n
|
||||
Create a new buffered reader using the given readable raw IO object.
|
||||
"""
|
||||
pass
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
"""Module for testing the behavior of generics across different modules."""
|
||||
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
from typing import TypeVar, Generic, Optional
|
||||
|
||||
|
||||
if sys.version_info[:2] >= (3, 6):
|
||||
exec(dedent("""
|
||||
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__
|
||||
"""))
|
||||
else: # This should stay in sync with the syntax above.
|
||||
__annotations__ = dict(
|
||||
default_a=Optional['A'],
|
||||
default_b=Optional['B'],
|
||||
)
|
||||
default_a = None
|
||||
default_b = None
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
class A(Generic[T]):
|
||||
__annotations__ = dict(
|
||||
some_b='B'
|
||||
)
|
||||
|
||||
|
||||
class B(Generic[T]):
|
||||
class A(Generic[T]):
|
||||
pass
|
||||
|
||||
__annotations__ = dict(
|
||||
my_inner_a1='B.A',
|
||||
my_inner_a2=A,
|
||||
my_outer_a='A' # unless somebody calls get_type_hints with localns=B.__dict__
|
||||
)
|
||||
Vendored
+1
-1
@@ -400,7 +400,7 @@ class GrammarTests(unittest.TestCase):
|
||||
def test_var_annot_in_module(self):
|
||||
# check that functions fail the same way when executed
|
||||
# outside of module where they were defined
|
||||
from test.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann
|
||||
from test.typinganndata.ann_module3 import f_bad_ann, g_bad_ann, D_bad_ann
|
||||
with self.assertRaises(NameError):
|
||||
f_bad_ann()
|
||||
with self.assertRaises(NameError):
|
||||
|
||||
Vendored
+1
-1
@@ -334,7 +334,7 @@ a = A(destroyed)"""
|
||||
del foo.__annotations__
|
||||
|
||||
def test_annotations_are_created_correctly(self):
|
||||
ann_module4 = import_helper.import_fresh_module('test.ann_module4')
|
||||
ann_module4 = import_helper.import_fresh_module('test.typinganndata.ann_module4')
|
||||
self.assertTrue("__annotations__" in ann_module4.__dict__)
|
||||
del ann_module4.__annotations__
|
||||
self.assertFalse("__annotations__" in ann_module4.__dict__)
|
||||
|
||||
Vendored
-19
@@ -629,8 +629,6 @@ class TypeParameterDefaultsTests(BaseTestCase):
|
||||
class A(Generic[T]): ...
|
||||
Alias = Optional[T]
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_typevar_none(self):
|
||||
U = TypeVar('U')
|
||||
U_None = TypeVar('U_None', default=None)
|
||||
@@ -1210,8 +1208,6 @@ class UnpackTests(BaseTestCase):
|
||||
self.assertEqual(repr(foo.__annotations__['kwargs']),
|
||||
f"typing.Unpack[{__name__}.Movie]")
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_builtin_tuple(self):
|
||||
Ts = TypeVarTuple("Ts")
|
||||
|
||||
@@ -2636,11 +2632,6 @@ class TypingCallableTests(BaseCallableTests, BaseTestCase):
|
||||
class CollectionsCallableTests(BaseCallableTests, BaseTestCase):
|
||||
Callable = collections.abc.Callable
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_errors(self):
|
||||
super().test_errors()
|
||||
|
||||
|
||||
class LiteralTests(BaseTestCase):
|
||||
def test_basics(self):
|
||||
@@ -3608,8 +3599,6 @@ class GenericTests(BaseTestCase):
|
||||
c.bar = 'abc'
|
||||
self.assertEqual(c.__dict__, {'bar': 'abc'})
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_setattr_exceptions(self):
|
||||
class Immutable[T]:
|
||||
def __setattr__(self, key, value):
|
||||
@@ -3753,8 +3742,6 @@ class GenericTests(BaseTestCase):
|
||||
self.assertEqual(Union[T, int][Meta], Union[Meta, int])
|
||||
self.assertEqual(Callable[..., Meta].__args__, (Ellipsis, Meta))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_generic_hashes(self):
|
||||
class A(Generic[T]):
|
||||
...
|
||||
@@ -4302,8 +4289,6 @@ class GenericTests(BaseTestCase):
|
||||
self.assertEqual(A[T], A[T])
|
||||
self.assertNotEqual(A[T], B[T])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_multiple_inheritance(self):
|
||||
|
||||
class A(Generic[T, VT]):
|
||||
@@ -6106,8 +6091,6 @@ class GetUtilitiesTestCase(TestCase):
|
||||
self.assertIs(get_origin((*tuple[*Ts],)[0]), tuple)
|
||||
self.assertIs(get_origin(Unpack[Tuple[Unpack[Ts]]]), Unpack)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_get_args(self):
|
||||
T = TypeVar('T')
|
||||
class C(Generic[T]): pass
|
||||
@@ -7599,8 +7582,6 @@ class TypedDictTests(BaseTestCase):
|
||||
self.assertEqual(A[str].__parameters__, ())
|
||||
self.assertEqual(A[str].__args__, (str,))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_generic_inheritance(self):
|
||||
class A(TypedDict, Generic[T]):
|
||||
a: T
|
||||
|
||||
+13
-15
@@ -2,25 +2,23 @@
|
||||
|
||||
from typing import TypeVar, Generic, Optional, TypeAliasType
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
default_a: Optional['A'] = None
|
||||
default_b: Optional['B'] = None
|
||||
|
||||
# default_a: Optional['A'] = None
|
||||
# default_b: Optional['B'] = None
|
||||
|
||||
# T = TypeVar('T')
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
# class A(Generic[T]):
|
||||
# some_b: 'B'
|
||||
class A(Generic[T]):
|
||||
some_b: 'B'
|
||||
|
||||
|
||||
# class B(Generic[T]):
|
||||
# class A(Generic[T]):
|
||||
# pass
|
||||
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__
|
||||
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)
|
||||
type Alias = int
|
||||
OldStyle = TypeAliasType("OldStyle", int)
|
||||
@@ -1215,38 +1215,83 @@ impl Compiler<'_> {
|
||||
// First, compile each type parameter and store it
|
||||
for type_param in &type_params.type_params {
|
||||
match type_param {
|
||||
TypeParam::TypeVar(TypeParamTypeVar { name, bound, .. }) => {
|
||||
TypeParam::TypeVar(TypeParamTypeVar {
|
||||
name,
|
||||
bound,
|
||||
default,
|
||||
..
|
||||
}) => {
|
||||
if let Some(expr) = &bound {
|
||||
self.compile_expression(expr)?;
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(self, Instruction::TypeVarWithBound);
|
||||
emit!(self, Instruction::Duplicate);
|
||||
self.store_name(name.as_ref())?;
|
||||
} else {
|
||||
// self.store_name(type_name.as_str())?;
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(self, Instruction::TypeVar);
|
||||
emit!(self, Instruction::Duplicate);
|
||||
self.store_name(name.as_ref())?;
|
||||
}
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
func: bytecode::IntrinsicFunction2::SetTypeparamDefault
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emit!(self, Instruction::Duplicate);
|
||||
self.store_name(name.as_ref())?;
|
||||
}
|
||||
TypeParam::ParamSpec(TypeParamParamSpec { name, .. }) => {
|
||||
TypeParam::ParamSpec(TypeParamParamSpec { name, default, .. }) => {
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(self, Instruction::ParamSpec);
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
func: bytecode::IntrinsicFunction2::SetTypeparamDefault
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emit!(self, Instruction::Duplicate);
|
||||
self.store_name(name.as_ref())?;
|
||||
}
|
||||
TypeParam::TypeVarTuple(TypeParamTypeVarTuple { name, .. }) => {
|
||||
TypeParam::TypeVarTuple(TypeParamTypeVarTuple { name, default, .. }) => {
|
||||
self.emit_load_const(ConstantData::Str {
|
||||
value: name.as_str().into(),
|
||||
});
|
||||
emit!(self, Instruction::TypeVarTuple);
|
||||
|
||||
// Handle default value if present (PEP 695)
|
||||
if let Some(default_expr) = default {
|
||||
// Compile the default expression
|
||||
self.compile_expression(default_expr)?;
|
||||
|
||||
// Handle starred expression (*default)
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic2 {
|
||||
func: bytecode::IntrinsicFunction2::SetTypeparamDefault
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emit!(self, Instruction::Duplicate);
|
||||
self.store_name(name.as_ref())?;
|
||||
}
|
||||
@@ -1759,13 +1804,41 @@ impl Compiler<'_> {
|
||||
|
||||
self.emit_load_const(ConstantData::Str { value: name.into() });
|
||||
|
||||
// Call the __build_class__ builtin
|
||||
let call = if let Some(arguments) = arguments {
|
||||
self.compile_call_inner(2, arguments)?
|
||||
// For PEP 695 classes: handle Generic base creation
|
||||
if type_params.is_some() {
|
||||
if let Some(arguments) = arguments {
|
||||
// Has explicit bases - use them as is, don't add Generic
|
||||
// CPython doesn't add Generic when explicit bases are present
|
||||
let call = self.compile_call_inner(2, arguments)?;
|
||||
self.compile_normal_call(call);
|
||||
} else {
|
||||
// No explicit bases, add Generic[*type_params] as the only base
|
||||
// Stack currently: [function, class_name]
|
||||
|
||||
// Load .type_params for creating Generic base
|
||||
let dot_type_params = self.name(".type_params");
|
||||
emit!(self, Instruction::LoadNameAny(dot_type_params));
|
||||
|
||||
// Call INTRINSIC_SUBSCRIPT_GENERIC to create Generic[*type_params]
|
||||
emit!(
|
||||
self,
|
||||
Instruction::CallIntrinsic1 {
|
||||
func: bytecode::IntrinsicFunction1::SubscriptGeneric
|
||||
}
|
||||
);
|
||||
|
||||
// Call __build_class__ with 3 positional args: function, class_name, Generic[T]
|
||||
emit!(self, Instruction::CallFunctionPositional { nargs: 3 });
|
||||
}
|
||||
} else {
|
||||
CallType::Positional { nargs: 2 }
|
||||
};
|
||||
self.compile_normal_call(call);
|
||||
// No type params, normal compilation
|
||||
let call = if let Some(arguments) = arguments {
|
||||
self.compile_call_inner(2, arguments)?
|
||||
} else {
|
||||
CallType::Positional { nargs: 2 }
|
||||
};
|
||||
self.compile_normal_call(call);
|
||||
}
|
||||
|
||||
// Pop the special type params symbol table
|
||||
if type_params.is_some() {
|
||||
|
||||
@@ -375,6 +375,49 @@ op_arg_enum!(
|
||||
}
|
||||
);
|
||||
|
||||
op_arg_enum!(
|
||||
/// Intrinsic function for CALL_INTRINSIC_1
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum IntrinsicFunction1 {
|
||||
/// Import * special case
|
||||
// ImportStar = 0,
|
||||
/// Set stop iteration value
|
||||
// StopAsyncIteration = 1,
|
||||
/// Unary operators
|
||||
// UnaryPositive = 2,
|
||||
// UnaryNegative = 3,
|
||||
// UnaryNot = 4,
|
||||
// UnaryInvert = 5,
|
||||
/// Exit init subclass
|
||||
// ExitInitCheck = 6,
|
||||
/// Create a new list from an iterator
|
||||
// ListToTupleForCall = 7,
|
||||
/// Type parameter related
|
||||
// TypeVar = 8,
|
||||
// TypeVarTuple = 9,
|
||||
// ParamSpec = 10,
|
||||
/// Generic subscript for PEP 695
|
||||
SubscriptGeneric = 10,
|
||||
// TypeAlias = 12,
|
||||
// TypeParams = 13,
|
||||
}
|
||||
);
|
||||
|
||||
op_arg_enum!(
|
||||
/// Intrinsic function for CALL_INTRINSIC_2
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[repr(u8)]
|
||||
pub enum IntrinsicFunction2 {
|
||||
// PrepReraiseS tar = 1,
|
||||
// TypeVarWithBound = 2,
|
||||
// TypeVarWithConstraints = 3,
|
||||
// SetFunctionTypeParams = 4,
|
||||
/// Set default value for type parameter (PEP 695)
|
||||
SetTypeparamDefault = 5,
|
||||
}
|
||||
);
|
||||
|
||||
pub type NameIdx = u32;
|
||||
|
||||
/// A Single bytecode instruction.
|
||||
@@ -454,6 +497,12 @@ pub enum Instruction {
|
||||
Duplicate2,
|
||||
GetIter,
|
||||
GetLen,
|
||||
CallIntrinsic1 {
|
||||
func: Arg<IntrinsicFunction1>,
|
||||
},
|
||||
CallIntrinsic2 {
|
||||
func: Arg<IntrinsicFunction2>,
|
||||
},
|
||||
Continue {
|
||||
target: Arg<Label>,
|
||||
},
|
||||
@@ -1235,6 +1284,8 @@ impl Instruction {
|
||||
Duplicate2 => 2,
|
||||
GetIter => 0,
|
||||
GetLen => 1,
|
||||
CallIntrinsic1 { .. } => 0, // Takes 1, pushes 1
|
||||
CallIntrinsic2 { .. } => -1, // Takes 2, pushes 1
|
||||
Continue { .. } => 0,
|
||||
Break { .. } => 0,
|
||||
Jump { .. } => 0,
|
||||
@@ -1444,6 +1495,8 @@ impl Instruction {
|
||||
GetIter => w!(GetIter),
|
||||
// GET_LEN
|
||||
GetLen => w!(GetLen),
|
||||
CallIntrinsic1 { func } => w!(CallIntrinsic1, ?func),
|
||||
CallIntrinsic2 { func } => w!(CallIntrinsic2, ?func),
|
||||
Continue { target } => w!(Continue, target),
|
||||
Break { target } => w!(Break, target),
|
||||
Jump { target } => w!(Jump, target),
|
||||
|
||||
@@ -613,6 +613,25 @@ impl Iterable for PyGenericAlias {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a GenericAlias from type parameters, equivalent to CPython's _Py_subscript_generic
|
||||
/// This is used for PEP 695 classes to create Generic[T] from type parameters
|
||||
// _Py_subscript_generic
|
||||
pub fn subscript_generic(type_params: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
// Get typing.Generic type
|
||||
let typing_module = vm.import("typing", 0)?;
|
||||
let generic_type = typing_module.get_attr("Generic", vm)?;
|
||||
let generic_type = PyTypeRef::try_from_object(vm, generic_type)?;
|
||||
|
||||
// Create GenericAlias: Generic[type_params]
|
||||
let args = if let Ok(tuple) = type_params.try_to_ref::<PyTuple>(vm) {
|
||||
tuple.to_owned()
|
||||
} else {
|
||||
PyTuple::new_ref(vec![type_params], &vm.ctx)
|
||||
};
|
||||
|
||||
Ok(PyGenericAlias::new(generic_type, args, false, vm).into_pyobject(vm))
|
||||
}
|
||||
|
||||
pub fn init(context: &Context) {
|
||||
let generic_alias_type = &context.types.generic_alias_type;
|
||||
PyGenericAlias::extend_class(context, generic_alias_type);
|
||||
|
||||
+42
-1
@@ -1050,6 +1050,19 @@ impl ExecutingFrame<'_> {
|
||||
self.push_value(vm.ctx.new_int(len).into());
|
||||
Ok(None)
|
||||
}
|
||||
bytecode::Instruction::CallIntrinsic1 { func } => {
|
||||
let value = self.pop_value();
|
||||
let result = self.call_intrinsic_1(func.get(arg), value, vm)?;
|
||||
self.push_value(result);
|
||||
Ok(None)
|
||||
}
|
||||
bytecode::Instruction::CallIntrinsic2 { func } => {
|
||||
let value2 = self.pop_value();
|
||||
let value1 = self.pop_value();
|
||||
let result = self.call_intrinsic_2(func.get(arg), value1, value2, vm)?;
|
||||
self.push_value(result);
|
||||
Ok(None)
|
||||
}
|
||||
bytecode::Instruction::GetAwaitable => {
|
||||
let awaited_obj = self.pop_value();
|
||||
let awaitable = if awaited_obj.payload_is::<PyCoroutine>() {
|
||||
@@ -1288,7 +1301,7 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
bytecode::Instruction::ParamSpec => {
|
||||
let param_spec_name = self.pop_value();
|
||||
let param_spec: PyObjectRef = typing::ParamSpec::new(param_spec_name.clone())
|
||||
let param_spec: PyObjectRef = typing::ParamSpec::new(param_spec_name.clone(), vm)
|
||||
.into_ref(&vm.ctx)
|
||||
.into();
|
||||
self.push_value(param_spec);
|
||||
@@ -2244,6 +2257,34 @@ impl ExecutingFrame<'_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn call_intrinsic_1(
|
||||
&mut self,
|
||||
func: bytecode::IntrinsicFunction1,
|
||||
arg: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
match func {
|
||||
bytecode::IntrinsicFunction1::SubscriptGeneric => {
|
||||
// Used for PEP 695: Generic[*type_params]
|
||||
crate::builtins::genericalias::subscript_generic(arg, vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn call_intrinsic_2(
|
||||
&mut self,
|
||||
func: bytecode::IntrinsicFunction2,
|
||||
arg1: PyObjectRef,
|
||||
arg2: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
match func {
|
||||
bytecode::IntrinsicFunction2::SetTypeparamDefault => {
|
||||
crate::stdlib::typing::set_typeparam_default(arg1, arg2, vm)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_multiple(&mut self, count: usize) -> crate::common::boxvec::Drain<'_, PyObjectRef> {
|
||||
let stack_len = self.state.stack.len();
|
||||
self.state.stack.drain(stack_len - count..)
|
||||
|
||||
@@ -1009,7 +1009,9 @@ mod builtins {
|
||||
{
|
||||
if let Some(type_params_tuple) = type_params.downcast_ref::<PyTuple>() {
|
||||
if !type_params_tuple.is_empty() {
|
||||
class.set_attr(identifier!(vm, __type_params__), type_params, vm)?;
|
||||
class.set_attr(identifier!(vm, __type_params__), type_params.clone(), vm)?;
|
||||
// Also set __parameters__ for compatibility with typing module
|
||||
class.set_attr(identifier!(vm, __parameters__), type_params, vm)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+75
-50
@@ -2,6 +2,7 @@
|
||||
use crate::{
|
||||
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyTupleRef, PyTypeRef, pystr::AsPyStr},
|
||||
common::lock::PyMutex,
|
||||
function::{FuncArgs, IntoFuncArgs, PyComparisonValue},
|
||||
protocol::PyNumberMethods,
|
||||
types::{AsNumber, Comparable, Constructor, Iterable, PyComparisonOp, Representable},
|
||||
@@ -76,7 +77,7 @@ pub struct TypeVar {
|
||||
constraints: parking_lot::Mutex<PyObjectRef>,
|
||||
evaluate_constraints: PyObjectRef,
|
||||
default_value: parking_lot::Mutex<PyObjectRef>,
|
||||
evaluate_default: PyObjectRef,
|
||||
evaluate_default: PyMutex<PyObjectRef>,
|
||||
covariant: bool,
|
||||
contravariant: bool,
|
||||
infer_variance: bool,
|
||||
@@ -145,8 +146,9 @@ impl TypeVar {
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
if !vm.is_none(&self.evaluate_default) {
|
||||
*default_value = self.evaluate_default.call((), vm)?;
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
*default_value = evaluate_default.call((), vm)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
// Return NoDefault singleton
|
||||
@@ -171,7 +173,7 @@ impl TypeVar {
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if !vm.is_none(&self.evaluate_default) {
|
||||
if !vm.is_none(&self.evaluate_default.lock()) {
|
||||
return true;
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
@@ -358,7 +360,7 @@ impl Constructor for TypeVar {
|
||||
constraints: parking_lot::Mutex::new(constraints_obj),
|
||||
evaluate_constraints,
|
||||
default_value: parking_lot::Mutex::new(default_value),
|
||||
evaluate_default,
|
||||
evaluate_default: PyMutex::new(evaluate_default),
|
||||
covariant,
|
||||
contravariant,
|
||||
infer_variance,
|
||||
@@ -384,8 +386,8 @@ impl TypeVar {
|
||||
evaluate_bound,
|
||||
constraints: parking_lot::Mutex::new(vm.ctx.none()),
|
||||
evaluate_constraints,
|
||||
default_value: parking_lot::Mutex::new(vm.ctx.none()),
|
||||
evaluate_default: vm.ctx.none(),
|
||||
default_value: parking_lot::Mutex::new(vm.ctx.typing_no_default.clone().into()),
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
covariant: false,
|
||||
contravariant: false,
|
||||
infer_variance: false,
|
||||
@@ -399,8 +401,8 @@ impl TypeVar {
|
||||
pub struct ParamSpec {
|
||||
name: PyObjectRef,
|
||||
bound: Option<PyObjectRef>,
|
||||
default_value: Option<PyObjectRef>,
|
||||
evaluate_default: Option<PyObjectRef>,
|
||||
default_value: PyObjectRef,
|
||||
evaluate_default: PyMutex<PyObjectRef>,
|
||||
covariant: bool,
|
||||
contravariant: bool,
|
||||
infer_variance: bool,
|
||||
@@ -461,14 +463,13 @@ impl ParamSpec {
|
||||
|
||||
#[pygetset]
|
||||
fn __default__(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if let Some(ref default_value) = self.default_value {
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
if !self.default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(self.default_value.clone());
|
||||
}
|
||||
// handle evaluate_default
|
||||
if let Some(evaluate_default) = self.evaluate_default.clone() {
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
let default_value = evaluate_default.call((), vm)?;
|
||||
return Ok(default_value);
|
||||
}
|
||||
@@ -477,11 +478,8 @@ impl ParamSpec {
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn evaluate_default(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
if let Some(evaluate_default) = self.evaluate_default.clone() {
|
||||
return evaluate_default;
|
||||
}
|
||||
vm.ctx.none()
|
||||
fn evaluate_default(&self, _vm: &VirtualMachine) -> PyObjectRef {
|
||||
self.evaluate_default.lock().clone()
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -491,15 +489,11 @@ impl ParamSpec {
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if self.evaluate_default.is_some() {
|
||||
if !vm.is_none(&self.evaluate_default.lock()) {
|
||||
return true;
|
||||
}
|
||||
if let Some(ref default_value) = self.default_value {
|
||||
// Check if default_value is not NoDefault
|
||||
!default_value.is(&vm.ctx.typing_no_default)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
// Check if default_value is not NoDefault
|
||||
!self.default_value.is(&vm.ctx.typing_no_default)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
@@ -594,18 +588,13 @@ impl Constructor for ParamSpec {
|
||||
}
|
||||
|
||||
// Handle default value
|
||||
let default_value = if let Some(default) = default {
|
||||
Some(default)
|
||||
} else {
|
||||
// If no default provided, use NoDefault singleton
|
||||
Some(vm.ctx.typing_no_default.clone().into())
|
||||
};
|
||||
let default_value = default.unwrap_or_else(|| vm.ctx.typing_no_default.clone().into());
|
||||
|
||||
let paramspec = Self {
|
||||
name,
|
||||
bound,
|
||||
default_value,
|
||||
evaluate_default: None,
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
covariant,
|
||||
contravariant,
|
||||
infer_variance,
|
||||
@@ -627,12 +616,12 @@ impl Representable for ParamSpec {
|
||||
}
|
||||
|
||||
impl ParamSpec {
|
||||
pub const fn new(name: PyObjectRef) -> Self {
|
||||
pub fn new(name: PyObjectRef, vm: &VirtualMachine) -> Self {
|
||||
Self {
|
||||
name,
|
||||
bound: None,
|
||||
default_value: None,
|
||||
evaluate_default: None,
|
||||
default_value: vm.ctx.typing_no_default.clone().into(),
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
covariant: false,
|
||||
contravariant: false,
|
||||
infer_variance: false,
|
||||
@@ -646,7 +635,7 @@ impl ParamSpec {
|
||||
pub struct TypeVarTuple {
|
||||
name: PyObjectRef,
|
||||
default_value: parking_lot::Mutex<PyObjectRef>,
|
||||
evaluate_default: PyObjectRef,
|
||||
evaluate_default: PyMutex<PyObjectRef>,
|
||||
}
|
||||
#[pyclass(flags(HAS_DICT), with(Constructor, Representable, Iterable))]
|
||||
impl TypeVarTuple {
|
||||
@@ -662,8 +651,9 @@ impl TypeVarTuple {
|
||||
if !default_value.is(&vm.ctx.typing_no_default) {
|
||||
return Ok(default_value.clone());
|
||||
}
|
||||
if !vm.is_none(&self.evaluate_default) {
|
||||
*default_value = self.evaluate_default.call((), vm)?;
|
||||
let evaluate_default = self.evaluate_default.lock();
|
||||
if !vm.is_none(&evaluate_default) {
|
||||
*default_value = evaluate_default.call((), vm)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
// Return NoDefault singleton
|
||||
@@ -673,7 +663,7 @@ impl TypeVarTuple {
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if !vm.is_none(&self.evaluate_default) {
|
||||
if !vm.is_none(&self.evaluate_default.lock()) {
|
||||
return true;
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
@@ -765,7 +755,7 @@ impl Constructor for TypeVarTuple {
|
||||
let typevartuple = Self {
|
||||
name,
|
||||
default_value: parking_lot::Mutex::new(default_value),
|
||||
evaluate_default,
|
||||
evaluate_default: PyMutex::new(evaluate_default),
|
||||
};
|
||||
|
||||
let obj = typevartuple.into_ref_with_type(vm, cls)?;
|
||||
@@ -788,7 +778,7 @@ impl TypeVarTuple {
|
||||
Self {
|
||||
name,
|
||||
default_value: parking_lot::Mutex::new(vm.ctx.typing_no_default.clone().into()),
|
||||
evaluate_default: vm.ctx.none(),
|
||||
evaluate_default: PyMutex::new(vm.ctx.none()),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -981,13 +971,8 @@ pub struct Generic {}
|
||||
#[pyclass(flags(BASETYPE))]
|
||||
impl Generic {
|
||||
#[pyclassmethod]
|
||||
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
// Convert single arg to FuncArgs
|
||||
let func_args = FuncArgs {
|
||||
args: vec![args],
|
||||
kwargs: Default::default(),
|
||||
};
|
||||
call_typing_args_kwargs("_generic_class_getitem", cls, func_args, vm)
|
||||
fn __class_getitem__(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
call_typing_args_kwargs("_generic_class_getitem", cls, args, vm)
|
||||
}
|
||||
|
||||
#[pyclassmethod]
|
||||
@@ -995,3 +980,43 @@ impl Generic {
|
||||
call_typing_args_kwargs("_generic_init_subclass", cls, args, vm)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the default value for a type parameter, equivalent to CPython's _Py_set_typeparam_default
|
||||
/// This is used by the CALL_INTRINSIC_2 SetTypeparamDefault instruction
|
||||
pub fn set_typeparam_default(
|
||||
type_param: PyObjectRef,
|
||||
evaluate_default: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
// Inner function to handle common pattern of setting evaluate_default
|
||||
fn try_set_default<T>(
|
||||
obj: &PyObjectRef,
|
||||
evaluate_default: &PyObjectRef,
|
||||
get_field: impl FnOnce(&T) -> &PyMutex<PyObjectRef>,
|
||||
) -> bool
|
||||
where
|
||||
T: PyPayload,
|
||||
{
|
||||
if let Some(typed_obj) = obj.downcast_ref::<T>() {
|
||||
*get_field(typed_obj).lock() = evaluate_default.clone();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// Try each type parameter type
|
||||
if try_set_default::<TypeVar>(&type_param, &evaluate_default, |tv| &tv.evaluate_default)
|
||||
|| try_set_default::<ParamSpec>(&type_param, &evaluate_default, |ps| &ps.evaluate_default)
|
||||
|| try_set_default::<TypeVarTuple>(&type_param, &evaluate_default, |tvt| {
|
||||
&tvt.evaluate_default
|
||||
})
|
||||
{
|
||||
Ok(type_param)
|
||||
} else {
|
||||
Err(vm.new_type_error(format!(
|
||||
"Expected a type param, got {}",
|
||||
type_param.class().name()
|
||||
)))
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ use crate::{PyPayload, PyRef, VirtualMachine, class::PyClassImpl, stdlib::PyModu
|
||||
|
||||
pub use crate::stdlib::typevar::{
|
||||
Generic, ParamSpec, ParamSpecArgs, ParamSpecKwargs, TypeVar, TypeVarTuple,
|
||||
set_typeparam_default,
|
||||
};
|
||||
pub use decl::*;
|
||||
|
||||
|
||||
Reference in new issue
Block a user