__type_params__ in __build_class__ (#5883)

* remove future __classs_getitem__

* __type_params__ in __build_class__
This commit is contained in:
Jeong, YunWon
2025-07-03 14:08:42 +09:00
committed by GitHub
parent c529c247bb
commit 8a2a6af91b
9 changed files with 76 additions and 24 deletions

View File

@@ -656,8 +656,6 @@ class TypeParameterDefaultsTests(BaseTestCase):
P_default = ParamSpec('P_default', default=...)
self.assertIs(P_default.__default__, ...)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_paramspec_none(self):
U = ParamSpec('U')
U_None = ParamSpec('U_None', default=None)
@@ -756,8 +754,6 @@ class TypeParameterDefaultsTests(BaseTestCase):
self.assertEqual(A[float, [range]].__args__, (float, (range,), float))
self.assertEqual(A[float, [range], int].__args__, (float, (range,), int))
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_typevartuple_none(self):
U = TypeVarTuple('U')
U_None = TypeVarTuple('U_None', default=None)
@@ -3893,8 +3889,6 @@ class GenericTests(BaseTestCase):
{'x': list[list[ForwardRef('X')]]}
)
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_pep695_generic_class_with_future_annotations(self):
original_globals = dict(ann_module695.__dict__)
@@ -3913,8 +3907,6 @@ class GenericTests(BaseTestCase):
hints_for_B = get_type_hints(ann_module695.B)
self.assertEqual(hints_for_B, {"x": int, "y": str, "z": bytes})
# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_pep695_generic_class_with_future_annotations_name_clash_with_global_vars(self):
hints_for_C = get_type_hints(ann_module695.C)
self.assertEqual(

View File

@@ -1212,6 +1212,7 @@ impl Compiler<'_> {
/// Store each type parameter so it is accessible to the current scope, and leave a tuple of
/// all the type parameters on the stack.
fn compile_type_params(&mut self, type_params: &TypeParams) -> CompileResult<()> {
// First, compile each type parameter and store it
for type_param in &type_params.type_params {
match type_param {
TypeParam::TypeVar(TypeParamTypeVar { name, bound, .. }) => {
@@ -1664,8 +1665,12 @@ impl Compiler<'_> {
let qualified_name = self.qualified_path.join(".");
// If there are type params, we need to push a special symbol table just for them
if type_params.is_some() {
if let Some(type_params) = type_params {
self.push_symbol_table();
// Compile type parameters and store as .type_params
self.compile_type_params(type_params)?;
let dot_type_params = self.name(".type_params");
emit!(self, Instruction::StoreLocal(dot_type_params));
}
self.push_output(bytecode::CodeFlags::empty(), 0, 0, 0, name.to_owned());
@@ -1688,6 +1693,18 @@ impl Compiler<'_> {
if Self::find_ann(body) {
emit!(self, Instruction::SetupAnnotation);
}
// Set __type_params__ from .type_params if we have type parameters (PEP 695)
if type_params.is_some() {
// Load .type_params from enclosing scope
let dot_type_params = self.name(".type_params");
emit!(self, Instruction::LoadNameAny(dot_type_params));
// Store as __type_params__
let dunder_type_params = self.name("__type_params__");
emit!(self, Instruction::StoreLocal(dunder_type_params));
}
self.compile_statements(body)?;
let classcell_idx = self
@@ -1721,8 +1738,10 @@ impl Compiler<'_> {
let mut func_flags = bytecode::MakeFunctionFlags::empty();
// Prepare generic type parameters:
if let Some(type_params) = type_params {
self.compile_type_params(type_params)?;
if type_params.is_some() {
// Load .type_params from the type params scope
let dot_type_params = self.name(".type_params");
emit!(self, Instruction::LoadNameAny(dot_type_params));
func_flags |= bytecode::MakeFunctionFlags::TYPE_PARAMS;
}

View File

@@ -1267,6 +1267,7 @@ impl SymbolTableBuilder<'_> {
}
fn scan_type_params(&mut self, type_params: &TypeParams) -> SymbolTableResult {
// First register all type parameters
for type_param in &type_params.type_params {
match type_param {
TypeParam::TypeVar(TypeParamTypeVar {

View File

@@ -568,7 +568,8 @@ impl PyByteArray {
self.borrow_buf_mut().reverse();
}
#[pyclassmethod]
// TODO: Uncomment when Python adds __class_getitem__ to bytearray
// #[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}

View File

@@ -483,7 +483,8 @@ impl PyBytes {
PyTuple::new_ref(param, &vm.ctx)
}
#[pyclassmethod]
// TODO: Uncomment when Python adds __class_getitem__ to bytes
// #[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}

View File

@@ -550,7 +550,8 @@ impl Py<PyMemoryView> {
Representable
))]
impl PyMemoryView {
#[pyclassmethod]
// TODO: Uncomment when Python adds __class_getitem__ to memoryview
// #[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}

View File

@@ -184,11 +184,6 @@ 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<PyRef<Self>> {
PyRange {
start: vm.ctx.new_pyref(0),
@@ -328,6 +323,12 @@ impl PyRange {
Ok(range.into())
}
// TODO: Uncomment when Python adds __class_getitem__ to range
// #[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}
}
#[pyclass]

View File

@@ -30,11 +30,6 @@ 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)
@@ -200,6 +195,12 @@ impl PySlice {
(zelf.start.clone(), zelf.stop.clone(), zelf.step.clone()),
))
}
// TODO: Uncomment when Python adds __class_getitem__ to slice
// #[pyclassmethod]
fn __class_getitem__(cls: PyTypeRef, args: PyObjectRef, vm: &VirtualMachine) -> PyGenericAlias {
PyGenericAlias::from_args(cls, args, vm)
}
}
impl Hashable for PySlice {

View File

@@ -932,6 +932,23 @@ mod builtins {
))
})?;
// For PEP 695 classes, set .type_params in namespace before calling the function
if let Ok(type_params) = function
.as_object()
.get_attr(identifier!(vm, __type_params__), vm)
{
if let Some(type_params_tuple) = type_params.downcast_ref::<PyTuple>() {
if !type_params_tuple.is_empty() {
// Set .type_params in namespace so the compiler-generated code can use it
namespace.as_object().set_item(
vm.ctx.intern_str(".type_params"),
type_params,
vm,
)?;
}
}
}
let classcell = function.invoke_with_locals(().into(), Some(namespace.clone()), vm)?;
let classcell = <Option<PyCellRef>>::try_from_object(vm, classcell)?;
@@ -943,9 +960,27 @@ mod builtins {
)?;
}
// Remove .type_params from namespace before creating the class
namespace
.as_object()
.del_item(vm.ctx.intern_str(".type_params"), vm)
.ok();
let args = FuncArgs::new(vec![name_obj.into(), bases, namespace.into()], kwargs);
let class = metaclass.call(args, vm)?;
// For PEP 695 classes, set __type_params__ on the class from the function
if let Ok(type_params) = function
.as_object()
.get_attr(identifier!(vm, __type_params__), vm)
{
if let Some(type_params_tuple) = type_params.downcast_ref::<PyTuple>() {
if !type_params_tuple.is_empty() {
class.set_attr(identifier!(vm, __type_params__), type_params, vm)?;
}
}
}
if let Some(ref classcell) = classcell {
let classcell = classcell.get().ok_or_else(|| {
vm.new_type_error(format!(