mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
_typing.Union
This commit is contained in:
committed by
Jeong, YunWon
parent
2bbb3d07c6
commit
bb72418d9e
4
Lib/copyreg.py
vendored
4
Lib/copyreg.py
vendored
@@ -31,8 +31,8 @@ def pickle_complex(c):
|
||||
pickle(complex, pickle_complex, complex)
|
||||
|
||||
def pickle_union(obj):
|
||||
import functools, operator
|
||||
return functools.reduce, (operator.or_, obj.__args__)
|
||||
import typing, operator
|
||||
return operator.getitem, (typing.Union, obj.__args__)
|
||||
|
||||
pickle(type(int | str), pickle_union)
|
||||
|
||||
|
||||
@@ -235,11 +235,11 @@ impl PyGenericAlias {
|
||||
Err(vm.new_type_error("issubclass() argument 2 cannot be a parameterized generic"))
|
||||
}
|
||||
|
||||
fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
type_::or_(other, zelf, vm)
|
||||
}
|
||||
|
||||
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
type_::or_(zelf, other, vm)
|
||||
}
|
||||
}
|
||||
@@ -509,7 +509,7 @@ impl AsMapping for PyGenericAlias {
|
||||
impl AsNumber for PyGenericAlias {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| Ok(PyGenericAlias::__or__(a.to_owned(), b.to_owned(), vm))),
|
||||
or: Some(|a, b, vm| PyGenericAlias::__or__(a.to_owned(), b.to_owned(), vm)),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::{
|
||||
borrow::BorrowedValue,
|
||||
lock::{PyRwLock, PyRwLockReadGuard},
|
||||
},
|
||||
convert::ToPyResult,
|
||||
function::{FuncArgs, KwArgs, OptionalArg, PyMethodDef, PySetterValue},
|
||||
object::{Traverse, TraverseFn},
|
||||
protocol::{PyIterReturn, PyNumberMethods},
|
||||
@@ -1038,11 +1037,11 @@ impl PyType {
|
||||
)
|
||||
}
|
||||
|
||||
pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
pub fn __ror__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
or_(other, zelf, vm)
|
||||
}
|
||||
|
||||
pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
pub fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
or_(zelf, other, vm)
|
||||
}
|
||||
|
||||
@@ -1850,7 +1849,7 @@ impl Callable for PyType {
|
||||
impl AsNumber for PyType {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| or_(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
|
||||
or: Some(|a, b, vm| or_(a.to_owned(), b.to_owned(), vm)),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
@@ -2013,9 +2012,9 @@ pub(crate) fn call_slot_new(
|
||||
slot_new(subtype, args, vm)
|
||||
}
|
||||
|
||||
pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
pub(crate) fn or_(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
if !union_::is_unionable(zelf.clone(), vm) || !union_::is_unionable(other.clone(), vm) {
|
||||
return vm.ctx.not_implemented();
|
||||
return Ok(vm.ctx.not_implemented());
|
||||
}
|
||||
|
||||
let tuple = PyTuple::new_ref(vec![zelf, other], &vm.ctx);
|
||||
|
||||
@@ -2,10 +2,10 @@ use super::{genericalias, type_};
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
atomic_func,
|
||||
builtins::{PyFrozenSet, PyGenericAlias, PyStr, PyTuple, PyTupleRef, PyType},
|
||||
builtins::{PyFrozenSet, PySet, PyStr, PyTuple, PyTupleRef, PyType},
|
||||
class::PyClassImpl,
|
||||
common::hash,
|
||||
convert::{ToPyObject, ToPyResult},
|
||||
convert::ToPyObject,
|
||||
function::PyComparisonValue,
|
||||
protocol::{PyMappingMethods, PyNumberMethods},
|
||||
stdlib::typing::TypeAliasType,
|
||||
@@ -16,9 +16,13 @@ use std::sync::LazyLock;
|
||||
|
||||
const CLS_ATTRS: &[&str] = &["__module__"];
|
||||
|
||||
#[pyclass(module = "types", name = "UnionType", traverse)]
|
||||
#[pyclass(module = "typing", name = "Union", traverse)]
|
||||
pub struct PyUnion {
|
||||
args: PyTupleRef,
|
||||
/// Frozenset of hashable args, or None if all args were hashable
|
||||
hashable_args: Option<PyRef<PyFrozenSet>>,
|
||||
/// Tuple of initially unhashable args, or None if all args were hashable
|
||||
unhashable_args: Option<PyTupleRef>,
|
||||
parameters: PyTupleRef,
|
||||
}
|
||||
|
||||
@@ -36,9 +40,15 @@ impl PyPayload for PyUnion {
|
||||
}
|
||||
|
||||
impl PyUnion {
|
||||
pub fn new(args: PyTupleRef, vm: &VirtualMachine) -> Self {
|
||||
let parameters = make_parameters(&args, vm);
|
||||
Self { args, parameters }
|
||||
/// Create a new union from dedup result (internal use)
|
||||
fn from_components(result: UnionComponents, vm: &VirtualMachine) -> PyResult<Self> {
|
||||
let parameters = make_parameters(&result.args, vm)?;
|
||||
Ok(Self {
|
||||
args: result.args,
|
||||
hashable_args: result.hashable_args,
|
||||
unhashable_args: result.unhashable_args,
|
||||
parameters,
|
||||
})
|
||||
}
|
||||
|
||||
/// Direct access to args field, matching CPython's _Py_union_args
|
||||
@@ -88,10 +98,25 @@ impl PyUnion {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
flags(BASETYPE),
|
||||
flags(DISALLOW_INSTANTIATION),
|
||||
with(Hashable, Comparable, AsMapping, AsNumber, Representable)
|
||||
)]
|
||||
impl PyUnion {
|
||||
#[pygetset]
|
||||
fn __name__(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_str("Union").into()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __qualname__(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.new_str("Union").into()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __origin__(&self, vm: &VirtualMachine) -> PyObjectRef {
|
||||
vm.ctx.types.union_type.to_owned().into()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
fn __parameters__(&self) -> PyObjectRef {
|
||||
self.parameters.clone().into()
|
||||
@@ -136,17 +161,35 @@ impl PyUnion {
|
||||
}
|
||||
}
|
||||
|
||||
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyObjectRef {
|
||||
fn __or__(zelf: PyObjectRef, other: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
type_::or_(zelf, other, vm)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn __mro_entries__(zelf: PyRef<Self>, _args: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
Err(vm.new_type_error(format!("Cannot subclass {}", zelf.repr(vm)?)))
|
||||
}
|
||||
|
||||
#[pyclassmethod]
|
||||
fn __class_getitem__(
|
||||
cls: crate::builtins::PyTypeRef,
|
||||
_cls: crate::builtins::PyTypeRef,
|
||||
args: PyObjectRef,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyGenericAlias {
|
||||
PyGenericAlias::from_args(cls, args, vm)
|
||||
) -> PyResult {
|
||||
// Convert args to tuple if not already
|
||||
let args_tuple = if let Some(tuple) = args.downcast_ref::<PyTuple>() {
|
||||
tuple.to_owned()
|
||||
} else {
|
||||
PyTuple::new_ref(vec![args], &vm.ctx)
|
||||
};
|
||||
|
||||
// Check for empty union
|
||||
if args_tuple.is_empty() {
|
||||
return Err(vm.new_type_error("Cannot create empty Union"));
|
||||
}
|
||||
|
||||
// Create union using make_union to properly handle None -> NoneType conversion
|
||||
make_union(&args_tuple, vm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,9 +202,10 @@ pub fn is_unionable(obj: PyObjectRef, vm: &VirtualMachine) -> bool {
|
||||
|| obj.downcast_ref::<TypeAliasType>().is_some()
|
||||
}
|
||||
|
||||
fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
|
||||
fn make_parameters(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult<PyTupleRef> {
|
||||
let parameters = genericalias::make_parameters(args, vm);
|
||||
dedup_and_flatten_args(¶meters, vm)
|
||||
let result = dedup_and_flatten_args(¶meters, vm)?;
|
||||
Ok(result.args)
|
||||
}
|
||||
|
||||
fn flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
|
||||
@@ -180,6 +224,12 @@ fn flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
|
||||
flattened_args.extend(pyref.args.iter().cloned());
|
||||
} else if vm.is_none(arg) {
|
||||
flattened_args.push(vm.ctx.types.none_type.to_owned().into());
|
||||
} else if arg.downcast_ref::<PyStr>().is_some() {
|
||||
// Convert string to ForwardRef
|
||||
match string_to_forwardref(arg.clone(), vm) {
|
||||
Ok(fr) => flattened_args.push(fr),
|
||||
Err(_) => flattened_args.push(arg.clone()),
|
||||
}
|
||||
} else {
|
||||
flattened_args.push(arg.clone());
|
||||
};
|
||||
@@ -188,31 +238,105 @@ fn flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
|
||||
PyTuple::new_ref(flattened_args, &vm.ctx)
|
||||
}
|
||||
|
||||
fn dedup_and_flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyTupleRef {
|
||||
fn string_to_forwardref(arg: PyObjectRef, vm: &VirtualMachine) -> PyResult {
|
||||
// Import annotationlib.ForwardRef and create a ForwardRef
|
||||
let annotationlib = vm.import("annotationlib", 0)?;
|
||||
let forwardref_cls = annotationlib.get_attr("ForwardRef", vm)?;
|
||||
forwardref_cls.call((arg,), vm)
|
||||
}
|
||||
|
||||
/// Components for creating a PyUnion after deduplication
|
||||
struct UnionComponents {
|
||||
/// All unique args in order
|
||||
args: PyTupleRef,
|
||||
/// Frozenset of hashable args (for fast equality comparison)
|
||||
hashable_args: Option<PyRef<PyFrozenSet>>,
|
||||
/// Tuple of unhashable args at creation time (for hash error message)
|
||||
unhashable_args: Option<PyTupleRef>,
|
||||
}
|
||||
|
||||
fn dedup_and_flatten_args(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult<UnionComponents> {
|
||||
let args = flatten_args(args, vm);
|
||||
|
||||
// Use set-based deduplication like CPython:
|
||||
// - For hashable elements: use Python's set semantics (hash + equality)
|
||||
// - For unhashable elements: use equality comparison
|
||||
//
|
||||
// This avoids calling __eq__ when hashes differ, matching CPython behavior
|
||||
// where `int | BadType` doesn't raise even if BadType.__eq__ raises.
|
||||
|
||||
let mut new_args: Vec<PyObjectRef> = Vec::with_capacity(args.len());
|
||||
|
||||
// Track hashable elements using a Python set (uses hash + equality)
|
||||
let hashable_set = PySet::default().into_ref(&vm.ctx);
|
||||
let mut hashable_list: Vec<PyObjectRef> = Vec::new();
|
||||
let mut unhashable_list: Vec<PyObjectRef> = Vec::new();
|
||||
|
||||
for arg in &*args {
|
||||
if !new_args.iter().any(|param| {
|
||||
param
|
||||
.rich_compare_bool(arg, PyComparisonOp::Eq, vm)
|
||||
.unwrap_or_default()
|
||||
}) {
|
||||
new_args.push(arg.clone());
|
||||
// Try to hash the element first
|
||||
match arg.hash(vm) {
|
||||
Ok(_) => {
|
||||
// Element is hashable - use set for deduplication
|
||||
// Set membership uses hash first, then equality only if hashes match
|
||||
let contains = vm
|
||||
.call_method(hashable_set.as_ref(), "__contains__", (arg.clone(),))
|
||||
.and_then(|r| r.try_to_bool(vm))?;
|
||||
if !contains {
|
||||
hashable_set.add(arg.clone(), vm)?;
|
||||
hashable_list.push(arg.clone());
|
||||
new_args.push(arg.clone());
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// Element is unhashable - use equality comparison
|
||||
let mut is_duplicate = false;
|
||||
for existing in &unhashable_list {
|
||||
match existing.rich_compare_bool(arg, PyComparisonOp::Eq, vm) {
|
||||
Ok(true) => {
|
||||
is_duplicate = true;
|
||||
break;
|
||||
}
|
||||
Ok(false) => continue,
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
if !is_duplicate {
|
||||
unhashable_list.push(arg.clone());
|
||||
new_args.push(arg.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_args.shrink_to_fit();
|
||||
|
||||
PyTuple::new_ref(new_args, &vm.ctx)
|
||||
// Create hashable_args frozenset if there are hashable elements
|
||||
let hashable_args = if !hashable_list.is_empty() {
|
||||
Some(PyFrozenSet::from_iter(vm, hashable_list.into_iter())?.into_ref(&vm.ctx))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Create unhashable_args tuple if there are unhashable elements
|
||||
let unhashable_args = if !unhashable_list.is_empty() {
|
||||
Some(PyTuple::new_ref(unhashable_list, &vm.ctx))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(UnionComponents {
|
||||
args: PyTuple::new_ref(new_args, &vm.ctx),
|
||||
hashable_args,
|
||||
unhashable_args,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn make_union(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyObjectRef {
|
||||
let args = dedup_and_flatten_args(args, vm);
|
||||
match args.len() {
|
||||
1 => args[0].to_owned(),
|
||||
_ => PyUnion::new(args, vm).to_pyobject(vm),
|
||||
}
|
||||
pub fn make_union(args: &Py<PyTuple>, vm: &VirtualMachine) -> PyResult {
|
||||
let result = dedup_and_flatten_args(args, vm)?;
|
||||
Ok(match result.args.len() {
|
||||
1 => result.args[0].to_owned(),
|
||||
_ => PyUnion::from_components(result, vm)?.to_pyobject(vm),
|
||||
})
|
||||
}
|
||||
|
||||
impl PyUnion {
|
||||
@@ -224,14 +348,15 @@ impl PyUnion {
|
||||
needle,
|
||||
vm,
|
||||
)?;
|
||||
let mut res;
|
||||
let res;
|
||||
if new_args.is_empty() {
|
||||
res = make_union(&new_args, vm);
|
||||
res = make_union(&new_args, vm)?;
|
||||
} else {
|
||||
res = new_args[0].to_owned();
|
||||
let mut tmp = new_args[0].to_owned();
|
||||
for arg in new_args.iter().skip(1) {
|
||||
res = vm._or(&res, arg)?;
|
||||
tmp = vm._or(&tmp, arg)?;
|
||||
}
|
||||
res = tmp;
|
||||
}
|
||||
|
||||
Ok(res)
|
||||
@@ -254,7 +379,7 @@ impl AsMapping for PyUnion {
|
||||
impl AsNumber for PyUnion {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| PyUnion::__or__(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
|
||||
or: Some(|a, b, vm| PyUnion::__or__(a.to_owned(), b.to_owned(), vm)),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
@@ -270,15 +395,62 @@ impl Comparable for PyUnion {
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
op.eq_only(|| {
|
||||
let other = class_or_notimplemented!(Self, other);
|
||||
let a = PyFrozenSet::from_iter(vm, zelf.args.into_iter().cloned())?;
|
||||
let b = PyFrozenSet::from_iter(vm, other.args.into_iter().cloned())?;
|
||||
Ok(PyComparisonValue::Implemented(
|
||||
a.into_pyobject(vm).as_object().rich_compare_bool(
|
||||
b.into_pyobject(vm).as_object(),
|
||||
PyComparisonOp::Eq,
|
||||
vm,
|
||||
)?,
|
||||
))
|
||||
|
||||
// Check if lengths are equal
|
||||
if zelf.args.len() != other.args.len() {
|
||||
return Ok(PyComparisonValue::Implemented(false));
|
||||
}
|
||||
|
||||
// Fast path: if both unions have all hashable args, compare frozensets directly
|
||||
// Always use Eq here since eq_only handles Ne by negating the result
|
||||
if zelf.unhashable_args.is_none()
|
||||
&& other.unhashable_args.is_none()
|
||||
&& let (Some(a), Some(b)) = (&zelf.hashable_args, &other.hashable_args)
|
||||
{
|
||||
let eq = a
|
||||
.as_object()
|
||||
.rich_compare_bool(b.as_object(), PyComparisonOp::Eq, vm)?;
|
||||
return Ok(PyComparisonValue::Implemented(eq));
|
||||
}
|
||||
|
||||
// Slow path: O(n^2) nested loop comparison for unhashable elements
|
||||
// Check if all elements in zelf.args are in other.args
|
||||
for arg_a in &*zelf.args {
|
||||
let mut found = false;
|
||||
for arg_b in &*other.args {
|
||||
match arg_a.rich_compare_bool(arg_b, PyComparisonOp::Eq, vm) {
|
||||
Ok(true) => {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
Ok(false) => continue,
|
||||
Err(e) => return Err(e), // Propagate comparison errors
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return Ok(PyComparisonValue::Implemented(false));
|
||||
}
|
||||
}
|
||||
|
||||
// Check if all elements in other.args are in zelf.args (for symmetry)
|
||||
for arg_b in &*other.args {
|
||||
let mut found = false;
|
||||
for arg_a in &*zelf.args {
|
||||
match arg_b.rich_compare_bool(arg_a, PyComparisonOp::Eq, vm) {
|
||||
Ok(true) => {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
Ok(false) => continue,
|
||||
Err(e) => return Err(e), // Propagate comparison errors
|
||||
}
|
||||
}
|
||||
if !found {
|
||||
return Ok(PyComparisonValue::Implemented(false));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PyComparisonValue::Implemented(true))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -286,7 +458,36 @@ impl Comparable for PyUnion {
|
||||
impl Hashable for PyUnion {
|
||||
#[inline]
|
||||
fn hash(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<hash::PyHash> {
|
||||
let set = PyFrozenSet::from_iter(vm, zelf.args.into_iter().cloned())?;
|
||||
// If there are any unhashable args from creation time, the union is unhashable
|
||||
if let Some(ref unhashable_args) = zelf.unhashable_args {
|
||||
let n = unhashable_args.len();
|
||||
// Try to hash each previously unhashable arg to get an error
|
||||
for arg in unhashable_args.iter() {
|
||||
arg.hash(vm)?;
|
||||
}
|
||||
// All previously unhashable args somehow became hashable
|
||||
// But still raise an error to maintain consistent hashing
|
||||
return Err(vm.new_type_error(format!(
|
||||
"union contains {} unhashable element{}",
|
||||
n,
|
||||
if n > 1 { "s" } else { "" }
|
||||
)));
|
||||
}
|
||||
|
||||
// If we have a stored frozenset of hashable args, use that
|
||||
if let Some(ref hashable_args) = zelf.hashable_args {
|
||||
return PyFrozenSet::hash(hashable_args, vm);
|
||||
}
|
||||
|
||||
// Fallback: compute hash from args
|
||||
let mut args_to_hash = Vec::new();
|
||||
for arg in &*zelf.args {
|
||||
match arg.hash(vm) {
|
||||
Ok(_) => args_to_hash.push(arg.clone()),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
let set = PyFrozenSet::from_iter(vm, args_to_hash.into_iter())?;
|
||||
PyFrozenSet::hash(&set.into_ref(&vm.ctx), vm)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,29 +149,24 @@ mod _abc {
|
||||
let items = vm.call_method(&ns, "items", ())?;
|
||||
let iter = items.get_iter(vm)?;
|
||||
|
||||
loop {
|
||||
match iter.next(vm)? {
|
||||
PyIterReturn::Return(item) => {
|
||||
let tuple: PyTupleRef = item
|
||||
.downcast()
|
||||
.map_err(|_| vm.new_type_error("items() returned non-tuple".to_owned()))?;
|
||||
let elements = tuple.as_slice();
|
||||
if elements.len() != 2 {
|
||||
return Err(vm.new_type_error(
|
||||
"items() returned item which size is not 2".to_owned(),
|
||||
));
|
||||
}
|
||||
let key = &elements[0];
|
||||
let value = &elements[1];
|
||||
while let PyIterReturn::Return(item) = iter.next(vm)? {
|
||||
let tuple: PyTupleRef = item
|
||||
.downcast()
|
||||
.map_err(|_| vm.new_type_error("items() returned non-tuple".to_owned()))?;
|
||||
let elements = tuple.as_slice();
|
||||
if elements.len() != 2 {
|
||||
return Err(
|
||||
vm.new_type_error("items() returned item which size is not 2".to_owned())
|
||||
);
|
||||
}
|
||||
let key = &elements[0];
|
||||
let value = &elements[1];
|
||||
|
||||
// Check if value has __isabstractmethod__ = True
|
||||
if let Ok(is_abstract) = value.get_attr("__isabstractmethod__", vm) {
|
||||
if is_abstract.try_to_bool(vm)? {
|
||||
abstracts.push(key.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
PyIterReturn::StopIteration(_) => break,
|
||||
// Check if value has __isabstractmethod__ = True
|
||||
if let Ok(is_abstract) = value.get_attr("__isabstractmethod__", vm)
|
||||
&& is_abstract.try_to_bool(vm)?
|
||||
{
|
||||
abstracts.push(key.clone());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -184,25 +179,14 @@ mod _abc {
|
||||
for base in bases.iter() {
|
||||
if let Ok(base_abstracts) = base.get_attr("__abstractmethods__", vm) {
|
||||
let iter = base_abstracts.get_iter(vm)?;
|
||||
loop {
|
||||
match iter.next(vm)? {
|
||||
PyIterReturn::Return(key) => {
|
||||
// Try to get the attribute from cls - key should be a string
|
||||
if let Some(key_str) = key.downcast_ref::<PyStr>() {
|
||||
if let Some(value) =
|
||||
vm.get_attribute_opt(cls.to_owned(), key_str)?
|
||||
{
|
||||
if let Ok(is_abstract) =
|
||||
value.get_attr("__isabstractmethod__", vm)
|
||||
{
|
||||
if is_abstract.try_to_bool(vm)? {
|
||||
abstracts.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
PyIterReturn::StopIteration(_) => break,
|
||||
while let PyIterReturn::Return(key) = iter.next(vm)? {
|
||||
// Try to get the attribute from cls - key should be a string
|
||||
if let Some(key_str) = key.downcast_ref::<PyStr>()
|
||||
&& let Some(value) = vm.get_attribute_opt(cls.to_owned(), key_str)?
|
||||
&& let Ok(is_abstract) = value.get_attr("__isabstractmethod__", vm)
|
||||
&& is_abstract.try_to_bool(vm)?
|
||||
{
|
||||
abstracts.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -279,10 +263,10 @@ mod _abc {
|
||||
let subtype: PyObjectRef = instance.class().to_owned().into();
|
||||
if subtype.is(&subclass) {
|
||||
let invalidation_counter = get_invalidation_counter();
|
||||
if impl_data.get_cache_version() == invalidation_counter {
|
||||
if in_weak_set(&impl_data.negative_cache, &subclass, vm)? {
|
||||
return Ok(vm.ctx.false_value.clone().into());
|
||||
}
|
||||
if impl_data.get_cache_version() == invalidation_counter
|
||||
&& in_weak_set(&impl_data.negative_cache, &subclass, vm)?
|
||||
{
|
||||
return Ok(vm.ctx.false_value.clone().into());
|
||||
}
|
||||
// Fall back to __subclasscheck__
|
||||
return vm.call_method(&cls, "__subclasscheck__", (subclass,));
|
||||
@@ -323,13 +307,12 @@ mod _abc {
|
||||
let registry_copy = PyFrozenSet::from_iter(vm, registry.elements().into_iter())?;
|
||||
|
||||
for weak_ref_obj in registry_copy.elements() {
|
||||
if let Ok(weak_ref) = weak_ref_obj.downcast::<PyWeak>() {
|
||||
if let Some(rkey) = weak_ref.upgrade() {
|
||||
if subclass.to_owned().is_subclass(&rkey, vm)? {
|
||||
add_to_weak_set(&impl_data.cache, subclass, vm)?;
|
||||
return Ok(Some(true));
|
||||
}
|
||||
}
|
||||
if let Ok(weak_ref) = weak_ref_obj.downcast::<PyWeak>()
|
||||
&& let Some(rkey) = weak_ref.upgrade()
|
||||
&& subclass.to_owned().is_subclass(&rkey, vm)?
|
||||
{
|
||||
add_to_weak_set(&impl_data.cache, subclass, vm)?;
|
||||
return Ok(Some(true));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,7 +251,7 @@ impl AsNumber for TypeVar {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| {
|
||||
let args = PyTuple::new_ref(vec![a.to_owned(), b.to_owned()], &vm.ctx);
|
||||
Ok(make_union(&args, vm))
|
||||
make_union(&args, vm)
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
@@ -527,7 +527,7 @@ impl AsNumber for ParamSpec {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| {
|
||||
let args = PyTuple::new_ref(vec![a.to_owned(), b.to_owned()], &vm.ctx);
|
||||
Ok(make_union(&args, vm))
|
||||
make_union(&args, vm)
|
||||
}),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@ pub(crate) fn make_module(vm: &VirtualMachine) -> PyRef<PyModule> {
|
||||
"ParamSpecArgs" => ParamSpecArgs::class(&vm.ctx).to_owned(),
|
||||
"ParamSpecKwargs" => ParamSpecKwargs::class(&vm.ctx).to_owned(),
|
||||
"Generic" => Generic::class(&vm.ctx).to_owned(),
|
||||
"Union" => vm.ctx.types.union_type.to_owned(),
|
||||
});
|
||||
module
|
||||
}
|
||||
@@ -37,7 +38,6 @@ pub(crate) mod decl {
|
||||
use crate::{
|
||||
Py, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{PyStrRef, PyTupleRef, PyType, PyTypeRef, pystr::AsPyStr, type_},
|
||||
convert::ToPyResult,
|
||||
function::{FuncArgs, IntoFuncArgs},
|
||||
protocol::PyNumberMethods,
|
||||
types::{AsNumber, Constructor, Representable},
|
||||
@@ -188,7 +188,7 @@ pub(crate) mod decl {
|
||||
impl AsNumber for TypeAliasType {
|
||||
fn as_number() -> &'static PyNumberMethods {
|
||||
static AS_NUMBER: PyNumberMethods = PyNumberMethods {
|
||||
or: Some(|a, b, vm| type_::or_(a.to_owned(), b.to_owned(), vm).to_pyresult(vm)),
|
||||
or: Some(|a, b, vm| type_::or_(a.to_owned(), b.to_owned(), vm)),
|
||||
..PyNumberMethods::NOT_IMPLEMENTED
|
||||
};
|
||||
&AS_NUMBER
|
||||
|
||||
@@ -483,7 +483,7 @@ fn hash_wrapper(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
|
||||
|
||||
/// Marks a type as unhashable. Similar to PyObject_HashNotImplemented in CPython
|
||||
pub fn hash_not_implemented(zelf: &PyObject, vm: &VirtualMachine) -> PyResult<PyHash> {
|
||||
Err(vm.new_type_error(format!("unhashable type: {}", zelf.class().name())))
|
||||
Err(vm.new_type_error(format!("unhashable type: '{}'", zelf.class().name())))
|
||||
}
|
||||
|
||||
fn call_wrapper(zelf: &PyObject, args: FuncArgs, vm: &VirtualMachine) -> PyResult {
|
||||
|
||||
Reference in New Issue
Block a user