mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
typing ParamSpec (#5837)
* Add cspell * TypeVar * ParamSpec * refactor * TypeVarTuple repr * Remove expectedFailure
This commit is contained in:
@@ -17,6 +17,7 @@ basicsize
|
||||
bdfl
|
||||
bigcharset
|
||||
bignum
|
||||
bivariant
|
||||
breakpointhook
|
||||
cformat
|
||||
chunksize
|
||||
|
||||
10
Lib/test/test_types.py
vendored
10
Lib/test/test_types.py
vendored
@@ -742,8 +742,6 @@ 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')
|
||||
|
||||
@@ -790,8 +788,6 @@ 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]
|
||||
@@ -799,8 +795,6 @@ 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()
|
||||
@@ -893,16 +887,12 @@ 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'
|
||||
|
||||
14
Lib/test/test_typing.py
vendored
14
Lib/test/test_typing.py
vendored
@@ -464,8 +464,6 @@ class TypeVarTests(BaseTestCase):
|
||||
self.assertEqual(Union[X, int].__parameters__, (X,))
|
||||
self.assertIs(Union[X, int].__origin__, Union)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_or(self):
|
||||
X = TypeVar('X')
|
||||
# use a string because str doesn't implement
|
||||
@@ -657,7 +655,6 @@ class TypeParameterDefaultsTests(BaseTestCase):
|
||||
self.assertFalse(T.has_default())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_paramspec(self):
|
||||
P = ParamSpec('P', default=(str, int))
|
||||
self.assertEqual(P.__default__, (str, int))
|
||||
@@ -686,7 +683,6 @@ class TypeParameterDefaultsTests(BaseTestCase):
|
||||
self.assertFalse(P.has_default())
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_typevartuple(self):
|
||||
Ts = TypeVarTuple('Ts', default=Unpack[Tuple[str, int]])
|
||||
self.assertEqual(Ts.__default__, Unpack[Tuple[str, int]])
|
||||
@@ -1289,7 +1285,6 @@ class UnpackTests(BaseTestCase):
|
||||
class TypeVarTupleTests(BaseTestCase):
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_name(self):
|
||||
Ts = TypeVarTuple('Ts')
|
||||
self.assertEqual(Ts.__name__, 'Ts')
|
||||
@@ -1297,7 +1292,6 @@ class TypeVarTupleTests(BaseTestCase):
|
||||
self.assertEqual(Ts2.__name__, 'Ts2')
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_module(self):
|
||||
Ts = TypeVarTuple('Ts')
|
||||
self.assertEqual(Ts.__module__, __name__)
|
||||
@@ -2050,7 +2044,6 @@ class TypeVarTuplePicklingTests(BaseTestCase):
|
||||
# statements at the start of each test.
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
@all_pickle_protocols
|
||||
def test_pickling_then_unpickling_results_in_same_identity(self, proto):
|
||||
global global_Ts1 # See explanation at start of class.
|
||||
@@ -4278,7 +4271,6 @@ class GenericTests(BaseTestCase):
|
||||
self.assertEqual(t, deepcopy(t))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_immutability_by_copy_and_pickle(self):
|
||||
# Special forms like Union, Any, etc., generic aliases to containers like List,
|
||||
# Mapping, etc., and type variabcles are considered immutable by copy and pickle.
|
||||
@@ -8801,7 +8793,6 @@ class TypeAliasTests(BaseTestCase):
|
||||
class ParamSpecTests(BaseTestCase):
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_basic_plain(self):
|
||||
P = ParamSpec('P')
|
||||
self.assertEqual(P, P)
|
||||
@@ -9010,7 +9001,6 @@ class ParamSpecTests(BaseTestCase):
|
||||
self.assertEqual(B.__args__, ((int, str,), Tuple[bytes, float]))
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_var_substitution(self):
|
||||
P = ParamSpec("P")
|
||||
subst = P.__typing_subst__
|
||||
@@ -9022,7 +9012,6 @@ class ParamSpecTests(BaseTestCase):
|
||||
self.assertEqual(subst(Concatenate[int, P]), Concatenate[int, P])
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_bad_var_substitution(self):
|
||||
T = TypeVar('T')
|
||||
P = ParamSpec('P')
|
||||
@@ -9241,7 +9230,6 @@ class ConcatenateTests(BaseTestCase):
|
||||
self.assertIn(required_item, dir_items)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_valid_uses(self):
|
||||
P = ParamSpec('P')
|
||||
T = TypeVar('T')
|
||||
@@ -9719,8 +9707,6 @@ class NoDefaultTests(BaseTestCase):
|
||||
with self.assertRaises(TypeError):
|
||||
type(NoDefault)(1)
|
||||
|
||||
# TODO: RUSTPYTHON
|
||||
@unittest.expectedFailure
|
||||
def test_repr(self):
|
||||
self.assertEqual(repr(NoDefault), 'typing.NoDefault')
|
||||
|
||||
|
||||
@@ -1282,7 +1282,7 @@ impl ExecutingFrame<'_> {
|
||||
bytecode::Instruction::TypeVarTuple => {
|
||||
let type_var_tuple_name = self.pop_value();
|
||||
let type_var_tuple: PyObjectRef =
|
||||
_typing::make_typevartuple(type_var_tuple_name.clone())
|
||||
_typing::make_typevartuple(type_var_tuple_name.clone(), vm)
|
||||
.into_ref(&vm.ctx)
|
||||
.into();
|
||||
self.push_value(type_var_tuple);
|
||||
|
||||
@@ -16,7 +16,8 @@ pub(crate) mod _typing {
|
||||
AsObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyGenericAlias, PyTupleRef, PyTypeRef, pystr::AsPyStr},
|
||||
function::{FuncArgs, IntoFuncArgs},
|
||||
types::{Constructor, Representable},
|
||||
protocol::PyNumberMethods,
|
||||
types::{AsNumber, Constructor, Representable},
|
||||
};
|
||||
|
||||
pub(crate) fn _call_typing_func_object<'a>(
|
||||
@@ -59,7 +60,7 @@ pub(crate) mod _typing {
|
||||
contravariant: bool,
|
||||
infer_variance: bool,
|
||||
}
|
||||
#[pyclass(flags(HAS_DICT), with(Constructor, Representable))]
|
||||
#[pyclass(flags(HAS_DICT), with(AsNumber, Constructor, Representable))]
|
||||
impl TypeVar {
|
||||
#[pymethod(magic)]
|
||||
fn mro_entries(&self, _bases: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
@@ -173,6 +174,18 @@ pub(crate) mod _typing {
|
||||
}
|
||||
}
|
||||
|
||||
impl AsNumber for TypeVar {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| {
|
||||
_call_typing_func_object(vm, "_make_union", (a.to_owned(), b.to_owned()))
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for TypeVar {
|
||||
type Args = FuncArgs;
|
||||
|
||||
@@ -283,17 +296,7 @@ pub(crate) mod _typing {
|
||||
|
||||
let obj = typevar.into_ref_with_type(vm, cls)?;
|
||||
let obj_ref: PyObjectRef = obj.into();
|
||||
|
||||
// Set __module__ from the current frame
|
||||
if let Some(frame) = vm.current_frame() {
|
||||
if let Ok(module_name) = frame.globals.get_item("__name__", vm) {
|
||||
// Don't set __module__ if it's None
|
||||
if !vm.is_none(&module_name) {
|
||||
obj_ref.set_attr("__module__", module_name, vm)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set_module_from_caller(&obj_ref, vm)?;
|
||||
Ok(obj_ref)
|
||||
}
|
||||
}
|
||||
@@ -319,7 +322,7 @@ pub(crate) mod _typing {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "ParamSpec")]
|
||||
#[pyclass(name = "ParamSpec", module = "typing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ParamSpec {
|
||||
@@ -332,13 +335,31 @@ pub(crate) mod _typing {
|
||||
infer_variance: bool,
|
||||
}
|
||||
|
||||
#[pyclass(flags(BASETYPE))]
|
||||
#[pyclass(flags(HAS_DICT), with(AsNumber, Constructor))]
|
||||
impl ParamSpec {
|
||||
#[pygetset(magic)]
|
||||
fn name(&self) -> PyObjectRef {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn args(zelf: crate::PyRef<Self>, vm: &VirtualMachine) -> PyResult {
|
||||
let self_obj: PyObjectRef = zelf.into();
|
||||
let psa = ParamSpecArgs {
|
||||
__origin__: self_obj,
|
||||
};
|
||||
Ok(psa.into_ref(&vm.ctx).into())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn kwargs(zelf: crate::PyRef<Self>, vm: &VirtualMachine) -> PyResult {
|
||||
let self_obj: PyObjectRef = zelf.into();
|
||||
let psk = ParamSpecKwargs {
|
||||
__origin__: self_obj,
|
||||
};
|
||||
Ok(psk.into_ref(&vm.ctx).into())
|
||||
}
|
||||
|
||||
#[pygetset(magic)]
|
||||
fn bound(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
if let Some(bound) = self.bound.clone() {
|
||||
@@ -364,16 +385,19 @@ pub(crate) mod _typing {
|
||||
|
||||
#[pygetset(magic)]
|
||||
fn default(&self, vm: &VirtualMachine) -> PyResult {
|
||||
if let Some(default_value) = self.default_value.clone() {
|
||||
return Ok(default_value);
|
||||
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());
|
||||
}
|
||||
}
|
||||
// handle evaluate_default
|
||||
if let Some(evaluate_default) = self.evaluate_default.clone() {
|
||||
let default_value = vm.call_method(evaluate_default.as_ref(), "__call__", ())?;
|
||||
let default_value = evaluate_default.call((), vm)?;
|
||||
return Ok(default_value);
|
||||
}
|
||||
// TODO: this isn't up to spec
|
||||
Ok(vm.ctx.none())
|
||||
// Return NoDefault singleton
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
@@ -381,7 +405,6 @@ pub(crate) mod _typing {
|
||||
if let Some(evaluate_default) = self.evaluate_default.clone() {
|
||||
return evaluate_default;
|
||||
}
|
||||
// TODO: default_value case
|
||||
vm.ctx.none()
|
||||
}
|
||||
|
||||
@@ -391,9 +414,137 @@ pub(crate) mod _typing {
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self) -> PyResult<bool> {
|
||||
// TODO: fix
|
||||
Ok(self.evaluate_default.is_some() || self.default_value.is_some())
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if self.evaluate_default.is_some() {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn typing_subst(
|
||||
zelf: crate::PyRef<Self>,
|
||||
arg: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let self_obj: PyObjectRef = zelf.into();
|
||||
_call_typing_func_object(vm, "_paramspec_subst", (self_obj, arg))
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn typing_prepare_subst(
|
||||
zelf: crate::PyRef<Self>,
|
||||
alias: PyObjectRef,
|
||||
args: PyTupleRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult {
|
||||
let self_obj: PyObjectRef = zelf.into();
|
||||
_call_typing_func_object(vm, "_paramspec_prepare_subst", (self_obj, alias, args))
|
||||
}
|
||||
}
|
||||
|
||||
impl AsNumber for ParamSpec {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| {
|
||||
_call_typing_func_object(vm, "_make_union", (a.to_owned(), b.to_owned()))
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for ParamSpec {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
|
||||
let mut kwargs = args.kwargs;
|
||||
// Parse arguments manually
|
||||
let name = if args.args.is_empty() {
|
||||
// Check if name is provided as keyword argument
|
||||
if let Some(name) = kwargs.swap_remove("name") {
|
||||
name
|
||||
} else {
|
||||
return Err(vm.new_type_error(
|
||||
"ParamSpec() missing required argument: 'name' (pos 1)".to_owned(),
|
||||
));
|
||||
}
|
||||
} else if args.args.len() == 1 {
|
||||
args.args[0].clone()
|
||||
} else {
|
||||
return Err(
|
||||
vm.new_type_error("ParamSpec() takes at most 1 positional argument".to_owned())
|
||||
);
|
||||
};
|
||||
|
||||
let bound = kwargs.swap_remove("bound");
|
||||
let covariant = kwargs
|
||||
.swap_remove("covariant")
|
||||
.map(|v| v.try_to_bool(vm))
|
||||
.transpose()?
|
||||
.unwrap_or(false);
|
||||
let contravariant = kwargs
|
||||
.swap_remove("contravariant")
|
||||
.map(|v| v.try_to_bool(vm))
|
||||
.transpose()?
|
||||
.unwrap_or(false);
|
||||
let infer_variance = kwargs
|
||||
.swap_remove("infer_variance")
|
||||
.map(|v| v.try_to_bool(vm))
|
||||
.transpose()?
|
||||
.unwrap_or(false);
|
||||
let default = kwargs.swap_remove("default");
|
||||
|
||||
// Check for unexpected keyword arguments
|
||||
if !kwargs.is_empty() {
|
||||
let unexpected_keys: Vec<String> = kwargs.keys().map(|s| s.to_string()).collect();
|
||||
return Err(vm.new_type_error(format!(
|
||||
"ParamSpec() got unexpected keyword argument(s): {}",
|
||||
unexpected_keys.join(", ")
|
||||
)));
|
||||
}
|
||||
|
||||
// Check for invalid combinations
|
||||
if covariant && contravariant {
|
||||
return Err(
|
||||
vm.new_value_error("Bivariant type variables are not supported.".to_owned())
|
||||
);
|
||||
}
|
||||
|
||||
if infer_variance && (covariant || contravariant) {
|
||||
return Err(vm.new_value_error(
|
||||
"Variance cannot be specified with infer_variance".to_owned(),
|
||||
));
|
||||
}
|
||||
|
||||
// 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 paramspec = ParamSpec {
|
||||
name,
|
||||
bound,
|
||||
default_value,
|
||||
evaluate_default: None,
|
||||
covariant,
|
||||
contravariant,
|
||||
infer_variance,
|
||||
};
|
||||
|
||||
let obj = paramspec.into_ref_with_type(vm, cls)?;
|
||||
let obj_ref: PyObjectRef = obj.into();
|
||||
set_module_from_caller(&obj_ref, vm)?;
|
||||
Ok(obj_ref)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,7 +564,7 @@ pub(crate) mod _typing {
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub struct NoDefault;
|
||||
|
||||
#[pyclass(with(Constructor), flags(BASETYPE))]
|
||||
#[pyclass(with(Constructor, Representable), flags(BASETYPE))]
|
||||
impl NoDefault {
|
||||
#[pymethod(magic)]
|
||||
fn reduce(&self, _vm: &VirtualMachine) -> String {
|
||||
@@ -442,34 +593,213 @@ pub(crate) mod _typing {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "TypeVarTuple")]
|
||||
#[pyclass(name = "TypeVarTuple", module = "typing")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct TypeVarTuple {
|
||||
name: PyObjectRef,
|
||||
default_value: parking_lot::Mutex<PyObjectRef>,
|
||||
evaluate_default: PyObjectRef,
|
||||
}
|
||||
#[pyclass(flags(BASETYPE))]
|
||||
impl TypeVarTuple {}
|
||||
#[pyclass(flags(HAS_DICT), with(Constructor, Representable))]
|
||||
impl TypeVarTuple {
|
||||
#[pygetset(magic)]
|
||||
fn name(&self) -> PyObjectRef {
|
||||
self.name.clone()
|
||||
}
|
||||
|
||||
pub(crate) fn make_typevartuple(name: PyObjectRef) -> TypeVarTuple {
|
||||
TypeVarTuple { name }
|
||||
#[pygetset(magic)]
|
||||
fn default(&self, vm: &VirtualMachine) -> PyResult {
|
||||
let mut default_value = self.default_value.lock();
|
||||
// Check if default_value is NoDefault (not just None)
|
||||
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)?;
|
||||
Ok(default_value.clone())
|
||||
} else {
|
||||
// Return NoDefault singleton
|
||||
Ok(vm.ctx.typing_no_default.clone().into())
|
||||
}
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn has_default(&self, vm: &VirtualMachine) -> bool {
|
||||
if !vm.is_none(&self.evaluate_default) {
|
||||
return true;
|
||||
}
|
||||
let default_value = self.default_value.lock();
|
||||
// Check if default_value is not NoDefault
|
||||
!default_value.is(&vm.ctx.typing_no_default)
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn reduce(&self) -> PyObjectRef {
|
||||
self.name.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for TypeVarTuple {
|
||||
type Args = FuncArgs;
|
||||
|
||||
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
|
||||
let mut kwargs = args.kwargs;
|
||||
// Parse arguments manually
|
||||
let name = if args.args.is_empty() {
|
||||
// Check if name is provided as keyword argument
|
||||
if let Some(name) = kwargs.swap_remove("name") {
|
||||
name
|
||||
} else {
|
||||
return Err(vm.new_type_error(
|
||||
"TypeVarTuple() missing required argument: 'name' (pos 1)".to_owned(),
|
||||
));
|
||||
}
|
||||
} else if args.args.len() == 1 {
|
||||
args.args[0].clone()
|
||||
} else {
|
||||
return Err(vm.new_type_error(
|
||||
"TypeVarTuple() takes at most 1 positional argument".to_owned(),
|
||||
));
|
||||
};
|
||||
|
||||
let default = kwargs.swap_remove("default");
|
||||
|
||||
// Check for unexpected keyword arguments
|
||||
if !kwargs.is_empty() {
|
||||
let unexpected_keys: Vec<String> = kwargs.keys().map(|s| s.to_string()).collect();
|
||||
return Err(vm.new_type_error(format!(
|
||||
"TypeVarTuple() got unexpected keyword argument(s): {}",
|
||||
unexpected_keys.join(", ")
|
||||
)));
|
||||
}
|
||||
|
||||
// Handle default value
|
||||
let (default_value, evaluate_default) = if let Some(default) = default {
|
||||
(default, vm.ctx.none())
|
||||
} else {
|
||||
// If no default provided, use NoDefault singleton
|
||||
(vm.ctx.typing_no_default.clone().into(), vm.ctx.none())
|
||||
};
|
||||
|
||||
let typevartuple = TypeVarTuple {
|
||||
name,
|
||||
default_value: parking_lot::Mutex::new(default_value),
|
||||
evaluate_default,
|
||||
};
|
||||
|
||||
let obj = typevartuple.into_ref_with_type(vm, cls)?;
|
||||
let obj_ref: PyObjectRef = obj.into();
|
||||
set_module_from_caller(&obj_ref, vm)?;
|
||||
Ok(obj_ref)
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for TypeVarTuple {
|
||||
#[inline(always)]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
let name = zelf.name.str(vm)?;
|
||||
Ok(format!("*{}", name))
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn make_typevartuple(name: PyObjectRef, vm: &VirtualMachine) -> TypeVarTuple {
|
||||
TypeVarTuple {
|
||||
name,
|
||||
default_value: parking_lot::Mutex::new(vm.ctx.typing_no_default.clone().into()),
|
||||
evaluate_default: vm.ctx.none(),
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "ParamSpecArgs")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ParamSpecArgs {}
|
||||
#[pyclass(flags(BASETYPE))]
|
||||
impl ParamSpecArgs {}
|
||||
pub(crate) struct ParamSpecArgs {
|
||||
__origin__: PyObjectRef,
|
||||
}
|
||||
#[pyclass(flags(BASETYPE), with(Constructor, Representable))]
|
||||
impl ParamSpecArgs {
|
||||
#[pygetset(magic)]
|
||||
fn origin(&self) -> PyObjectRef {
|
||||
self.__origin__.clone()
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn eq(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
// Check if other has __origin__ attribute
|
||||
if let Ok(other_origin) = other.get_attr("__origin__", vm) {
|
||||
return Ok(self.__origin__.is(&other_origin));
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for ParamSpecArgs {
|
||||
type Args = (PyObjectRef,);
|
||||
|
||||
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
|
||||
let origin = args.0;
|
||||
let psa = ParamSpecArgs { __origin__: origin };
|
||||
psa.into_ref_with_type(vm, cls).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for ParamSpecArgs {
|
||||
#[inline(always)]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
// Check if origin is a ParamSpec
|
||||
if let Ok(name) = zelf.__origin__.get_attr("__name__", vm) {
|
||||
return Ok(format!("{}.args", name.str(vm)?));
|
||||
}
|
||||
Ok(format!("{:?}.args", zelf.__origin__))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "ParamSpecKwargs")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) struct ParamSpecKwargs {}
|
||||
#[pyclass(flags(BASETYPE))]
|
||||
impl ParamSpecKwargs {}
|
||||
pub(crate) struct ParamSpecKwargs {
|
||||
__origin__: PyObjectRef,
|
||||
}
|
||||
#[pyclass(flags(BASETYPE), with(Constructor, Representable))]
|
||||
impl ParamSpecKwargs {
|
||||
#[pygetset(magic)]
|
||||
fn origin(&self) -> PyObjectRef {
|
||||
self.__origin__.clone()
|
||||
}
|
||||
|
||||
#[pymethod(magic)]
|
||||
fn eq(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<bool> {
|
||||
// Check if other has __origin__ attribute
|
||||
if let Ok(other_origin) = other.get_attr("__origin__", vm) {
|
||||
return Ok(self.__origin__.is(&other_origin));
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Constructor for ParamSpecKwargs {
|
||||
type Args = (PyObjectRef,);
|
||||
|
||||
fn py_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
|
||||
let origin = args.0;
|
||||
let psa = ParamSpecKwargs { __origin__: origin };
|
||||
psa.into_ref_with_type(vm, cls).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for ParamSpecKwargs {
|
||||
#[inline(always)]
|
||||
fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
|
||||
// Check if origin is a ParamSpec
|
||||
if let Ok(name) = zelf.__origin__.get_attr("__name__", vm) {
|
||||
return Ok(format!("{}.kwargs", name.str(vm)?));
|
||||
}
|
||||
Ok(format!("{:?}.kwargs", zelf.__origin__))
|
||||
}
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name)]
|
||||
@@ -523,4 +853,40 @@ pub(crate) mod _typing {
|
||||
// &AS_MAPPING
|
||||
// }
|
||||
// }
|
||||
|
||||
/// Get the module of the caller frame, similar to CPython's caller() function.
|
||||
/// Returns the module name or None if not found.
|
||||
///
|
||||
/// Note: CPython's implementation (in typevarobject.c) gets the module from the
|
||||
/// frame's function object using PyFunction_GetModule(f->f_funcobj). However,
|
||||
/// RustPython's Frame doesn't store a reference to the function object, so we
|
||||
/// get the module name from the frame's globals dictionary instead.
|
||||
fn caller(vm: &VirtualMachine) -> Option<PyObjectRef> {
|
||||
let frame = vm.current_frame()?;
|
||||
|
||||
// In RustPython, we get the module name from frame's globals
|
||||
// This is similar to CPython's sys._getframe().f_globals.get('__name__')
|
||||
frame.globals.get_item("__name__", vm).ok()
|
||||
}
|
||||
|
||||
/// Set __module__ attribute for an object based on the caller's module.
|
||||
/// This follows CPython's behavior for TypeVar and similar objects.
|
||||
fn set_module_from_caller(obj: &PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
|
||||
// Note: CPython gets module from frame->f_funcobj, but RustPython's Frame
|
||||
// architecture is different - we use globals['__name__'] instead
|
||||
if let Some(module_name) = caller(vm) {
|
||||
// Special handling for certain module names
|
||||
if let Ok(name_str) = module_name.str(vm) {
|
||||
let name = name_str.as_str();
|
||||
// CPython sets __module__ to None for builtins and <...> modules
|
||||
if name == "builtins" || name.starts_with('<') {
|
||||
// Don't set __module__ attribute at all (CPython behavior)
|
||||
// This allows the typing module to handle it
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
obj.set_attr("__module__", module_name, vm)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user