Skip to main content

rustpython_vm/stdlib/
typevar.rs

1// spell-checker:ignore typevarobject funcobj
2
3pub use typevar::*;
4
5#[pymodule(sub, name = "_typing")]
6pub(crate) mod typevar {
7    use crate::{
8        AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
9        builtins::{PyTuple, PyTupleRef, PyType, PyTypeRef, make_union},
10        common::lock::PyMutex,
11        function::{FuncArgs, PyComparisonValue},
12        protocol::PyNumberMethods,
13        stdlib::_typing::{call_typing_func_object, decl::const_evaluator_alloc},
14        types::{AsNumber, Comparable, Constructor, Iterable, PyComparisonOp, Representable},
15    };
16
17    fn type_check(arg: PyObjectRef, msg: &str, vm: &VirtualMachine) -> PyResult<PyObjectRef> {
18        // Calling typing.py here leads to bootstrapping problems
19        if vm.is_none(&arg) {
20            return Ok(arg.class().to_owned().into());
21        }
22        let message_str: PyObjectRef = vm.ctx.new_str(msg).into();
23        call_typing_func_object(vm, "_type_check", (arg, message_str))
24    }
25
26    fn variance_repr(
27        name: &str,
28        infer_variance: bool,
29        covariant: bool,
30        contravariant: bool,
31    ) -> String {
32        if infer_variance {
33            return name.to_owned();
34        }
35        let prefix = if covariant {
36            '+'
37        } else if contravariant {
38            '-'
39        } else {
40            '~'
41        };
42        format!("{prefix}{name}")
43    }
44
45    /// Get the module of the caller frame, similar to CPython's caller() function.
46    /// Returns the module name or None if not found.
47    ///
48    /// Note: CPython's implementation (in typevarobject.c) gets the module from the
49    /// frame's function object using PyFunction_GetModule(f->f_funcobj). However,
50    /// RustPython's FrameObject doesn't store a reference to the function object, so we
51    /// get the module name from the frame's globals dictionary instead.
52    fn caller(vm: &VirtualMachine) -> Option<PyObjectRef> {
53        let globals = crate::frame::current_globals()?;
54        globals.get_item("__name__", vm).ok()
55    }
56
57    /// Set __module__ attribute for an object based on the caller's module.
58    /// This follows CPython's behavior for TypeVar and similar objects.
59    fn set_module_from_caller(obj: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
60        // Note: CPython gets module from frame->f_funcobj, but RustPython's FrameObject
61        // architecture is different - we use globals['__name__'] instead
62        let module_value: PyObjectRef = if let Some(module_name) = caller(vm) {
63            // Special handling for certain module names
64            if let Ok(name_str) = module_name.str(vm)
65                && let Some(name) = name_str.to_str()
66                && (name == "builtins" || name.starts_with('<'))
67            {
68                return Ok(());
69            }
70            module_name
71        } else {
72            vm.ctx.none()
73        };
74        obj.set_attr("__module__", module_value, vm)?;
75        Ok(())
76    }
77
78    #[pyattr]
79    #[pyclass(name = "TypeVar", module = "typing")]
80    #[derive(Debug, PyPayload)]
81    pub struct TypeVar {
82        #[pymember(name = "__name__")]
83        name: PyObjectRef, // TODO PyStrRef?
84        bound: PyMutex<PyObjectRef>,
85        evaluate_bound: PyObjectRef,
86        constraints: PyMutex<PyObjectRef>,
87        evaluate_constraints: PyObjectRef,
88        default_value: PyMutex<PyObjectRef>,
89        evaluate_default: PyMutex<PyObjectRef>,
90        #[pymember(name = "__covariant__")]
91        covariant: bool,
92        #[pymember(name = "__contravariant__")]
93        contravariant: bool,
94        #[pymember(name = "__infer_variance__")]
95        infer_variance: bool,
96    }
97
98    #[pyclass(
99        flags(HAS_DICT, HAS_WEAKREF),
100        with(AsNumber, Constructor, Representable)
101    )]
102    impl Py<TypeVar> {
103        #[pymethod]
104        fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
105            Err(vm.new_type_error("Cannot subclass an instance of TypeVar"))
106        }
107
108        #[pygetset]
109        fn __constraints__(&self, vm: &VirtualMachine) -> PyResult {
110            let mut constraints = self.constraints.lock();
111            if !vm.is_none(&constraints) {
112                return Ok(constraints.clone());
113            }
114            let r = if !vm.is_none(&self.evaluate_constraints) {
115                *constraints = self.evaluate_constraints.call((1i32,), vm)?;
116                constraints.clone()
117            } else {
118                vm.ctx.empty_tuple.clone().into()
119            };
120            Ok(r)
121        }
122
123        #[pygetset]
124        fn __bound__(&self, vm: &VirtualMachine) -> PyResult {
125            let mut bound = self.bound.lock();
126            if !vm.is_none(&bound) {
127                return Ok(bound.clone());
128            }
129            let r = if !vm.is_none(&self.evaluate_bound) {
130                *bound = self.evaluate_bound.call((1i32,), vm)?;
131                bound.clone()
132            } else {
133                vm.ctx.none()
134            };
135            Ok(r)
136        }
137
138        #[pygetset]
139        fn __default__(&self, vm: &VirtualMachine) -> PyResult {
140            {
141                let default_value = self.default_value.lock();
142                if !default_value.is(&vm.ctx.typing_no_default) {
143                    return Ok(default_value.clone());
144                }
145            }
146            let evaluator = self.evaluate_default.lock().clone();
147            if !vm.is_none(&evaluator) {
148                let result = evaluator.call((1i32,), vm)?;
149                *self.default_value.lock() = result.clone();
150                Ok(result)
151            } else {
152                Ok(vm.ctx.typing_no_default.clone().into())
153            }
154        }
155
156        #[pygetset]
157        fn evaluate_bound(&self, vm: &VirtualMachine) -> PyObjectRef {
158            if !vm.is_none(&self.evaluate_bound) {
159                return self.evaluate_bound.clone();
160            }
161
162            let bound = self.bound.lock();
163            if !vm.is_none(&bound) {
164                return const_evaluator_alloc(bound.clone(), vm);
165            }
166
167            vm.ctx.none()
168        }
169
170        #[pygetset]
171        fn evaluate_constraints(&self, vm: &VirtualMachine) -> PyObjectRef {
172            if !vm.is_none(&self.evaluate_constraints) {
173                return self.evaluate_constraints.clone();
174            }
175
176            let constraints = self.constraints.lock();
177            if !vm.is_none(&constraints) {
178                return const_evaluator_alloc(constraints.clone(), vm);
179            }
180
181            vm.ctx.none()
182        }
183
184        #[pygetset]
185        fn evaluate_default(&self, vm: &VirtualMachine) -> PyObjectRef {
186            let evaluator = self.evaluate_default.lock().clone();
187            if !vm.is_none(&evaluator) {
188                return evaluator;
189            }
190
191            let default_value = self.default_value.lock().clone();
192            if !default_value.is(&vm.ctx.typing_no_default) {
193                return const_evaluator_alloc(default_value, vm);
194            }
195
196            vm.ctx.none()
197        }
198
199        #[pymethod]
200        fn __typing_subst__(
201            zelf: crate::PyRef<TypeVar>,
202            arg: PyObjectRef,
203            vm: &VirtualMachine,
204        ) -> PyResult {
205            let self_obj: PyObjectRef = zelf.into();
206            call_typing_func_object(vm, "_typevar_subst", (self_obj, arg))
207        }
208
209        #[pymethod]
210        fn __reduce__(&self) -> PyObjectRef {
211            self.name.clone()
212        }
213
214        #[pymethod]
215        fn has_default(&self, vm: &VirtualMachine) -> bool {
216            if !vm.is_none(&self.evaluate_default.lock()) {
217                return true;
218            }
219            let default_value = self.default_value.lock();
220            // Check if default_value is not NoDefault
221            !default_value.is(&vm.ctx.typing_no_default)
222        }
223
224        #[pymethod]
225        fn __typing_prepare_subst__(
226            zelf: crate::PyRef<TypeVar>,
227            alias: PyObjectRef,
228            args: PyObjectRef,
229            vm: &VirtualMachine,
230        ) -> PyResult {
231            // Convert args to tuple if needed
232            let args_tuple =
233                if let Ok(tuple) = args.try_to_ref::<rustpython_vm::builtins::PyTuple>(vm) {
234                    tuple
235                } else {
236                    return Ok(args);
237                };
238
239            // Get alias.__parameters__
240            let parameters = alias.get_attr(identifier!(vm, __parameters__), vm)?;
241            let params_tuple: PyTupleRef = parameters.try_into_value(vm)?;
242
243            // Find our index in parameters
244            let self_obj: PyObjectRef = zelf.to_owned().into();
245            let param_index = params_tuple.as_slice().iter().position(|p| p.is(&self_obj));
246
247            if let Some(index) = param_index {
248                // Check if we have enough arguments
249                if args_tuple.as_slice().len() <= index && zelf.has_default(vm) {
250                    // Need to add default value
251                    let mut new_args = args_tuple.as_slice().to_vec();
252
253                    // Add default value at the correct position
254                    while new_args.len() <= index {
255                        // For the current parameter, add its default
256                        if new_args.len() == index {
257                            let default_val = zelf.__default__(vm)?;
258                            new_args.push(default_val);
259                        } else {
260                            // This shouldn't happen in well-formed code
261                            break;
262                        }
263                    }
264
265                    return Ok(rustpython_vm::builtins::PyTuple::new_ref(new_args, &vm.ctx).into());
266                }
267            }
268
269            // No changes needed
270            Ok(args)
271        }
272    }
273
274    impl Representable for TypeVar {
275        #[inline(always)]
276        fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
277            let name = zelf.name.str_utf8(vm)?;
278            Ok(variance_repr(
279                name.as_str(),
280                zelf.infer_variance,
281                zelf.covariant,
282                zelf.contravariant,
283            ))
284        }
285    }
286
287    impl AsNumber for TypeVar {
288        fn as_number() -> &'static PyNumberMethods {
289            static AS_NUMBER: PyNumberMethods = PyNumberMethods {
290                or: Some(|a, b, vm| {
291                    let args = PyTuple::new_ref(vec![a.to_owned(), b.to_owned()], &vm.ctx);
292                    make_union(&args, vm)
293                }),
294                ..PyNumberMethods::NOT_IMPLEMENTED
295            };
296            &AS_NUMBER
297        }
298    }
299
300    impl Constructor for TypeVar {
301        type Args = FuncArgs;
302
303        fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
304            let typevar = <Self as Constructor>::py_new(&cls, args, vm)?;
305            let obj = typevar.into_ref_with_type(vm, cls)?;
306            let obj_ref: PyObjectRef = obj.into();
307            set_module_from_caller(&obj_ref, vm)?;
308            Ok(obj_ref)
309        }
310
311        fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
312            let mut kwargs = args.kwargs;
313            // Parse arguments manually
314            let (name, constraints) = if args.args.is_empty() {
315                // Check if name is provided as keyword argument
316                if let Some(name) = kwargs.swap_remove("name") {
317                    (name, vec![])
318                } else {
319                    return Err(
320                        vm.new_type_error("TypeVar() missing required argument: 'name' (pos 1)")
321                    );
322                }
323            } else if args.args.len() == 1 {
324                (args.args[0].clone(), vec![])
325            } else {
326                let name = args.args[0].clone();
327                let constraints = args.args[1..].to_vec();
328                (name, constraints)
329            };
330
331            let bound = kwargs.swap_remove("bound");
332            let covariant = kwargs
333                .swap_remove("covariant")
334                .map(|v| v.try_to_bool(vm))
335                .transpose()?
336                .unwrap_or(false);
337            let contravariant = kwargs
338                .swap_remove("contravariant")
339                .map(|v| v.try_to_bool(vm))
340                .transpose()?
341                .unwrap_or(false);
342            let infer_variance = kwargs
343                .swap_remove("infer_variance")
344                .map(|v| v.try_to_bool(vm))
345                .transpose()?
346                .unwrap_or(false);
347            let default = kwargs.swap_remove("default");
348
349            // Check for unexpected keyword arguments
350            if let Some(invalid_key) = kwargs.keys().next() {
351                return Err(
352                    vm.new_unexpected_keyword_type_error(Some("typevar"), &invalid_key.to_string())
353                );
354            }
355
356            // Check for invalid combinations
357            if covariant && contravariant {
358                return Err(vm.new_value_error("Bivariant type variables are not supported."));
359            }
360
361            if infer_variance && (covariant || contravariant) {
362                return Err(vm.new_value_error("Variance cannot be specified with infer_variance"));
363            }
364
365            // Handle constraints and bound
366            let (constraints_obj, evaluate_constraints) = if !constraints.is_empty() {
367                // Check for single constraint
368                if constraints.len() == 1 {
369                    return Err(vm.new_type_error("A single constraint is not allowed"));
370                }
371                if bound.is_some() {
372                    return Err(vm.new_type_error("Constraints cannot be used with bound"));
373                }
374                let constraints_tuple = vm.ctx.new_tuple(constraints);
375                (constraints_tuple.into(), vm.ctx.none())
376            } else {
377                (vm.ctx.none(), vm.ctx.none())
378            };
379
380            // Handle bound
381            let (bound_obj, evaluate_bound) = if let Some(bound) = bound {
382                if vm.is_none(&bound) {
383                    (vm.ctx.none(), vm.ctx.none())
384                } else {
385                    // Type check the bound
386                    let bound = type_check(bound, "Bound must be a type.", vm)?;
387                    (bound, vm.ctx.none())
388                }
389            } else {
390                (vm.ctx.none(), vm.ctx.none())
391            };
392
393            // Handle default value
394            let (default_value, evaluate_default) = if let Some(default) = default {
395                (default, vm.ctx.none())
396            } else {
397                // If no default provided, use NoDefault singleton
398                (vm.ctx.typing_no_default.clone().into(), vm.ctx.none())
399            };
400
401            Ok(Self {
402                name,
403                bound: PyMutex::new(bound_obj),
404                evaluate_bound,
405                constraints: PyMutex::new(constraints_obj),
406                evaluate_constraints,
407                default_value: PyMutex::new(default_value),
408                evaluate_default: PyMutex::new(evaluate_default),
409                covariant,
410                contravariant,
411                infer_variance,
412            })
413        }
414    }
415
416    impl TypeVar {
417        pub fn new(
418            vm: &VirtualMachine,
419            name: PyObjectRef,
420            evaluate_bound: PyObjectRef,
421            evaluate_constraints: PyObjectRef,
422        ) -> Self {
423            Self {
424                name,
425                bound: PyMutex::new(vm.ctx.none()),
426                evaluate_bound,
427                constraints: PyMutex::new(vm.ctx.none()),
428                evaluate_constraints,
429                default_value: PyMutex::new(vm.ctx.typing_no_default.clone().into()),
430                evaluate_default: PyMutex::new(vm.ctx.none()),
431                covariant: false,
432                contravariant: false,
433                infer_variance: true,
434            }
435        }
436    }
437
438    #[pyattr]
439    #[pyclass(name = "ParamSpec", module = "typing")]
440    #[derive(Debug, PyPayload)]
441    pub struct ParamSpec {
442        #[pymember(name = "__name__")]
443        name: PyObjectRef,
444        #[pymember(name = "__bound__")]
445        bound: Option<PyObjectRef>,
446        default_value: PyMutex<PyObjectRef>,
447        evaluate_default: PyMutex<PyObjectRef>,
448        #[pymember(name = "__covariant__")]
449        covariant: bool,
450        #[pymember(name = "__contravariant__")]
451        contravariant: bool,
452        #[pymember(name = "__infer_variance__")]
453        infer_variance: bool,
454    }
455
456    #[pyclass(
457        flags(HAS_DICT, HAS_WEAKREF),
458        with(AsNumber, Constructor, Representable)
459    )]
460    impl Py<ParamSpec> {
461        #[pymethod]
462        fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
463            Err(vm.new_type_error("Cannot subclass an instance of ParamSpec"))
464        }
465
466        #[pygetset]
467        fn args(zelf: crate::PyRef<ParamSpec>, vm: &VirtualMachine) -> PyObjectRef {
468            let self_obj: PyObjectRef = zelf.into();
469            let psa = ParamSpecArgs {
470                __origin__: self_obj,
471            };
472            psa.into_ref(&vm.ctx).into()
473        }
474
475        #[pygetset]
476        fn kwargs(zelf: crate::PyRef<ParamSpec>, vm: &VirtualMachine) -> PyObjectRef {
477            let self_obj: PyObjectRef = zelf.into();
478            let psk = ParamSpecKwargs {
479                __origin__: self_obj,
480            };
481            psk.into_ref(&vm.ctx).into()
482        }
483
484        #[pygetset]
485        fn __default__(&self, vm: &VirtualMachine) -> PyResult {
486            {
487                let default_value = self.default_value.lock();
488                if !default_value.is(&vm.ctx.typing_no_default) {
489                    return Ok(default_value.clone());
490                }
491            }
492            let evaluator = self.evaluate_default.lock().clone();
493            if !vm.is_none(&evaluator) {
494                let result = evaluator.call((1i32,), vm)?;
495                *self.default_value.lock() = result.clone();
496                Ok(result)
497            } else {
498                Ok(vm.ctx.typing_no_default.clone().into())
499            }
500        }
501
502        #[pygetset]
503        fn evaluate_default(&self, vm: &VirtualMachine) -> PyObjectRef {
504            let evaluator = self.evaluate_default.lock().clone();
505            if !vm.is_none(&evaluator) {
506                return evaluator;
507            }
508
509            let default_value = self.default_value.lock().clone();
510            if !default_value.is(&vm.ctx.typing_no_default) {
511                return const_evaluator_alloc(default_value, vm);
512            }
513
514            vm.ctx.none()
515        }
516
517        #[pymethod]
518        fn __reduce__(&self) -> PyObjectRef {
519            self.name.clone()
520        }
521
522        #[pymethod]
523        fn has_default(&self, vm: &VirtualMachine) -> bool {
524            if !vm.is_none(&self.evaluate_default.lock()) {
525                return true;
526            }
527            !self.default_value.lock().is(&vm.ctx.typing_no_default)
528        }
529
530        #[pymethod]
531        fn __typing_subst__(
532            zelf: crate::PyRef<ParamSpec>,
533            arg: PyObjectRef,
534            vm: &VirtualMachine,
535        ) -> PyResult {
536            let self_obj: PyObjectRef = zelf.into();
537            call_typing_func_object(vm, "_paramspec_subst", (self_obj, arg))
538        }
539
540        #[pymethod]
541        fn __typing_prepare_subst__(
542            zelf: crate::PyRef<ParamSpec>,
543            alias: PyObjectRef,
544            args: PyObjectRef,
545            vm: &VirtualMachine,
546        ) -> PyResult {
547            let self_obj: PyObjectRef = zelf.into();
548            call_typing_func_object(vm, "_paramspec_prepare_subst", (self_obj, alias, args))
549        }
550    }
551
552    impl AsNumber for ParamSpec {
553        fn as_number() -> &'static PyNumberMethods {
554            static AS_NUMBER: PyNumberMethods = PyNumberMethods {
555                or: Some(|a, b, vm| {
556                    let args = PyTuple::new_ref(vec![a.to_owned(), b.to_owned()], &vm.ctx);
557                    make_union(&args, vm)
558                }),
559                ..PyNumberMethods::NOT_IMPLEMENTED
560            };
561            &AS_NUMBER
562        }
563    }
564
565    impl Constructor for ParamSpec {
566        type Args = FuncArgs;
567
568        fn slot_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
569            let mut kwargs = args.kwargs;
570            // Parse arguments manually
571            let name = if args.args.is_empty() {
572                // Check if name is provided as keyword argument
573                if let Some(name) = kwargs.swap_remove("name") {
574                    name
575                } else {
576                    return Err(
577                        vm.new_type_error("ParamSpec() missing required argument: 'name' (pos 1)")
578                    );
579                }
580            } else if args.args.len() == 1 {
581                args.args[0].clone()
582            } else {
583                return Err(vm.new_type_error("ParamSpec() takes at most 1 positional argument"));
584            };
585
586            let bound = kwargs
587                .swap_remove("bound")
588                .map(|b| type_check(b, "Bound must be a type.", vm))
589                .transpose()?;
590            let covariant = kwargs
591                .swap_remove("covariant")
592                .map(|v| v.try_to_bool(vm))
593                .transpose()?
594                .unwrap_or(false);
595            let contravariant = kwargs
596                .swap_remove("contravariant")
597                .map(|v| v.try_to_bool(vm))
598                .transpose()?
599                .unwrap_or(false);
600            let infer_variance = kwargs
601                .swap_remove("infer_variance")
602                .map(|v| v.try_to_bool(vm))
603                .transpose()?
604                .unwrap_or(false);
605            let default = kwargs.swap_remove("default");
606
607            // Check for unexpected keyword arguments
608            if let Some(invalid_key) = kwargs.keys().next() {
609                return Err(vm.new_unexpected_keyword_type_error(
610                    Some("paramspec"),
611                    &invalid_key.to_string(),
612                ));
613            }
614
615            // Check for invalid combinations
616            if covariant && contravariant {
617                return Err(vm.new_value_error("Bivariant type variables are not supported."));
618            }
619
620            if infer_variance && (covariant || contravariant) {
621                return Err(vm.new_value_error("Variance cannot be specified with infer_variance"));
622            }
623
624            // Handle default value
625            let default_value = default.unwrap_or_else(|| vm.ctx.typing_no_default.clone().into());
626
627            let paramspec = Self {
628                name,
629                bound,
630                default_value: PyMutex::new(default_value),
631                evaluate_default: PyMutex::new(vm.ctx.none()),
632                covariant,
633                contravariant,
634                infer_variance,
635            };
636
637            let obj = paramspec.into_ref_with_type(vm, cls)?;
638            let obj_ref: PyObjectRef = obj.into();
639            set_module_from_caller(&obj_ref, vm)?;
640            Ok(obj_ref)
641        }
642
643        fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
644            unimplemented!("use slot_new")
645        }
646    }
647
648    impl Representable for ParamSpec {
649        #[inline(always)]
650        fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
651            let name = zelf.name.str_utf8(vm)?;
652            Ok(variance_repr(
653                name.as_str(),
654                zelf.infer_variance,
655                zelf.covariant,
656                zelf.contravariant,
657            ))
658        }
659    }
660
661    impl ParamSpec {
662        pub fn new(name: PyObjectRef, vm: &VirtualMachine) -> Self {
663            Self {
664                name,
665                bound: None,
666                default_value: PyMutex::new(vm.ctx.typing_no_default.clone().into()),
667                evaluate_default: PyMutex::new(vm.ctx.none()),
668                covariant: false,
669                contravariant: false,
670                infer_variance: true,
671            }
672        }
673    }
674
675    #[pyattr]
676    #[pyclass(name = "TypeVarTuple", module = "typing")]
677    #[derive(Debug, PyPayload)]
678    pub struct TypeVarTuple {
679        #[pymember(name = "__name__")]
680        name: PyObjectRef,
681        default_value: PyMutex<PyObjectRef>,
682        evaluate_default: PyMutex<PyObjectRef>,
683    }
684
685    #[pyclass(
686        flags(HAS_DICT, HAS_WEAKREF),
687        with(Constructor, Representable, Iterable)
688    )]
689    impl Py<TypeVarTuple> {
690        #[pygetset]
691        fn __default__(&self, vm: &VirtualMachine) -> PyResult {
692            {
693                let default_value = self.default_value.lock();
694                if !default_value.is(&vm.ctx.typing_no_default) {
695                    return Ok(default_value.clone());
696                }
697            }
698            let evaluator = self.evaluate_default.lock().clone();
699            if !vm.is_none(&evaluator) {
700                let result = evaluator.call((1i32,), vm)?;
701                *self.default_value.lock() = result.clone();
702                Ok(result)
703            } else {
704                Ok(vm.ctx.typing_no_default.clone().into())
705            }
706        }
707
708        #[pygetset]
709        fn evaluate_default(&self, vm: &VirtualMachine) -> PyObjectRef {
710            let evaluator = self.evaluate_default.lock().clone();
711            if !vm.is_none(&evaluator) {
712                return evaluator;
713            }
714
715            let default_value = self.default_value.lock().clone();
716            if !default_value.is(&vm.ctx.typing_no_default) {
717                return const_evaluator_alloc(default_value, vm);
718            }
719
720            vm.ctx.none()
721        }
722
723        #[pymethod]
724        fn has_default(&self, vm: &VirtualMachine) -> bool {
725            if !vm.is_none(&self.evaluate_default.lock()) {
726                return true;
727            }
728            let default_value = self.default_value.lock();
729            !default_value.is(&vm.ctx.typing_no_default)
730        }
731
732        #[pymethod]
733        fn __reduce__(&self) -> PyObjectRef {
734            self.name.clone()
735        }
736
737        #[pymethod]
738        fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
739            Err(vm.new_type_error("Cannot subclass an instance of TypeVarTuple"))
740        }
741
742        #[pymethod]
743        fn __typing_subst__(&self, _arg: PyObjectRef, vm: &VirtualMachine) -> PyResult {
744            Err(vm.new_type_error("Substitution of bare TypeVarTuple is not supported"))
745        }
746
747        #[pymethod]
748        fn __typing_prepare_subst__(
749            zelf: crate::PyRef<TypeVarTuple>,
750            alias: PyObjectRef,
751            args: PyObjectRef,
752            vm: &VirtualMachine,
753        ) -> PyResult {
754            let self_obj: PyObjectRef = zelf.into();
755            call_typing_func_object(vm, "_typevartuple_prepare_subst", (self_obj, alias, args))
756        }
757    }
758
759    impl Iterable for TypeVarTuple {
760        fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
761            // When unpacking TypeVarTuple with *, return [Unpack[self]]
762            // This is how CPython handles Generic[*Ts]
763            let typing = vm.import("typing", 0)?;
764            let unpack = typing.get_attr("Unpack", vm)?;
765            let zelf_obj: PyObjectRef = zelf.into();
766            let unpacked = vm.call_method(&unpack, "__getitem__", (zelf_obj,))?;
767            let list = vm.ctx.new_list(vec![unpacked]);
768            let list_obj: PyObjectRef = list.into();
769            vm.call_method(&list_obj, "__iter__", ())
770        }
771    }
772
773    impl Constructor for TypeVarTuple {
774        type Args = FuncArgs;
775
776        fn slot_new(cls: PyTypeRef, args: Self::Args, vm: &VirtualMachine) -> PyResult {
777            let mut kwargs = args.kwargs;
778            // Parse arguments manually
779            let name = if args.args.is_empty() {
780                // Check if name is provided as keyword argument
781                if let Some(name) = kwargs.swap_remove("name") {
782                    name
783                } else {
784                    return Err(vm.new_type_error(
785                        "TypeVarTuple() missing required argument: 'name' (pos 1)",
786                    ));
787                }
788            } else if args.args.len() == 1 {
789                args.args[0].clone()
790            } else {
791                return Err(vm.new_type_error("TypeVarTuple() takes at most 1 positional argument"));
792            };
793
794            let default = kwargs.swap_remove("default");
795
796            // Check for unexpected keyword arguments
797            if let Some(invalid_key) = kwargs.keys().next() {
798                return Err(vm.new_unexpected_keyword_type_error(
799                    Some("typevartuple"),
800                    &invalid_key.to_string(),
801                ));
802            }
803
804            // Handle default value
805            let (default_value, evaluate_default) = if let Some(default) = default {
806                (default, vm.ctx.none())
807            } else {
808                // If no default provided, use NoDefault singleton
809                (vm.ctx.typing_no_default.clone().into(), vm.ctx.none())
810            };
811
812            let typevartuple = Self {
813                name,
814                default_value: PyMutex::new(default_value),
815                evaluate_default: PyMutex::new(evaluate_default),
816            };
817
818            let obj = typevartuple.into_ref_with_type(vm, cls)?;
819            let obj_ref: PyObjectRef = obj.into();
820            set_module_from_caller(&obj_ref, vm)?;
821            Ok(obj_ref)
822        }
823
824        fn py_new(_cls: &Py<PyType>, _args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
825            unimplemented!("use slot_new")
826        }
827    }
828
829    impl Representable for TypeVarTuple {
830        #[inline(always)]
831        fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
832            let name = zelf.name.str(vm)?;
833            Ok(name.to_string())
834        }
835    }
836
837    impl TypeVarTuple {
838        pub fn new(name: PyObjectRef, vm: &VirtualMachine) -> Self {
839            Self {
840                name,
841                default_value: PyMutex::new(vm.ctx.typing_no_default.clone().into()),
842                evaluate_default: PyMutex::new(vm.ctx.none()),
843            }
844        }
845    }
846
847    #[pyattr]
848    #[pyclass(name = "ParamSpecArgs", module = "typing", unhashable = true)]
849    #[derive(Debug, PyPayload)]
850    pub struct ParamSpecArgs {
851        #[pymember]
852        __origin__: PyObjectRef,
853    }
854
855    #[pyclass(with(Constructor, Representable, Comparable), flags(HAS_WEAKREF))]
856    impl Py<ParamSpecArgs> {
857        #[pymethod]
858        fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
859            Err(vm.new_type_error("Cannot subclass an instance of ParamSpecArgs"))
860        }
861    }
862
863    impl Constructor for ParamSpecArgs {
864        type Args = (PyObjectRef,);
865
866        fn py_new(_cls: &Py<PyType>, args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
867            let origin = args.0;
868            Ok(Self { __origin__: origin })
869        }
870    }
871
872    impl Representable for ParamSpecArgs {
873        #[inline(always)]
874        fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
875            // A ParamSpec origin is named; anything else is shown by its repr,
876            // which carries the recursion guard a Rust `{:?}` walk does not.
877            if let Some(param_spec) = zelf.__origin__.downcast_ref::<ParamSpec>() {
878                return Ok(format!("{}.args", param_spec.name.str_utf8(vm)?));
879            }
880            Ok(format!("{}.args", zelf.__origin__.repr(vm)?))
881        }
882    }
883
884    impl Comparable for ParamSpecArgs {
885        fn cmp(
886            zelf: &crate::Py<Self>,
887            other: &PyObject,
888            op: PyComparisonOp,
889            vm: &VirtualMachine,
890        ) -> PyResult<PyComparisonValue> {
891            op.eq_only(|| {
892                if other.class().is(zelf.class())
893                    && let Some(other_args) = other.downcast_ref::<Self>()
894                {
895                    let eq = zelf.__origin__.rich_compare_bool(
896                        &other_args.__origin__,
897                        PyComparisonOp::Eq,
898                        vm,
899                    )?;
900                    return Ok(PyComparisonValue::Implemented(eq));
901                }
902                Ok(PyComparisonValue::NotImplemented)
903            })
904        }
905    }
906
907    #[pyattr]
908    #[pyclass(name = "ParamSpecKwargs", module = "typing", unhashable = true)]
909    #[derive(Debug, PyPayload)]
910    pub struct ParamSpecKwargs {
911        #[pymember]
912        __origin__: PyObjectRef,
913    }
914
915    #[pyclass(with(Constructor, Representable, Comparable), flags(HAS_WEAKREF))]
916    impl Py<ParamSpecKwargs> {
917        #[pymethod]
918        fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyResult {
919            Err(vm.new_type_error("Cannot subclass an instance of ParamSpecKwargs"))
920        }
921    }
922
923    impl Constructor for ParamSpecKwargs {
924        type Args = (PyObjectRef,);
925
926        fn py_new(_cls: &Py<PyType>, args: Self::Args, _vm: &VirtualMachine) -> PyResult<Self> {
927            let origin = args.0;
928            Ok(Self { __origin__: origin })
929        }
930    }
931
932    impl Representable for ParamSpecKwargs {
933        #[inline(always)]
934        fn repr_str(zelf: &crate::Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
935            // A ParamSpec origin is named; anything else is shown by its repr,
936            // which carries the recursion guard a Rust `{:?}` walk does not.
937            if let Some(param_spec) = zelf.__origin__.downcast_ref::<ParamSpec>() {
938                return Ok(format!("{}.kwargs", param_spec.name.str_utf8(vm)?));
939            }
940            Ok(format!("{}.kwargs", zelf.__origin__.repr(vm)?))
941        }
942    }
943
944    impl Comparable for ParamSpecKwargs {
945        fn cmp(
946            zelf: &crate::Py<Self>,
947            other: &PyObject,
948            op: PyComparisonOp,
949            vm: &VirtualMachine,
950        ) -> PyResult<PyComparisonValue> {
951            op.eq_only(|| {
952                if other.class().is(zelf.class())
953                    && let Some(other_kwargs) = other.downcast_ref::<Self>()
954                {
955                    let eq = zelf.__origin__.rich_compare_bool(
956                        &other_kwargs.__origin__,
957                        PyComparisonOp::Eq,
958                        vm,
959                    )?;
960                    return Ok(PyComparisonValue::Implemented(eq));
961                }
962                Ok(PyComparisonValue::NotImplemented)
963            })
964        }
965    }
966
967    /// Helper function to call typing module functions with cls as first argument
968    /// Similar to CPython's call_typing_args_kwargs
969    fn call_typing_args_kwargs(
970        name: &'static str,
971        cls: PyTypeRef,
972        args: FuncArgs,
973        vm: &VirtualMachine,
974    ) -> PyResult {
975        let typing = vm.import("typing", 0)?;
976        let func = typing.get_attr(name, vm)?;
977
978        // Prepare arguments: (cls, *args)
979        let mut call_args = vec![cls.into()];
980        call_args.extend(args.args);
981
982        // Call with prepared args and original kwargs
983        let func_args = FuncArgs {
984            args: call_args,
985            kwargs: args.kwargs,
986        };
987
988        func.call(func_args, vm)
989    }
990
991    #[pyattr]
992    #[pyclass(name = "Generic", module = "typing")]
993    #[derive(Debug, PyPayload)]
994    pub struct Generic;
995
996    #[pyclass(flags(BASETYPE, HEAPTYPE))]
997    impl Generic {
998        #[pyattr]
999        fn __slots__(ctx: &Context) -> PyTupleRef {
1000            ctx.empty_tuple.clone()
1001        }
1002
1003        #[pyclassmethod]
1004        fn __class_getitem__(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1005            call_typing_args_kwargs("_generic_class_getitem", cls, args, vm)
1006        }
1007
1008        #[pyclassmethod]
1009        fn __init_subclass__(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
1010            call_typing_args_kwargs("_generic_init_subclass", cls, args, vm)
1011        }
1012    }
1013
1014    /// Sets the default value for a type parameter, equivalent to CPython's _Py_set_typeparam_default
1015    /// This is used by the CALL_INTRINSIC_2 SetTypeparamDefault instruction
1016    pub fn set_typeparam_default(
1017        type_param: PyObjectRef,
1018        evaluate_default: PyObjectRef,
1019        vm: &VirtualMachine,
1020    ) -> PyResult {
1021        // Inner function to handle common pattern of setting evaluate_default
1022        fn try_set_default<T>(
1023            obj: &PyObject,
1024            evaluate_default: &PyObject,
1025            get_field: impl FnOnce(&T) -> &PyMutex<PyObjectRef>,
1026        ) -> bool
1027        where
1028            T: PyPayload,
1029        {
1030            if let Some(typed_obj) = obj.downcast_ref::<T>() {
1031                *get_field(typed_obj).lock() = evaluate_default.to_owned();
1032                true
1033            } else {
1034                false
1035            }
1036        }
1037
1038        // Try each type parameter type
1039        if try_set_default::<TypeVar>(&type_param, &evaluate_default, |tv| &tv.evaluate_default)
1040            || try_set_default::<ParamSpec>(&type_param, &evaluate_default, |ps| {
1041                &ps.evaluate_default
1042            })
1043            || try_set_default::<TypeVarTuple>(&type_param, &evaluate_default, |tvt| {
1044                &tvt.evaluate_default
1045            })
1046        {
1047            Ok(type_param)
1048        } else {
1049            Err(vm.new_type_error(format!(
1050                "Expected a type param, got {}",
1051                type_param.class().name()
1052            )))
1053        }
1054    }
1055}