From d61ef66ab6c68afb1d9c46d9ce2bc5bb3d79c67c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Wed, 2 Jul 2025 22:22:33 +0900 Subject: [PATCH] __class_getitem__ --- Lib/test/test_genericalias.py | 4 ---- stdlib/src/array.rs | 13 +++++++++++-- stdlib/src/contextvars.rs | 21 +++++++++++++++------ vm/src/builtins/bytearray.rs | 9 +++++++-- vm/src/builtins/bytes.rs | 8 +++++++- vm/src/builtins/classmethod.rs | 7 ++++++- vm/src/builtins/coroutine.rs | 7 ++++++- vm/src/builtins/generator.rs | 7 ++++++- vm/src/builtins/memory.rs | 9 +++++++-- vm/src/builtins/range.rs | 8 +++++++- vm/src/builtins/slice.rs | 7 ++++++- vm/src/builtins/staticmethod.rs | 7 ++++++- vm/src/builtins/union.rs | 11 ++++++++++- vm/src/exceptions.rs | 17 +++++++++++++++-- vm/src/stdlib/functools.rs | 11 ++++++++++- 15 files changed, 119 insertions(+), 27 deletions(-) diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 560b96f7e3b..0daaff099a8 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -85,8 +85,6 @@ class BaseTest(unittest.TestCase): if ctypes is not None: generic_types.extend((ctypes.Array, ctypes.LibraryLoader)) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_subscriptable(self): for t in self.generic_types: if t is None: @@ -314,8 +312,6 @@ def test_dir(self): for generic_alias_property in ("__origin__", "__args__", "__parameters__"): self.assertIn(generic_alias_property, dir_of_gen_alias) - # TODO: RUSTPYTHON - @unittest.expectedFailure def test_weakref(self): for t in self.generic_types: if t is None: diff --git a/stdlib/src/array.rs b/stdlib/src/array.rs index 0bdbb67af50..b306c52185b 100644 --- a/stdlib/src/array.rs +++ b/stdlib/src/array.rs @@ -44,8 +44,8 @@ mod array { AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func, builtins::{ - PositionIterInternal, PyByteArray, PyBytes, PyBytesRef, PyDictRef, PyFloat, PyInt, - PyList, PyListRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef, + PositionIterInternal, PyByteArray, PyBytes, PyBytesRef, PyDictRef, PyFloat, + PyGenericAlias, PyInt, PyList, PyListRef, PyStr, PyStrRef, PyTupleRef, PyTypeRef, }, class_or_notimplemented, convert::{ToPyObject, ToPyResult, TryFromBorrowedObject, TryFromObject}, @@ -1195,6 +1195,15 @@ mod array { false } + + #[pyclassmethod] + fn __class_getitem__( + cls: PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Comparable for PyArray { diff --git a/stdlib/src/contextvars.rs b/stdlib/src/contextvars.rs index d1b8457e285..b52310e6250 100644 --- a/stdlib/src/contextvars.rs +++ b/stdlib/src/contextvars.rs @@ -24,7 +24,7 @@ thread_local! { mod _contextvars { use crate::vm::{ AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func, - builtins::{PyStrRef, PyTypeRef}, + builtins::{PyGenericAlias, PyStrRef, PyTypeRef}, class::StaticType, common::hash::PyHash, function::{ArgCallable, FuncArgs, OptionalArg}, @@ -478,11 +478,11 @@ mod _contextvars { #[pyclassmethod] fn __class_getitem__( - _cls: PyTypeRef, - _key: PyStrRef, - _vm: &VirtualMachine, - ) -> PyResult<()> { - unimplemented!("ContextVar.__class_getitem__() is currently under construction") + cls: PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) } } @@ -571,6 +571,15 @@ mod _contextvars { None => ContextTokenMissing::static_type().to_owned().into(), } } + + #[pyclassmethod] + fn __class_getitem__( + cls: PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Constructor for ContextToken { diff --git a/vm/src/builtins/bytearray.rs b/vm/src/builtins/bytearray.rs index 9599f66648c..0a13c04dc87 100644 --- a/vm/src/builtins/bytearray.rs +++ b/vm/src/builtins/bytearray.rs @@ -1,7 +1,7 @@ //! Implementation of the python bytearray object. use super::{ - PositionIterInternal, PyBytes, PyBytesRef, PyDictRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, - PyType, PyTypeRef, + PositionIterInternal, PyBytes, PyBytesRef, PyDictRef, PyGenericAlias, PyIntRef, PyStrRef, + PyTuple, PyTupleRef, PyType, PyTypeRef, }; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, @@ -571,6 +571,11 @@ impl PyByteArray { fn reverse(&self) { self.borrow_buf_mut().reverse(); } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } #[pyclass] diff --git a/vm/src/builtins/bytes.rs b/vm/src/builtins/bytes.rs index 82d7c3f26df..8045081dc74 100644 --- a/vm/src/builtins/bytes.rs +++ b/vm/src/builtins/bytes.rs @@ -1,5 +1,6 @@ use super::{ - PositionIterInternal, PyDictRef, PyIntRef, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, + PositionIterInternal, PyDictRef, PyGenericAlias, PyIntRef, PyStrRef, PyTuple, PyTupleRef, + PyType, PyTypeRef, }; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, @@ -481,6 +482,11 @@ impl PyBytes { let param: Vec = self.elements().map(|x| x.to_pyobject(vm)).collect(); PyTuple::new_ref(param, &vm.ctx) } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } #[pyclass] diff --git a/vm/src/builtins/classmethod.rs b/vm/src/builtins/classmethod.rs index 79ac8fc0fe1..47e115a10cf 100644 --- a/vm/src/builtins/classmethod.rs +++ b/vm/src/builtins/classmethod.rs @@ -1,4 +1,4 @@ -use super::{PyBoundMethod, PyStr, PyType, PyTypeRef}; +use super::{PyBoundMethod, PyGenericAlias, PyStr, PyType, PyTypeRef}; use crate::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl, @@ -170,6 +170,11 @@ impl PyClassMethod { .set_attr("__isabstractmethod__", value, vm)?; Ok(()) } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Representable for PyClassMethod { diff --git a/vm/src/builtins/coroutine.rs b/vm/src/builtins/coroutine.rs index 94a1fc41bf3..eea50a567b3 100644 --- a/vm/src/builtins/coroutine.rs +++ b/vm/src/builtins/coroutine.rs @@ -1,4 +1,4 @@ -use super::{PyCode, PyStrRef, PyType}; +use super::{PyCode, PyGenericAlias, PyStrRef, PyType, PyTypeRef}; use crate::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl, @@ -72,6 +72,11 @@ impl PyCoroutine { fn cr_origin(&self, _vm: &VirtualMachine) -> Option<(PyStrRef, usize, PyStrRef)> { None } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } #[pyclass] diff --git a/vm/src/builtins/generator.rs b/vm/src/builtins/generator.rs index c362b3d5275..a7db393eab7 100644 --- a/vm/src/builtins/generator.rs +++ b/vm/src/builtins/generator.rs @@ -2,7 +2,7 @@ * The mythical generator. */ -use super::{PyCode, PyStrRef, PyType}; +use super::{PyCode, PyGenericAlias, PyStrRef, PyType, PyTypeRef}; use crate::{ AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl, @@ -67,6 +67,11 @@ impl PyGenerator { fn gi_yieldfrom(&self, _vm: &VirtualMachine) -> Option { self.inner.frame().yield_from_target() } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } #[pyclass] diff --git a/vm/src/builtins/memory.rs b/vm/src/builtins/memory.rs index 00cbdbb3b56..d2c77d26526 100644 --- a/vm/src/builtins/memory.rs +++ b/vm/src/builtins/memory.rs @@ -1,6 +1,6 @@ use super::{ - PositionIterInternal, PyBytes, PyBytesRef, PyInt, PyListRef, PySlice, PyStr, PyStrRef, PyTuple, - PyTupleRef, PyType, PyTypeRef, + PositionIterInternal, PyBytes, PyBytesRef, PyGenericAlias, PyInt, PyListRef, PySlice, PyStr, + PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef, }; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, @@ -550,6 +550,11 @@ impl Py { Representable ))] impl PyMemoryView { + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } + #[pymethod] pub fn release(&self) { if self.released.compare_exchange(false, true).is_ok() { diff --git a/vm/src/builtins/range.rs b/vm/src/builtins/range.rs index 073f6c76590..3f698de6543 100644 --- a/vm/src/builtins/range.rs +++ b/vm/src/builtins/range.rs @@ -1,5 +1,6 @@ use super::{ - PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef, builtins_iter, tuple::tuple_hash, + PyGenericAlias, PyInt, PyIntRef, PySlice, PyTupleRef, PyType, PyTypeRef, builtins_iter, + tuple::tuple_hash, }; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, @@ -183,6 +184,11 @@ pub fn init(context: &Context) { Representable ))] impl PyRange { + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } + fn new(cls: PyTypeRef, stop: ArgIndex, vm: &VirtualMachine) -> PyResult> { PyRange { start: vm.ctx.new_pyref(0), diff --git a/vm/src/builtins/slice.rs b/vm/src/builtins/slice.rs index f595c53754c..02e066bbac9 100644 --- a/vm/src/builtins/slice.rs +++ b/vm/src/builtins/slice.rs @@ -1,6 +1,6 @@ // sliceobject.{h,c} in CPython // spell-checker:ignore sliceobject -use super::{PyStrRef, PyTupleRef, PyType, PyTypeRef}; +use super::{PyGenericAlias, PyStrRef, PyTupleRef, PyType, PyTypeRef}; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl, @@ -30,6 +30,11 @@ impl PyPayload for PySlice { #[pyclass(with(Comparable, Representable, Hashable))] impl PySlice { + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } + #[pygetset] fn start(&self, vm: &VirtualMachine) -> PyObjectRef { self.start.clone().to_pyobject(vm) diff --git a/vm/src/builtins/staticmethod.rs b/vm/src/builtins/staticmethod.rs index 53a1681e908..6024835487e 100644 --- a/vm/src/builtins/staticmethod.rs +++ b/vm/src/builtins/staticmethod.rs @@ -1,4 +1,4 @@ -use super::{PyStr, PyType, PyTypeRef}; +use super::{PyGenericAlias, PyStr, PyType, PyTypeRef}; use crate::{ Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, class::PyClassImpl, @@ -131,6 +131,11 @@ impl PyStaticMethod { .set_attr("__isabstractmethod__", value, vm)?; Ok(()) } + + #[pyclassmethod] + fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Callable for PyStaticMethod { diff --git a/vm/src/builtins/union.rs b/vm/src/builtins/union.rs index f1662901a5b..b5a604ec2a8 100644 --- a/vm/src/builtins/union.rs +++ b/vm/src/builtins/union.rs @@ -2,7 +2,7 @@ use super::{genericalias, type_}; use crate::{ AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, atomic_func, - builtins::{PyFrozenSet, PyStr, PyTuple, PyTupleRef, PyType}, + builtins::{PyFrozenSet, PyGenericAlias, PyStr, PyTuple, PyTupleRef, PyType}, class::PyClassImpl, common::hash, convert::{ToPyObject, ToPyResult}, @@ -140,6 +140,15 @@ impl PyUnion { fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef { type_::or_(zelf, other, vm) } + + #[pyclassmethod] + fn __class_getitem__( + cls: crate::builtins::PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } pub fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool { diff --git a/vm/src/exceptions.rs b/vm/src/exceptions.rs index 520a09ba3f1..7544bcc99f3 100644 --- a/vm/src/exceptions.rs +++ b/vm/src/exceptions.rs @@ -1203,7 +1203,8 @@ pub(super) mod types { use crate::{ AsObject, PyObjectRef, PyRef, PyResult, VirtualMachine, builtins::{ - PyInt, PyStrRef, PyTupleRef, PyTypeRef, traceback::PyTracebackRef, tuple::IntoPyTuple, + PyGenericAlias, PyInt, PyStrRef, PyTupleRef, PyTypeRef, traceback::PyTracebackRef, + tuple::IntoPyTuple, }, convert::ToPyResult, function::{ArgBytesLike, FuncArgs}, @@ -1234,10 +1235,22 @@ pub(super) mod types { #[derive(Debug)] pub struct PySystemExit {} - #[pyexception(name, base = "PyBaseException", ctx = "base_exception_group", impl)] + #[pyexception(name, base = "PyBaseException", ctx = "base_exception_group")] #[derive(Debug)] pub struct PyBaseExceptionGroup {} + #[pyexception] + impl PyBaseExceptionGroup { + #[pyclassmethod] + fn __class_getitem__( + cls: PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } + } + #[pyexception(name, base = "PyBaseExceptionGroup", ctx = "exception_group", impl)] #[derive(Debug)] pub struct PyExceptionGroup {} diff --git a/vm/src/stdlib/functools.rs b/vm/src/stdlib/functools.rs index 2cca5a807c6..5449db3c390 100644 --- a/vm/src/stdlib/functools.rs +++ b/vm/src/stdlib/functools.rs @@ -4,7 +4,7 @@ pub(crate) use _functools::make_module; mod _functools { use crate::{ Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine, - builtins::{PyDict, PyTuple, PyTypeRef}, + builtins::{PyDict, PyGenericAlias, PyTuple, PyTypeRef}, common::lock::PyRwLock, function::{FuncArgs, KwArgs, OptionalArg}, object::AsObject, @@ -190,6 +190,15 @@ mod _functools { Ok(()) } + + #[pyclassmethod] + fn __class_getitem__( + cls: PyTypeRef, + args: PyObjectRef, + vm: &VirtualMachine, + ) -> PyGenericAlias { + PyGenericAlias::from_args(cls, args, vm) + } } impl Constructor for PyPartial {