mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 18:59:04 +08:00
Align mapping boundaries with Python 3.15 final (#9128)
Support frozen mappings in str.maketrans and native JSON encoding, preserve integer translation keys, and protect mappingproxy dictionaries from unknown reflected comparisons. Assisted-by: OpenAI Codex:model version unavailable
This commit is contained in:
1 parent
ba54cce23a
commit
3ab5c0c2f2
5 files changed
+43
-24
No files matched your search
@@ -5,7 +5,8 @@ mod _json {
|
||||
use crate::vm::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyResult, VirtualMachine,
|
||||
builtins::{
|
||||
PyBaseExceptionRef, PyDict, PyFloat, PyInt, PyList, PyStr, PyStrRef, PyTuple, PyType,
|
||||
PyAnyDict, PyBaseExceptionRef, PyDict, PyFloat, PyInt, PyList, PyStr, PyStrRef,
|
||||
PyTuple, PyType,
|
||||
},
|
||||
convert::ToPyResult,
|
||||
function::{IntoFuncArgs, OptionalArg},
|
||||
@@ -1126,18 +1127,20 @@ mod _json {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Fetch `dct.items()` for encoding. For an exact `dict` this reads
|
||||
/// Fetch `dct.items()` for encoding. For an exact `dict` or `frozendict` this reads
|
||||
/// the native table directly (matching CPython's fast path); for a
|
||||
/// `dict` subclass (e.g. `collections.OrderedDict`) it calls the
|
||||
/// subclass (e.g. `collections.OrderedDict`) it calls the
|
||||
/// real (possibly overridden) `.items()` method instead, so a
|
||||
/// subclass's custom ordering/behavior is respected.
|
||||
fn dict_items(
|
||||
&self,
|
||||
obj: &PyObject,
|
||||
dict: &Py<PyDict>,
|
||||
dict: &PyAnyDict,
|
||||
vm: &VirtualMachine,
|
||||
) -> PyResult<Vec<(PyObjectRef, PyObjectRef)>> {
|
||||
if obj.class().is(vm.ctx.types.dict_type) {
|
||||
if obj.class().is(vm.ctx.types.dict_type)
|
||||
|| obj.class().is(vm.ctx.types.frozendict_type)
|
||||
{
|
||||
return Ok(dict.items_vec());
|
||||
}
|
||||
let items_obj = obj.get_attr("items", vm)?.call((), vm)?;
|
||||
@@ -1155,7 +1158,7 @@ mod _json {
|
||||
fn encode_dict(
|
||||
&self,
|
||||
obj: &PyObject,
|
||||
dict: &Py<PyDict>,
|
||||
dict: &PyAnyDict,
|
||||
level: isize,
|
||||
out: &mut Wtf8Buf,
|
||||
vm: &VirtualMachine,
|
||||
@@ -1289,7 +1292,7 @@ mod _json {
|
||||
} else if let Some(tuple) = obj.downcast_ref::<PyTuple>() {
|
||||
let slice = tuple.as_slice();
|
||||
self.encode_list(obj, level, out, vm, || slice.len(), |i| slice[i].clone())
|
||||
} else if let Some(dict) = obj.downcast_ref::<PyDict>() {
|
||||
} else if let Some(dict) = PyAnyDict::from_object(obj) {
|
||||
self.encode_dict(obj, dict, level, out, vm)
|
||||
} else {
|
||||
self.encode_default(obj, level, out, vm)
|
||||
|
||||
@@ -2,12 +2,13 @@ use super::{PyDict, PyDictRef, PyGenericAlias, PyList, PyTuple, PyType, PyTypeRe
|
||||
use crate::{
|
||||
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
atomic_func,
|
||||
class::PyClassImpl,
|
||||
class::{PyClassImpl, StaticType},
|
||||
common::{hash, lock::LazyLock},
|
||||
convert::ToPyObject,
|
||||
function::{ArgMapping, Either, OptionalArg, PyComparisonValue},
|
||||
object::{Traverse, TraverseFn},
|
||||
protocol::{PyMappingMethods, PyNumberMethods, PySequenceMethods},
|
||||
stdlib::PyOrderedDict,
|
||||
types::{
|
||||
AsMapping, AsNumber, AsSequence, Constructor, Hashable, Iterable, PyComparisonOp,
|
||||
Representable,
|
||||
@@ -202,8 +203,24 @@ impl Py<PyMappingProxy> {
|
||||
if !matches!(op, PyComparisonOp::Eq | PyComparisonOp::Ne) {
|
||||
return Ok(Either::B(PyComparisonValue::NotImplemented));
|
||||
}
|
||||
let obj = zelf.to_object(vm)?;
|
||||
// Equality comparisons preserve the underlying mapping's result.
|
||||
let mut obj = zelf.to_object(vm)?;
|
||||
// Unknown comparison methods must not receive a mutable dictionary
|
||||
// protected by the proxy. Class namespaces are already copied by
|
||||
// to_object; other exact dicts need the same protection here.
|
||||
if matches!(&zelf.mapping, MappingProxyInner::Mapping(_))
|
||||
&& obj.class().is(vm.ctx.types.dict_type)
|
||||
&& !(other.class().is(vm.ctx.types.dict_type)
|
||||
|| other.class().is(vm.ctx.types.frozendict_type)
|
||||
|| other.class().is(vm.ctx.types.mappingproxy_type)
|
||||
|| (other.downcastable::<PyOrderedDict>()
|
||||
&& other.class().is(PyOrderedDict::static_type())))
|
||||
{
|
||||
obj = obj
|
||||
.downcast_ref::<PyDict>()
|
||||
.unwrap()
|
||||
.copy()
|
||||
.into_pyobject(vm);
|
||||
}
|
||||
obj.rich_compare(other.to_owned(), op, vm).map(Either::A)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::{
|
||||
PositionIterInternal, PyBytesRef, PyDict, PyList, PyTuple, PyTupleRef, PyType, PyTypeRef,
|
||||
PositionIterInternal, PyAnyDict, PyBytesRef, PyDict, PyList, PyTuple, PyTupleRef, PyType,
|
||||
PyTypeRef,
|
||||
int::PyInt,
|
||||
iter::{IterStatus, builtins_iter},
|
||||
};
|
||||
@@ -1580,17 +1581,15 @@ impl Py<PyStr> {
|
||||
)),
|
||||
}
|
||||
} else {
|
||||
// dict_str must be a dict
|
||||
match dict_or_str.downcast::<PyDict>() {
|
||||
Ok(dict) => {
|
||||
// The single-argument form accepts exact dict and frozendict objects.
|
||||
match PyAnyDict::from_object(&dict_or_str).filter(|_| {
|
||||
dict_or_str.class().is(vm.ctx.types.dict_type)
|
||||
|| dict_or_str.class().is(vm.ctx.types.frozendict_type)
|
||||
}) {
|
||||
Some(dict) => {
|
||||
for (key, val) in dict {
|
||||
// FIXME: ints are key-compatible
|
||||
if let Some(num) = key.downcast_ref::<PyInt>() {
|
||||
new_dict.set_item(
|
||||
&*num.as_bigint().to_i32().to_pyobject(vm),
|
||||
val,
|
||||
vm,
|
||||
)?;
|
||||
if key.downcast_ref::<PyInt>().is_some() {
|
||||
new_dict.set_item(&*key, val, vm)?;
|
||||
} else if let Some(string) = key.downcast_ref::<PyStr>() {
|
||||
if string.len() == 1 {
|
||||
let num_value =
|
||||
@@ -1609,7 +1608,7 @@ impl Py<PyStr> {
|
||||
}
|
||||
Ok(new_dict.to_pyobject(vm))
|
||||
}
|
||||
_ => Err(vm.new_value_error(
|
||||
_ => Err(vm.new_type_error(
|
||||
"if you give only one argument to maketrans it must be a dict",
|
||||
)),
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ pub(crate) mod ordered_dict {
|
||||
traverse = "manual"
|
||||
)]
|
||||
#[derive(Debug)]
|
||||
struct PyOrderedDict {
|
||||
pub(crate) struct PyOrderedDict {
|
||||
dict: PyDict,
|
||||
links: PyMutex<ODictLinks>,
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ mod _abc;
|
||||
pub(crate) mod _ast;
|
||||
mod _codecs;
|
||||
mod _collections;
|
||||
pub(crate) use _collections::ordered_dict::ordered_dict::PyOrderedDictItems;
|
||||
pub(crate) use _collections::ordered_dict::ordered_dict::{PyOrderedDict, PyOrderedDictItems};
|
||||
mod _functools;
|
||||
mod _imp;
|
||||
pub mod _io;
|
||||
|
||||
Reference in new issue
Block a user