Skip to content

Commit c414da1

Browse files
authored
Merge pull request RustPython#3374 from Snowapril/ga_mapping_protocol
Implement `mapping` trait for `GenericAlias`
2 parents c502037 + 795bfeb commit c414da1

File tree

2 files changed

+122
-4
lines changed

2 files changed

+122
-4
lines changed

extra_tests/snippets/test_typing.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from typing import TypeVar
2+
3+
Y = TypeVar('Y')
4+
assert dict[str,Y][int] == dict[str, int]

vm/src/builtins/genericalias.rs

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ use crate::{
22
builtins::{PyList, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType, PyTypeRef},
33
common::hash,
44
function::{FuncArgs, IntoPyObject},
5-
types::{Callable, Comparable, Constructor, GetAttr, Hashable, PyComparisonOp},
6-
IdProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef, PyRef, PyResult,
7-
PyValue, TryFromObject, TypeProtocol, VirtualMachine,
5+
protocol::PyMappingMethods,
6+
types::{AsMapping, Callable, Comparable, Constructor, GetAttr, Hashable, PyComparisonOp},
7+
IdProtocol, ItemProtocol, PyClassImpl, PyComparisonValue, PyContext, PyObject, PyObjectRef,
8+
PyObjectView, PyRef, PyResult, PyValue, TryFromObject, TypeProtocol, VirtualMachine,
89
};
910
use std::fmt;
1011

@@ -53,7 +54,7 @@ impl Constructor for PyGenericAlias {
5354
}
5455

5556
#[pyimpl(
56-
with(Callable, Comparable, Constructor, GetAttr, Hashable),
57+
with(AsMapping, Callable, Comparable, Constructor, GetAttr, Hashable),
5758
flags(BASETYPE)
5859
)]
5960
impl PyGenericAlias {
@@ -192,6 +193,119 @@ fn make_parameters(args: &PyTupleRef, vm: &VirtualMachine) -> PyTupleRef {
192193
PyTuple::new_ref(parameters, &vm.ctx)
193194
}
194195

196+
#[inline]
197+
fn tuple_index(tuple: &PyTupleRef, item: &PyObjectRef) -> Option<usize> {
198+
tuple.as_slice().iter().position(|element| element.is(item))
199+
}
200+
201+
fn subs_tvars(
202+
obj: PyObjectRef,
203+
params: &PyTupleRef,
204+
argitems: &[PyObjectRef],
205+
vm: &VirtualMachine,
206+
) -> PyResult {
207+
obj.clone_class()
208+
.get_attr("__parameters__")
209+
.and_then(|sub_params| {
210+
PyTupleRef::try_from_object(vm, sub_params)
211+
.ok()
212+
.map(|sub_params| {
213+
let sub_args = sub_params
214+
.as_slice()
215+
.iter()
216+
.map(|arg| {
217+
if let Some(idx) = tuple_index(params, arg) {
218+
argitems[idx].clone()
219+
} else {
220+
arg.clone()
221+
}
222+
})
223+
.collect::<Vec<_>>();
224+
let sub_args: PyObjectRef = PyTuple::new_ref(sub_args, &vm.ctx).into();
225+
obj.get_item(sub_args, vm)
226+
})
227+
})
228+
.unwrap_or(Ok(obj))
229+
}
230+
231+
impl AsMapping for PyGenericAlias {
232+
fn as_mapping(_zelf: &PyObjectView<Self>, _vm: &VirtualMachine) -> PyMappingMethods {
233+
PyMappingMethods {
234+
length: None,
235+
subscript: Some(Self::subscript),
236+
ass_subscript: None,
237+
}
238+
}
239+
240+
#[cold]
241+
fn length(zelf: PyObjectRef, _vm: &VirtualMachine) -> PyResult<usize> {
242+
unreachable!("length not implemented for {}", zelf.class())
243+
}
244+
245+
fn subscript(zelf: PyObjectRef, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
246+
Self::downcast(zelf, vm).map(|zelf| {
247+
let num_params = zelf.parameters.len();
248+
if num_params == 0 {
249+
return Err(vm.new_type_error(format!(
250+
"There are no type variables left in {}",
251+
zelf.repr(vm)?
252+
)));
253+
}
254+
255+
let items = PyTupleRef::try_from_object(vm, needle.clone());
256+
let arg_items = match items {
257+
Ok(ref tuple) => tuple.as_slice(),
258+
Err(_) => std::slice::from_ref(&needle),
259+
};
260+
261+
let num_items = arg_items.len();
262+
if num_params != num_items {
263+
let plural = if num_items > num_params {
264+
"many"
265+
} else {
266+
"few"
267+
};
268+
return Err(vm.new_type_error(format!(
269+
"Too {} arguments for {}",
270+
plural,
271+
zelf.repr(vm)?
272+
)));
273+
}
274+
275+
let new_args = zelf
276+
.args
277+
.as_slice()
278+
.iter()
279+
.map(|arg| {
280+
if is_typevar(arg) {
281+
let idx = tuple_index(&zelf.parameters, arg).unwrap();
282+
Ok(arg_items[idx].clone())
283+
} else {
284+
subs_tvars(arg.clone(), &zelf.parameters, arg_items, vm)
285+
}
286+
})
287+
.collect::<PyResult<Vec<_>>>()?;
288+
289+
Ok(PyGenericAlias::new(
290+
zelf.origin.clone(),
291+
PyTuple::new_ref(new_args, &vm.ctx).into(),
292+
vm,
293+
)
294+
.into_object(vm))
295+
})?
296+
}
297+
298+
#[cold]
299+
fn ass_subscript(
300+
zelf: PyObjectRef,
301+
_needle: PyObjectRef,
302+
_value: Option<PyObjectRef>,
303+
_vm: &VirtualMachine,
304+
) -> PyResult<()> {
305+
unreachable!("ass_subscript not implemented for {}", zelf.class())
306+
}
307+
}
308+
195309
impl Callable for PyGenericAlias {
196310
type Args = FuncArgs;
197311
fn call(zelf: &crate::PyObjectView<Self>, args: FuncArgs, vm: &VirtualMachine) -> PyResult {

0 commit comments

Comments
 (0)