Skip to content

Commit 46a1c36

Browse files
committed
Implement mapping protocol for GenericAlias
Signed-off-by: snowapril <sinjihng@gmail.com>
1 parent b433594 commit 46a1c36

File tree

1 file changed

+110
-4
lines changed

1 file changed

+110
-4
lines changed

vm/src/builtins/genericalias.rs

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

0 commit comments

Comments
 (0)