Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 73 additions & 77 deletions crates/vm/src/builtins/genericalias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ use crate::{
VirtualMachine, atomic_func,
builtins::{PyList, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef},
class::{PyClassDef, PyClassImpl},
common::hash,
common::{hash, wtf8::Wtf8Buf},
convert::ToPyObject,
function::{FuncArgs, PyComparisonValue},
function::{Either, FuncArgs, PyArithmeticValue, PyComparisonValue},
protocol::{PyMappingMethods, PyNumberMethods},
stdlib::_typing::typing_type_repr,
types::{
AsMapping, AsNumber, Callable, Comparable, Constructor, GetAttr, Hashable, IterNext,
Iterable, PyComparisonOp, Representable,
Expand Down Expand Up @@ -133,79 +134,48 @@ impl PyGenericAlias {
cached_parameters(&self.parameters, &self.args, vm)
}

fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
fn repr_item(obj: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
if obj.is(&vm.ctx.ellipsis) {
return Ok("...".to_string());
}

if vm
.get_attribute_opt(obj, identifier!(vm, __origin__))?
.is_some()
&& vm
.get_attribute_opt(obj, identifier!(vm, __args__))?
.is_some()
{
return Ok(obj.repr(vm)?.to_string());
}

match (
vm.get_attribute_opt(obj, identifier!(vm, __qualname__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|n| n.to_string())),
vm.get_attribute_opt(obj, identifier!(vm, __module__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|m| m.to_string())),
) {
(None, _) | (_, None) => Ok(obj.repr(vm)?.to_string()),
(Some(qualname), Some(module)) => Ok(if module == "builtins" {
qualname
} else {
format!("{module}.{qualname}")
}),
}
}

fn repr_arg(obj: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
// ParamSpec args can be lists - format their items with repr_item
fn repr(&self, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
fn repr_arg(repr: &mut Wtf8Buf, obj: &PyObject, vm: &VirtualMachine) -> PyResult<()> {
// ParamSpec args can be lists - format their items as types.
if obj.class().is(vm.ctx.types.list_type) {
let list = obj.downcast_ref::<crate::builtins::PyList>().unwrap();
let len = list.borrow_vec().len();
let mut parts = Vec::with_capacity(len);
repr.push_str("[");
// Use indexed access so list mutation during repr causes IndexError
for i in 0..len {
let item = list
.borrow_vec()
.get(i)
.cloned()
.ok_or_else(|| vm.new_index_error("list index out of range"))?;
parts.push(repr_item(&item, vm)?);
if i > 0 {
repr.push_str(", ");
}
typing_type_repr(repr, &item, vm)?;
}
Ok(format!("[{}]", parts.join(", ")))
repr.push_str("]");
Ok(())
} else {
repr_item(obj, vm)
typing_type_repr(repr, obj, vm)
}
}

let repr_str = format!(
"{}[{}]",
repr_item(&self.origin, vm)?,
if self.args.as_slice().is_empty() {
"()".to_owned()
} else {
self.args
.as_slice()
.iter()
.map(|o| repr_arg(o, vm))
.collect::<PyResult<Vec<_>>>()?
.join(", ")
}
);

// Add * prefix if this is a starred GenericAlias
Ok(if self.starred {
format!("*{repr_str}")
let mut repr = Wtf8Buf::from(if self.starred { "*" } else { "" });
typing_type_repr(&mut repr, &self.origin, vm)?;
repr.push_str("[");
if self.args.as_slice().is_empty() {
repr.push_str("()");
} else {
repr_str
})
for (i, arg) in self.args.as_slice().iter().enumerate() {
if i > 0 {
repr.push_str(", ");
}
repr_arg(&mut repr, arg, vm)?;
}
}
repr.push_str("]");
Ok(repr)
}

fn __getitem__(zelf: &Py<Self>, needle: PyObjectRef, vm: &VirtualMachine) -> PyResult {
Expand Down Expand Up @@ -283,7 +253,7 @@ impl Py<PyGenericAlias> {
} else {
Ok(PyTuple::new_ref(
vec![
vm.ctx.types.generic_alias_type.to_owned().into(),
zelf.class().to_owned().into(),
PyTuple::new_ref(vec![zelf.origin.clone(), zelf.args.clone().into()], &vm.ctx)
.into(),
],
Expand Down Expand Up @@ -662,27 +632,51 @@ impl Callable for PyGenericAlias {
}

impl Comparable for PyGenericAlias {
fn slot_richcompare(
zelf: &PyObject,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<Either<PyObjectRef, PyComparisonValue>> {
if !matches!(op, PyComparisonOp::Eq | PyComparisonOp::Ne) {
return Ok(Either::B(PyComparisonValue::NotImplemented));
}
let (Some(zelf), Some(other)) = (zelf.downcast_ref::<Self>(), other.downcast_ref::<Self>())
else {
return Ok(Either::B(PyComparisonValue::NotImplemented));
};
let equal = if zelf.starred != other.starred
|| !zelf
.origin
.rich_compare_bool(&other.origin, PyComparisonOp::Eq, vm)?
{
vm.ctx.new_bool(false).into()
} else {
// Tuple subclasses can return a non-bool result from equality.
zelf.args
.as_object()
.rich_compare(other.args.as_object(), PyComparisonOp::Eq, vm)?
};
if op == PyComparisonOp::Ne {
// ga_richcompare negates identity with True, without truth conversion.
Ok(Either::B((!equal.is(&vm.ctx.true_value)).into()))
} else {
Ok(Either::A(equal))
}
}

fn cmp(
zelf: &Py<Self>,
other: &PyObject,
op: PyComparisonOp,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
op.eq_only(|| {
let other = class_or_notimplemented!(Self, other);
if zelf.starred != other.starred {
return Ok(PyComparisonValue::Implemented(false));
}
Ok(PyComparisonValue::Implemented(
zelf.origin
.rich_compare_bool(&other.origin, PyComparisonOp::Eq, vm)?
&& zelf.args.as_object().rich_compare_bool(
other.args.as_object(),
PyComparisonOp::Eq,
vm,
)?,
))
})
match Self::slot_richcompare(zelf.as_object(), other, op, vm)? {
Either::A(value) => PyArithmeticValue::from_object(vm, value)
.map(|value| value.try_to_bool(vm))
.transpose(),
Either::B(value) => Ok(value),
}
}
}

Expand Down Expand Up @@ -712,7 +706,7 @@ impl GetAttr for PyGenericAlias {

impl Representable for PyGenericAlias {
#[inline]
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
zelf.repr(vm)
}
}
Expand Down Expand Up @@ -766,11 +760,13 @@ impl Iterable for PyGenericAliasIterator {
impl crate::types::IterNext for PyGenericAliasIterator {
fn next(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<crate::protocol::PyIterReturn> {
use crate::protocol::PyIterReturn;
let mut guard = zelf.obj.lock();
let obj = match guard.take() {
let obj = zelf.obj.lock().take();
let obj = match obj {
Some(obj) => obj,
None => return Ok(PyIterReturn::StopIteration(None)),
};
// The consumed alias may run a subclass finalizer that re-enters us.
// Keep it alive through result construction, without holding the lock.
// Create a starred GenericAlias from the original
let alias = obj
.downcast_ref::<PyGenericAlias>()
Expand Down
53 changes: 13 additions & 40 deletions crates/vm/src/builtins/union.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ use crate::{
atomic_func,
builtins::{PyFrozenSet, PySet, PyStr, PyTuple, PyTupleRef, PyType},
class::PyClassImpl,
common::hash,
common::{hash, wtf8::Wtf8Buf},
convert::ToPyObject,
function::PyComparisonValue,
protocol::{PyMappingMethods, PyNumberMethods},
stdlib::_typing::{TypeAliasType, call_typing_func_object},
stdlib::_typing::{TypeAliasType, call_typing_func_object, typing_type_repr},
types::{AsMapping, AsNumber, Comparable, GetAttr, Hashable, PyComparisonOp, Representable},
};
use alloc::fmt;
Expand Down Expand Up @@ -58,44 +58,15 @@ impl PyUnion {
&self.args
}

fn repr(&self, vm: &VirtualMachine) -> PyResult<String> {
fn repr_item(obj: &PyObject, vm: &VirtualMachine) -> PyResult<String> {
if obj.is(vm.ctx.types.none_type) {
return Ok("None".to_string());
}

if vm
.get_attribute_opt(obj, identifier!(vm, __origin__))?
.is_some()
&& vm
.get_attribute_opt(obj, identifier!(vm, __args__))?
.is_some()
{
return Ok(obj.repr(vm)?.to_string());
}

match (
vm.get_attribute_opt(obj, identifier!(vm, __qualname__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|n| n.to_string())),
vm.get_attribute_opt(obj, identifier!(vm, __module__))?
.and_then(|o| o.downcast_ref::<PyStr>().map(|m| m.to_string())),
) {
(None, _) | (_, None) => Ok(obj.repr(vm)?.to_string()),
(Some(qualname), Some(module)) => Ok(if module == "builtins" {
qualname
} else {
format!("{module}.{qualname}")
}),
fn repr(&self, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
let mut repr = Wtf8Buf::new();
for (i, arg) in self.args.as_slice().iter().enumerate() {
if i > 0 {
repr.push_str(" | ");
}
typing_type_repr(&mut repr, arg, vm)?;
}

Ok(self
.args
.as_slice()
.iter()
.map(|o| repr_item(o, vm))
.collect::<PyResult<Vec<_>>>()?
.join(" | "))
Ok(repr)
}
}

Expand Down Expand Up @@ -172,7 +143,9 @@ impl Py<PyUnion> {
_object: PyObjectRef,
vm: &VirtualMachine,
) -> PyResult {
Err(vm.new_type_error(format!("Cannot subclass {}", zelf.repr(vm)?)))
let mut message = Wtf8Buf::from("Cannot subclass ");
message.push_wtf8(&zelf.repr(vm)?);
Err(vm.new_type_error(message))
}

#[pyclassmethod]
Expand Down Expand Up @@ -529,7 +502,7 @@ impl GetAttr for PyUnion {

impl Representable for PyUnion {
#[inline]
fn repr_str(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<String> {
fn repr_wtf8(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<Wtf8Buf> {
zelf.repr(vm)
}
}
Expand Down
Loading
Loading