1use crate::common::lock::LazyLock;
3
4use super::type_;
5use crate::{
6 AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
7 VirtualMachine, atomic_func,
8 builtins::{PyList, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef},
9 class::{PyClassDef, PyClassImpl},
10 common::hash,
11 convert::ToPyObject,
12 function::{FuncArgs, PyComparisonValue},
13 protocol::{PyMappingMethods, PyNumberMethods},
14 types::{
15 AsMapping, AsNumber, Callable, Comparable, Constructor, GetAttr, Hashable, IterNext,
16 Iterable, PyComparisonOp, Representable,
17 },
18};
19use alloc::fmt;
20
21static ATTR_EXCEPTIONS: [&str; 9] = [
23 "__class__",
24 "__origin__",
25 "__args__",
26 "__unpacked__",
27 "__parameters__",
28 "__typing_unpacked_tuple_args__",
29 "__mro_entries__",
30 "__reduce_ex__", "__reduce__",
32];
33
34static ATTR_BLOCKED: [&str; 3] = ["__bases__", "__copy__", "__deepcopy__"];
36
37#[pyclass(module = "types", name = "GenericAlias")]
38pub struct PyGenericAlias {
39 #[pymember(name = "__origin__")]
40 origin: PyObjectRef,
41 #[pymember(name = "__args__")]
42 args: PyTupleRef,
43 parameters: PyTupleRef,
44 #[pymember(name = "__unpacked__")]
45 starred: bool, }
47
48impl fmt::Debug for PyGenericAlias {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 f.write_str("GenericAlias")
51 }
52}
53
54impl PyPayload for PyGenericAlias {
55 #[inline]
56 fn class(ctx: &Context) -> &'static Py<PyType> {
57 ctx.types.generic_alias_type
58 }
59}
60
61#[derive(FromArgs)]
62pub struct GenericAliasArgs {
63 #[pyarg(positional)]
64 origin: PyObjectRef,
65 #[pyarg(positional)]
66 args: PyObjectRef,
67}
68
69impl Constructor for PyGenericAlias {
70 type Args = GenericAliasArgs;
71
72 fn slot_new(cls: PyTypeRef, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
73 if !args.kwargs.is_empty() {
74 return Err(vm.new_type_error("GenericAlias() takes no keyword arguments"));
75 }
76 let GenericAliasArgs {
77 origin,
78 args: arguments,
79 } = args.bind_for(vm, Self::NAME)?;
80 let arguments = if let Ok(tuple) = arguments.try_to_ref::<PyTuple>(vm) {
81 tuple.to_owned()
82 } else {
83 PyTuple::new_ref(vec![arguments], &vm.ctx)
84 };
85 let payload = Self::new(origin, arguments, false, vm)?;
86 payload.into_ref_with_type(vm, cls).map(Into::into)
87 }
88
89 fn py_new(_cls: &Py<PyType>, args: Self::Args, vm: &VirtualMachine) -> PyResult<Self> {
90 let GenericAliasArgs {
91 origin,
92 args: arguments,
93 } = args;
94 let args = if let Ok(tuple) = arguments.try_to_ref::<PyTuple>(vm) {
95 tuple.to_owned()
96 } else {
97 PyTuple::new_ref(vec![arguments], &vm.ctx)
98 };
99 Self::new(origin, args, false, vm)
100 }
101}
102
103impl PyGenericAlias {
104 pub fn new(
105 origin: impl Into<PyObjectRef>,
106 args: PyTupleRef,
107 starred: bool,
108 vm: &VirtualMachine,
109 ) -> PyResult<Self> {
110 let parameters = make_parameters(&args, vm)?;
111 Ok(Self {
112 origin: origin.into(),
113 args,
114 parameters,
115 starred,
116 })
117 }
118
119 pub fn from_args(
121 origin: impl Into<PyObjectRef>,
122 args: PyObjectRef,
123 vm: &VirtualMachine,
124 ) -> PyResult<Self> {
125 let args = if let Ok(tuple) = args.try_to_ref::<PyTuple>(vm) {
126 tuple.to_owned()
127 } else {
128 PyTuple::new_ref(vec![args], &vm.ctx)
129 };
130 Self::new(origin, args, false, vm)
131 }
132
133 fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
134 fn repr_item(obj: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
135 if obj.is(&vm.ctx.ellipsis) {
136 return Ok("...".to_string());
137 }
138
139 if vm
140 .get_attribute_opt(obj, identifier!(vm, __origin__))?
141 .is_some()
142 && vm
143 .get_attribute_opt(obj, identifier!(vm, __args__))?
144 .is_some()
145 {
146 return Ok(obj.repr(vm)?.to_string());
147 }
148
149 match (
150 vm.get_attribute_opt(obj, identifier!(vm, __qualname__))?
151 .and_then(|o| o.downcast_ref::<PyStr>().map(|n| n.to_string())),
152 vm.get_attribute_opt(obj, identifier!(vm, __module__))?
153 .and_then(|o| o.downcast_ref::<PyStr>().map(|m| m.to_string())),
154 ) {
155 (None, _) | (_, None) => Ok(obj.repr(vm)?.to_string()),
156 (Some(qualname), Some(module)) => Ok(if module == "builtins" {
157 qualname
158 } else {
159 format!("{module}.{qualname}")
160 }),
161 }
162 }
163
164 fn repr_arg(obj: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
165 if obj.class().is(vm.ctx.types.list_type) {
167 let list = obj.downcast_ref::<crate::builtins::PyList>().unwrap();
168 let len = list.borrow_vec().len();
169 let mut parts = Vec::with_capacity(len);
170 for i in 0..len {
172 let item = list
173 .borrow_vec()
174 .get(i)
175 .cloned()
176 .ok_or_else(|| vm.new_index_error("list index out of range"))?;
177 parts.push(repr_item(&item, vm)?);
178 }
179 Ok(format!("[{}]", parts.join(", ")))
180 } else {
181 repr_item(obj, vm)
182 }
183 }
184
185 let repr_str = format!(
186 "{}[{}]",
187 repr_item(&self.origin, vm)?,
188 if self.args.as_slice().is_empty() {
189 "()".to_owned()
190 } else {
191 self.args
192 .as_slice()
193 .iter()
194 .map(|o| repr_arg(o, vm))
195 .collect::<PyResult<Vec<_>>>()?
196 .join(", ")
197 }
198 );
199
200 Ok(if self.starred {
202 format!("*{repr_str}")
203 } else {
204 repr_str
205 })
206 }
207
208 fn __getitem__(zelf: &Py<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
209 let new_args = subs_parameters(zelf.as_object(), &zelf.args, &zelf.parameters, needle, vm)?;
210
211 Ok(Self::new(zelf.origin.clone(), new_args, false, vm)?.into_pyobject(vm))
212 }
213
214 fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
215 type_::or_(other, zelf, vm)
216 }
217
218 fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
219 type_::or_(zelf, other, vm)
220 }
221}
222
223#[pyclass(
224 with(
225 AsNumber,
226 AsMapping,
227 Callable,
228 Comparable,
229 Constructor,
230 GetAttr,
231 Hashable,
232 Iterable,
233 Representable
234 ),
235 flags(BASETYPE, HAS_WEAKREF)
236)]
237impl Py<PyGenericAlias> {
238 #[pygetset]
239 fn __parameters__(&self) -> PyObjectRef {
240 self.parameters.clone().into()
241 }
242
243 #[pygetset]
244 fn __typing_unpacked_tuple_args__(&self, vm: &VirtualMachine) -> PyObjectRef {
245 if self.starred && self.origin.is(vm.ctx.types.tuple_type.as_object()) {
246 self.args.clone().into()
247 } else {
248 vm.ctx.none()
249 }
250 }
251
252 #[pymethod]
253 fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyList> {
254 let dir = vm.dir(Some(self.origin.clone()))?;
255 for exc in &ATTR_EXCEPTIONS {
256 let exc_obj = (*exc).to_pyobject(vm);
257 if !dir.__contains__(&exc_obj, vm)? {
258 dir.append((*exc).to_pyobject(vm));
259 }
260 }
261 Ok(dir)
262 }
263
264 #[pymethod]
265 fn __reduce__(zelf: &Self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
266 if zelf.starred {
267 let next_fn = vm.builtins.get_attr("next", vm)?;
269 let non_starred =
270 PyGenericAlias::new(zelf.origin.clone(), zelf.args.clone(), false, vm)?;
271 let iter_obj = PyGenericAliasIterator {
272 obj: crate::common::lock::PyMutex::new(Some(non_starred.into_pyobject(vm))),
273 }
274 .into_pyobject(vm);
275 Ok(PyTuple::new_ref(
276 vec![next_fn, PyTuple::new_ref(vec![iter_obj], &vm.ctx).into()],
277 &vm.ctx,
278 ))
279 } else {
280 Ok(PyTuple::new_ref(
281 vec![
282 vm.ctx.types.generic_alias_type.to_owned().into(),
283 PyTuple::new_ref(vec![zelf.origin.clone(), zelf.args.clone().into()], &vm.ctx)
284 .into(),
285 ],
286 &vm.ctx,
287 ))
288 }
289 }
290
291 #[pymethod]
292 fn __mro_entries__(&self, _object: PyObjectRef, vm: &VirtualMachine) -> PyTupleRef {
293 PyTuple::new_ref(vec![self.origin.clone()], &vm.ctx)
294 }
295
296 #[pymethod]
297 fn __instancecheck__(
298 _zelf: PyRef<PyGenericAlias>,
299 _object: PyObjectRef,
300 vm: &VirtualMachine,
301 ) -> PyResult {
302 Err(vm.new_type_error("isinstance() argument 2 cannot be a parameterized generic"))
303 }
304
305 #[pymethod]
306 fn __subclasscheck__(
307 _zelf: PyRef<PyGenericAlias>,
308 _object: PyObjectRef,
309 vm: &VirtualMachine,
310 ) -> PyResult {
311 Err(vm.new_type_error("issubclass() argument 2 cannot be a parameterized generic"))
312 }
313}
314
315pub(crate) fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
316 make_parameters_from_slice(args.as_slice(), vm)
317}
318
319fn make_parameters_from_slice(args: &[PyObjectRef], vm: &VirtualMachine) -> PyResult<PyTupleRef> {
320 let mut parameters: Vec<PyObjectRef> = Vec::with_capacity(args.len());
321
322 for arg in args {
323 if arg.class().is(vm.ctx.types.type_type) {
325 continue;
326 }
327
328 if arg.get_attr(identifier!(vm, __typing_subst__), vm).is_ok() {
330 if tuple_index(¶meters, arg).is_none() {
331 parameters.push(arg.clone());
332 }
333 } else if let Ok(subparams) = arg.get_attr(identifier!(vm, __parameters__), vm)
334 && let Ok(sub_params) = subparams.try_to_ref::<PyTuple>(vm)
335 {
336 for sub_param in sub_params {
337 if tuple_index(¶meters, sub_param).is_none() {
338 parameters.push(sub_param.clone());
339 }
340 }
341 } else if arg.try_to_ref::<PyTuple>(vm).is_ok() || arg.try_to_ref::<PyList>(vm).is_ok() {
342 let items: Vec<PyObjectRef> = if let Ok(t) = arg.try_to_ref::<PyTuple>(vm) {
344 t.as_slice().to_vec()
345 } else {
346 let list = arg.downcast_ref::<PyList>().unwrap();
347 list.borrow_vec().to_vec()
348 };
349 let sub = vm.with_recursion("while computing __parameters__", || {
350 make_parameters_from_slice(&items, vm)
351 })?;
352 for sub_param in sub.as_slice() {
353 if tuple_index(¶meters, sub_param).is_none() {
354 parameters.push(sub_param.clone());
355 }
356 }
357 }
358 }
359
360 Ok(PyTuple::new_ref(parameters, &vm.ctx))
361}
362
363#[inline]
364fn tuple_index(vec: &[PyObjectRef], item: &PyObject) -> Option<usize> {
365 vec.iter().position(|element| element.is(item))
366}
367
368fn is_unpacked_typevartuple(arg: &PyObject, vm: &VirtualMachine) -> PyResult<bool> {
369 if arg.class().is(vm.ctx.types.type_type) {
370 return Ok(false);
371 }
372
373 if let Ok(attr) = arg.get_attr(identifier!(vm, __typing_is_unpacked_typevartuple__), vm) {
374 attr.try_to_bool(vm)
375 } else {
376 Ok(false)
377 }
378}
379
380fn subs_tvars(
381 obj: PyObjectRef,
382 params: &Py<PyTuple>,
383 arg_items: &[PyObjectRef],
384 vm: &VirtualMachine,
385) -> PyResult {
386 obj.get_attr(identifier!(vm, __parameters__), vm)
387 .ok()
388 .and_then(|sub_params| {
389 PyTupleRef::try_from_object(vm, sub_params)
390 .ok()
391 .filter(|sub_params| !sub_params.as_slice().is_empty())
392 .map(|sub_params| {
393 let mut sub_args = Vec::new();
394
395 for arg in sub_params.as_slice() {
396 if let Some(idx) = tuple_index(params.as_slice(), arg) {
397 let param = ¶ms.as_slice()[idx];
398 let substituted_arg = &arg_items[idx];
399
400 if param.class().slots().iter.load().is_some()
402 && substituted_arg.try_to_ref::<PyTuple>(vm).is_ok()
403 {
404 if let Ok(tuple) = substituted_arg.try_to_ref::<PyTuple>(vm) {
406 for elem in tuple {
407 sub_args.push(elem.clone());
408 }
409 continue;
410 }
411 }
412
413 sub_args.push(substituted_arg.clone());
414 } else {
415 sub_args.push(arg.clone());
416 }
417 }
418
419 let sub_args: PyObjectRef = PyTuple::new_ref(sub_args, &vm.ctx).into();
420 obj.get_item(&*sub_args, vm)
421 })
422 })
423 .unwrap_or(Ok(obj))
424}
425
426fn unpack_args(item: PyObjectRef, vm: &VirtualMachine) -> PyTupleRef {
428 let mut new_args = Vec::new();
429
430 let arg_items = if let Ok(tuple) = item.try_to_ref::<PyTuple>(vm) {
431 tuple.as_slice().to_vec()
432 } else {
433 vec![item]
434 };
435
436 for item in arg_items {
437 if item.class().is(vm.ctx.types.type_type) {
439 new_args.push(item);
440 continue;
441 }
442
443 if let Ok(sub_args) = item.get_attr(identifier!(vm, __typing_unpacked_tuple_args__), vm)
445 && !sub_args.is(&vm.ctx.none)
446 && let Ok(tuple) = sub_args.try_to_ref::<PyTuple>(vm)
447 {
448 let has_ellipsis_at_end = tuple
450 .as_slice()
451 .last()
452 .is_some_and(|item| item.is(&vm.ctx.ellipsis));
453
454 if !has_ellipsis_at_end {
455 for arg in tuple {
457 new_args.push(arg.clone());
458 }
459 continue;
460 }
461 }
462
463 new_args.push(item);
465 }
466
467 PyTuple::new_ref(new_args, &vm.ctx)
468}
469
470pub(crate) fn subs_parameters(
472 alias: &PyObject, args: &Py<PyTuple>,
474 parameters: &Py<PyTuple>,
475 item: PyObjectRef,
476 vm: &VirtualMachine,
477) -> PyResult<PyTupleRef> {
478 let n_params = parameters.as_slice().len();
479 if n_params == 0 {
480 return Err(vm.new_type_error(format!("{} is not a generic class", alias.repr(vm)?)));
481 }
482
483 let mut item: PyObjectRef = unpack_args(item, vm).into();
485
486 for param in parameters {
488 if let Ok(prepare) = param.get_attr(identifier!(vm, __typing_prepare_subst__), vm)
489 && !prepare.is(&vm.ctx.none)
490 {
491 item = if item.try_to_ref::<PyTuple>(vm).is_ok() {
493 prepare.call((alias.to_owned(), item.clone()), vm)?
494 } else {
495 let tuple_args = PyTuple::new_ref(vec![item.clone()], &vm.ctx);
497 prepare.call((alias.to_owned(), tuple_args.to_pyobject(vm)), vm)?
498 };
499 }
500 }
501
502 let arg_items = if let Ok(tuple) = item.try_to_ref::<PyTuple>(vm) {
504 tuple.as_slice().to_vec()
505 } else {
506 vec![item.clone()]
507 };
508 let n_items = arg_items.len();
509
510 if n_items != n_params {
511 return Err(vm.new_type_error(format!(
512 "Too {} arguments for {}; actual {}, expected {}",
513 if n_items > n_params { "many" } else { "few" },
514 alias.repr(vm)?,
515 n_items,
516 n_params
517 )));
518 }
519
520 let mut new_args = Vec::new();
522
523 for arg in args {
524 if arg.class().is(vm.ctx.types.type_type) {
526 new_args.push(arg.clone());
527 continue;
528 }
529
530 let is_list = arg.try_to_ref::<PyList>(vm).is_ok();
532 if arg.try_to_ref::<PyTuple>(vm).is_ok() || is_list {
533 let sub_items: Vec<PyObjectRef> = if let Ok(t) = arg.try_to_ref::<PyTuple>(vm) {
534 t.as_slice().to_vec()
535 } else {
536 arg.downcast_ref::<PyList>().unwrap().borrow_vec().to_vec()
537 };
538 let sub_tuple = PyTuple::new_ref(sub_items, &vm.ctx);
539 let sub_result = subs_parameters(alias, &sub_tuple, parameters, item.clone(), vm)?;
540 let substituted: PyObjectRef = if is_list {
541 PyList::from(sub_result.as_slice().to_vec())
543 .into_ref(&vm.ctx)
544 .into()
545 } else {
546 sub_result.into()
547 };
548 new_args.push(substituted);
549 continue;
550 }
551
552 let unpack = is_unpacked_typevartuple(arg, vm)?;
554
555 let substituted_arg = if let Ok(subst) = arg.get_attr(identifier!(vm, __typing_subst__), vm)
557 {
558 if let Some(iparam) = tuple_index(parameters.as_slice(), arg) {
559 subst.call((arg_items[iparam].clone(),), vm)?
560 } else {
561 subs_tvars(arg.clone(), parameters, &arg_items, vm)?
562 }
563 } else {
564 subs_tvars(arg.clone(), parameters, &arg_items, vm)?
565 };
566
567 if unpack {
568 let tuple = match substituted_arg.try_to_ref::<PyTuple>(vm) {
571 Ok(tuple) => tuple,
572 Err(_) => {
573 let arg_type = arg.class().fully_qualified_name(vm)?;
574 let substituted_type = substituted_arg.class().fully_qualified_name(vm)?;
575 return Err(vm.new_type_error(format!(
576 "expected __typing_subst__ of {arg_type} objects to return a tuple, not {substituted_type}",
577 )));
578 }
579 };
580 for elem in tuple {
581 new_args.push(elem.clone());
582 }
583 } else {
584 new_args.push(substituted_arg);
585 }
586 }
587
588 Ok(PyTuple::new_ref(new_args, &vm.ctx))
589}
590
591impl AsMapping for PyGenericAlias {
592 fn as_mapping() -> &'static PyMappingMethods {
593 static AS_MAPPING: LazyLock<PyMappingMethods> = LazyLock::new(|| PyMappingMethods {
594 subscript: atomic_func!(|mapping, needle, vm| {
595 let zelf = PyGenericAlias::mapping_downcast(mapping);
596 PyGenericAlias::__getitem__(zelf, needle.to_owned(), vm)
597 }),
598 ..PyMappingMethods::NOT_IMPLEMENTED
599 });
600 &AS_MAPPING
601 }
602}
603
604impl AsNumber for PyGenericAlias {
605 fn as_number() -> &'static PyNumberMethods {
606 static AS_NUMBER: PyNumberMethods = PyNumberMethods {
607 or: Some(|a, b, vm| PyGenericAlias::__or__(a.to_owned(), b.to_owned(), vm)),
608 ..PyNumberMethods::NOT_IMPLEMENTED
609 };
610 &AS_NUMBER
611 }
612}
613
614impl Callable for PyGenericAlias {
615 type Args = FuncArgs;
616 fn call(zelf: &Py<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
617 zelf.origin.call(args, vm).map(|obj| {
618 if let Err(exc) = obj.set_attr(identifier!(vm, __orig_class__), zelf.to_owned(), vm)
619 && !exc.fast_isinstance(vm.ctx.exceptions.attribute_error)
620 && !exc.fast_isinstance(vm.ctx.exceptions.type_error)
621 {
622 return Err(exc);
623 }
624 Ok(obj)
625 })?
626 }
627}
628
629impl Comparable for PyGenericAlias {
630 fn cmp(
631 zelf: &Py<Self>,
632 other: &PyObject,
633 op: PyComparisonOp,
634 vm: &VirtualMachine,
635 ) -> PyResult<PyComparisonValue> {
636 op.eq_only(|| {
637 let other = class_or_notimplemented!(Self, other);
638 if zelf.starred != other.starred {
639 return Ok(PyComparisonValue::Implemented(false));
640 }
641 Ok(PyComparisonValue::Implemented(
642 zelf.origin
643 .rich_compare_bool(&other.origin, PyComparisonOp::Eq, vm)?
644 && zelf.args.as_object().rich_compare_bool(
645 other.args.as_object(),
646 PyComparisonOp::Eq,
647 vm,
648 )?,
649 ))
650 })
651 }
652}
653
654impl Hashable for PyGenericAlias {
655 #[inline]
656 fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<hash::PyHash> {
657 Ok(zelf.origin.hash(vm)? ^ zelf.args.as_object().hash(vm)?)
658 }
659}
660
661impl GetAttr for PyGenericAlias {
662 fn getattro(zelf: &Py<Self>, attr: &Py<PyStr>, vm: &VirtualMachine) -> PyResult {
663 let attr_str = attr.as_wtf8();
664 for exc in &ATTR_EXCEPTIONS {
665 if attr_str == *exc {
666 return zelf.as_object().generic_getattr(attr, vm);
667 }
668 }
669 for blocked in &ATTR_BLOCKED {
670 if attr_str == *blocked {
671 return zelf.as_object().generic_getattr(attr, vm);
672 }
673 }
674 zelf.origin.get_attr(attr, vm)
675 }
676}
677
678impl Representable for PyGenericAlias {
679 #[inline]
680 fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
681 zelf.repr(vm)
682 }
683}
684
685impl Iterable for PyGenericAlias {
686 fn iter(zelf: PyRef<Self>, vm: &VirtualMachine) -> PyResult {
687 Ok(PyGenericAliasIterator {
688 obj: crate::common::lock::PyMutex::new(Some(zelf.into())),
689 }
690 .into_pyobject(vm))
691 }
692}
693
694#[pyclass(module = "types", name = "generic_alias_iterator")]
696#[derive(Debug, PyPayload)]
697pub(crate) struct PyGenericAliasIterator {
698 obj: crate::common::lock::PyMutex<Option<PyObjectRef>>,
699}
700
701#[pyclass(with(Representable, Iterable, IterNext))]
702impl Py<PyGenericAliasIterator> {
703 #[pymethod]
704 fn __reduce__(&self, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
705 let iter_fn = vm.builtins.get_attr("iter", vm)?;
706 let guard = self.obj.lock();
707 let arg: PyObjectRef = if let Some(ref obj) = *guard {
708 PyTuple::new_ref(vec![obj.clone()], &vm.ctx).into()
710 } else {
711 let empty = PyTuple::new_ref(vec![], &vm.ctx);
713 PyTuple::new_ref(vec![empty.into()], &vm.ctx).into()
714 };
715 Ok(PyTuple::new_ref(vec![iter_fn, arg], &vm.ctx))
716 }
717}
718
719impl Representable for PyGenericAliasIterator {
720 fn repr_str(_zelf: &Py<Self>, _vm: &VirtualMachine) -> PyResult<String> {
721 Ok("<generic_alias_iterator>".to_owned())
722 }
723}
724
725impl Iterable for PyGenericAliasIterator {
726 fn iter(zelf: PyRef<Self>, _vm: &VirtualMachine) -> PyResult {
727 Ok(zelf.into())
728 }
729}
730
731impl crate::types::IterNext for PyGenericAliasIterator {
732 fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<crate::protocol::PyIterReturn> {
733 use crate::protocol::PyIterReturn;
734 let mut guard = zelf.obj.lock();
735 let obj = match guard.take() {
736 Some(obj) => obj,
737 None => return Ok(PyIterReturn::StopIteration(None)),
738 };
739 let alias = obj
741 .downcast_ref::<PyGenericAlias>()
742 .ok_or_else(|| vm.new_type_error("generic_alias_iterator expected GenericAlias"))?;
743 let starred = PyGenericAlias::new(alias.origin.clone(), alias.args.clone(), true, vm)?;
744 Ok(PyIterReturn::Return(starred.into_pyobject(vm)))
745 }
746}
747
748pub(crate) fn subscript_generic(type_params: PyObjectRef, vm: &VirtualMachine) -> PyResult {
752 let typing_module = vm.import("typing", 0)?;
753 let generic_type = typing_module.get_attr("Generic", vm)?;
754 let generic_alias_class = typing_module.get_attr("_GenericAlias", vm)?;
755
756 let params = if let Ok(tuple) = type_params.try_to_ref::<PyTuple>(vm) {
757 tuple.to_owned()
758 } else {
759 PyTuple::new_ref(vec![type_params], &vm.ctx)
760 };
761
762 let args = crate::stdlib::_typing::unpack_typevartuples(¶ms, vm)?;
763
764 generic_alias_class.call((generic_type, args.to_pyobject(vm)), vm)
765}
766
767pub(crate) fn init(context: &'static Context) {
768 PyGenericAlias::extend_class(context, context.types.generic_alias_type);
769 PyGenericAliasIterator::extend_class(context, context.types.generic_alias_iterator_type);
770}