mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
fix(core): align hashability of dict views, namespace, and typing builtins (#8844)
This commit is contained in:
1 parent
908368eb1c
commit
7329839fb5
11 files changed
+69
-29
No files matched your search
@@ -7,11 +7,11 @@ mod _queue {
|
||||
use std::time::Instant;
|
||||
|
||||
use crate::vm::{
|
||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
AsObject, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||
builtins::{PyBaseExceptionRef, PyException, PyGenericAlias, PyStr, PyType, PyTypeRef},
|
||||
function::{PyComparisonValue, TimeoutSeconds},
|
||||
function::TimeoutSeconds,
|
||||
protocol::PyNumberMethods,
|
||||
types::{AsNumber, Comparable, Constructor, PyComparisonOp, Representable},
|
||||
types::{AsNumber, Constructor, Representable},
|
||||
};
|
||||
|
||||
type BufInner = VecDeque<PyObjectRef>;
|
||||
@@ -143,7 +143,7 @@ mod _queue {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "_queue", name = "SimpleQueue", unhashable = true)]
|
||||
#[pyclass(module = "_queue", name = "SimpleQueue")]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PySimpleQueue {
|
||||
buf: Buf,
|
||||
@@ -227,7 +227,7 @@ mod _queue {
|
||||
}
|
||||
|
||||
#[pyclass(
|
||||
with(Constructor, Comparable, Representable),
|
||||
with(Constructor, Representable),
|
||||
flags(BASETYPE, HAS_WEAKREF, IMMUTABLETYPE)
|
||||
)]
|
||||
impl PySimpleQueue {
|
||||
@@ -329,21 +329,6 @@ mod _queue {
|
||||
}
|
||||
}
|
||||
|
||||
impl Comparable for PySimpleQueue {
|
||||
fn cmp(
|
||||
zelf: &Py<Self>,
|
||||
other: &PyObject,
|
||||
op: PyComparisonOp,
|
||||
_vm: &VirtualMachine,
|
||||
) -> PyResult<PyComparisonValue> {
|
||||
Ok(if let Some(res) = op.identical_optimization(zelf, other) {
|
||||
res.into()
|
||||
} else {
|
||||
PyComparisonValue::NotImplemented
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Representable for PySimpleQueue {
|
||||
fn repr(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<PyRef<PyStr>> {
|
||||
Ok(vm.ctx.new_str(format!(
|
||||
|
||||
@@ -3298,7 +3298,7 @@ mod _ssl {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(module = "ssl", name = "SSLSession")]
|
||||
#[pyclass(module = "ssl", name = "SSLSession", unhashable = true)]
|
||||
#[derive(PyPayload)]
|
||||
struct PySslSession {
|
||||
session: *mut sys::SSL_SESSION,
|
||||
|
||||
@@ -3979,7 +3979,7 @@ mod _ssl {
|
||||
// SSLSession - represents a cached SSL session
|
||||
// NOTE: This is an EMULATION - actual session data is managed by Rustls internally
|
||||
#[pyattr]
|
||||
#[pyclass(name = "SSLSession", module = "ssl")]
|
||||
#[pyclass(name = "SSLSession", module = "ssl", unhashable = true)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PySSLSession {
|
||||
context_identity: Arc<()>,
|
||||
|
||||
@@ -737,7 +737,7 @@ mod _ssl {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "SSLSession", module = "ssl")]
|
||||
#[pyclass(name = "SSLSession", module = "ssl", unhashable = true)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PySSLSession {
|
||||
session_id: Vec<u8>,
|
||||
|
||||
@@ -1165,10 +1165,11 @@ macro_rules! dict_view {
|
||||
$class_name: literal,
|
||||
$iter_class_name: literal,
|
||||
$reverse_iter_class_name: literal,
|
||||
$unhashable: literal,
|
||||
$project_fn: expr,
|
||||
$result_fn: expr
|
||||
) => {
|
||||
#[pyclass(module = false, name = $class_name)]
|
||||
#[pyclass(module = false, name = $class_name, unhashable = $unhashable)]
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct $name {
|
||||
pub(crate) dict: PyDictRef,
|
||||
@@ -1456,6 +1457,7 @@ dict_view! {
|
||||
"dict_keys",
|
||||
"dict_keyiterator",
|
||||
"dict_reversekeyiterator",
|
||||
true,
|
||||
|key: &PyObject, _value| key.to_owned(),
|
||||
|_vm: &VirtualMachine, key: PyObjectRef| key
|
||||
}
|
||||
@@ -1470,6 +1472,7 @@ dict_view! {
|
||||
"dict_values",
|
||||
"dict_valueiterator",
|
||||
"dict_reversevalueiterator",
|
||||
false,
|
||||
|_key: &PyObject, value: &PyObjectRef| value.clone(),
|
||||
|_vm: &VirtualMachine, value: PyObjectRef| value
|
||||
}
|
||||
@@ -1484,6 +1487,7 @@ dict_view! {
|
||||
"dict_items",
|
||||
"dict_itemiterator",
|
||||
"dict_reverseitemiterator",
|
||||
true,
|
||||
|key: &PyObject, value: &PyObjectRef| (key.to_owned(), value.clone()),
|
||||
// Builds a tuple, so it runs after the dict's read guard is released.
|
||||
|vm: &VirtualMachine, (key, value): (PyObjectRef, PyObjectRef)|
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
};
|
||||
use rustpython_common::wtf8::Wtf8Buf;
|
||||
|
||||
#[pyclass(module = "types", name = "SimpleNamespace")]
|
||||
#[pyclass(module = "types", name = "SimpleNamespace", unhashable = true)]
|
||||
// The dict lives in the object extension, not the payload. The offset is
|
||||
// the dict cell at the front of that extension.
|
||||
#[pymember(name = "__dict__", offset = ::rustpython_vm::object::dict_member_offset())]
|
||||
|
||||
@@ -978,7 +978,7 @@ pub(crate) mod ordered_dict {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "odict_keys", traverse)]
|
||||
#[pyclass(name = "odict_keys", unhashable = true, traverse)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
struct PyODictKeys {
|
||||
od: PyRef<PyOrderedDict>,
|
||||
@@ -1122,7 +1122,7 @@ pub(crate) mod ordered_dict {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "odict_items", traverse)]
|
||||
#[pyclass(name = "odict_items", unhashable = true, traverse)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub(crate) struct PyOrderedDictItems {
|
||||
od: PyRef<PyOrderedDict>,
|
||||
|
||||
@@ -845,7 +845,7 @@ pub(crate) mod typevar {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "ParamSpecArgs", module = "typing")]
|
||||
#[pyclass(name = "ParamSpecArgs", module = "typing", unhashable = true)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub struct ParamSpecArgs {
|
||||
#[pymember]
|
||||
@@ -905,7 +905,7 @@ pub(crate) mod typevar {
|
||||
}
|
||||
|
||||
#[pyattr]
|
||||
#[pyclass(name = "ParamSpecKwargs", module = "typing")]
|
||||
#[pyclass(name = "ParamSpecKwargs", module = "typing", unhashable = true)]
|
||||
#[derive(Debug, PyPayload)]
|
||||
pub struct ParamSpecKwargs {
|
||||
#[pymember]
|
||||
|
||||
@@ -687,3 +687,28 @@ class MergeMapping(dict):
|
||||
# Generic mappings retain their lookup hooks instead of exposing dict storage.
|
||||
for merge in (dict, merge_with_update, merge_with_ior):
|
||||
assert merge(MergeMapping(stored=0)) == {"virtual": 42}
|
||||
|
||||
# Test hashability of dict and OrderedDict views
|
||||
import collections.abc
|
||||
|
||||
d = {"a": 1, "b": 2}
|
||||
assert type(d.keys()).__hash__ is None
|
||||
assert type(d.items()).__hash__ is None
|
||||
assert type(d.values()).__hash__ is not None
|
||||
assert not isinstance(d.keys(), collections.abc.Hashable)
|
||||
assert not isinstance(d.items(), collections.abc.Hashable)
|
||||
with assert_raises(TypeError):
|
||||
hash(d.keys())
|
||||
with assert_raises(TypeError):
|
||||
hash(d.items())
|
||||
|
||||
od = collections.OrderedDict([("a", 1)])
|
||||
assert type(od.keys()).__hash__ is None
|
||||
assert type(od.items()).__hash__ is None
|
||||
assert type(od.values()).__hash__ is not None
|
||||
assert not isinstance(od.keys(), collections.abc.Hashable)
|
||||
assert not isinstance(od.items(), collections.abc.Hashable)
|
||||
with assert_raises(TypeError):
|
||||
hash(od.keys())
|
||||
with assert_raises(TypeError):
|
||||
hash(od.items())
|
||||
@@ -12,6 +12,13 @@ assert ns.b == "Rust"
|
||||
with assert_raises(AttributeError):
|
||||
_ = ns.c
|
||||
|
||||
import collections.abc
|
||||
|
||||
assert types.SimpleNamespace.__hash__ is None
|
||||
assert not isinstance(ns, collections.abc.Hashable)
|
||||
with assert_raises(TypeError):
|
||||
hash(ns)
|
||||
|
||||
|
||||
def _run_missing_type_params_regression():
|
||||
args = _ast.arguments(
|
||||
|
||||
@@ -10,3 +10,22 @@ print(curry.__annotations__)
|
||||
assert curry.__annotations__['foo'] is int
|
||||
assert curry.__annotations__['return'] is float
|
||||
assert curry.__annotations__['bla'] is int
|
||||
|
||||
import collections.abc
|
||||
import typing
|
||||
|
||||
P = typing.ParamSpec('P')
|
||||
assert P.args.__hash__ is None
|
||||
assert P.kwargs.__hash__ is None
|
||||
assert not isinstance(P.args, collections.abc.Hashable)
|
||||
assert not isinstance(P.kwargs, collections.abc.Hashable)
|
||||
try:
|
||||
hash(P.args)
|
||||
assert False, "expected TypeError"
|
||||
except TypeError:
|
||||
pass
|
||||
try:
|
||||
hash(P.kwargs)
|
||||
assert False, "expected TypeError"
|
||||
except TypeError:
|
||||
pass
|
||||
Reference in new issue
Block a user