mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
Finish frozendict with borrowed PyAnyDict views
Use the landed PyRef target model for a transparent dict/frozendict header view, retaining objects explicitly with PyRef<PyAnyDict>. Keep targets sealed and preserve mutable-only entry points. Borrow frame globals from the object header rather than movable reference wrappers. Unify exact-TypeError key diagnostics with final CPython semantics, including callback failure propagation and no explicit cause. Keep the Python regressions in builtin_dict and test reference layout/ownership in Rust. Preserve existing canonical library updates. Assisted-by: OpenAI Codex:model identifier unavailable
This commit is contained in:
1 parent
ea0bc900d2
commit
c78d811fc4
17 files changed
+368
-146
No files matched your search
@@ -3,10 +3,10 @@ use crate::pystate::with_vm;
|
|||||||
use crate::unicodeobject::decode_fsdefault_and_size;
|
use crate::unicodeobject::decode_fsdefault_and_size;
|
||||||
use crate::util::{CStrExt, FfiPtrExt};
|
use crate::util::{CStrExt, FfiPtrExt};
|
||||||
use core::ffi::{CStr, c_char, c_int};
|
use core::ffi::{CStr, c_char, c_int};
|
||||||
use rustpython_vm::builtins::{PyAnyDictRef, PyCode};
|
use rustpython_vm::builtins::{PyAnyDict, PyCode};
|
||||||
use rustpython_vm::function::ArgMapping;
|
use rustpython_vm::function::ArgMapping;
|
||||||
use rustpython_vm::scope::Scope;
|
use rustpython_vm::scope::Scope;
|
||||||
use rustpython_vm::{AsObject, PyObject, TryFromObject};
|
use rustpython_vm::{AsObject, PyObject, PyRef, TryFromObject};
|
||||||
use rustpython_vm::{PyObjectRef, version};
|
use rustpython_vm::{PyObjectRef, version};
|
||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
@@ -32,8 +32,10 @@ pub unsafe extern "C" fn PyEval_EvalCode(
|
|||||||
) -> *mut PyObject {
|
) -> *mut PyObject {
|
||||||
with_vm(|vm| {
|
with_vm(|vm| {
|
||||||
let code = unsafe { co.assume_borrowed_and_cast::<PyCode>(vm) }?;
|
let code = unsafe { co.assume_borrowed_and_cast::<PyCode>(vm) }?;
|
||||||
let globals =
|
let globals = PyRef::<PyAnyDict>::try_from_object(
|
||||||
PyAnyDictRef::try_from_object(vm, unsafe { globals.assume_borrowed() }.to_owned())?;
|
vm,
|
||||||
|
unsafe { globals.assume_borrowed() }.to_owned(),
|
||||||
|
)?;
|
||||||
let locals = unsafe { locals.assume_borrowed_or_opt() }
|
let locals = unsafe { locals.assume_borrowed_or_opt() }
|
||||||
.map(|obj| ArgMapping::try_from_object(vm, obj.to_owned()))
|
.map(|obj| ArgMapping::try_from_object(vm, obj.to_owned()))
|
||||||
.transpose()?;
|
.transpose()?;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ use crate::pystate::with_vm;
|
|||||||
use crate::util::{CStrExt, FfiPtrExt};
|
use crate::util::{CStrExt, FfiPtrExt};
|
||||||
use core::ffi::{c_char, c_int};
|
use core::ffi::{c_char, c_int};
|
||||||
use core::ptr::NonNull;
|
use core::ptr::NonNull;
|
||||||
use rustpython_vm::builtins::{PyAnyDictRef, PyDict, PyFrozenDict};
|
use rustpython_vm::builtins::{PyAnyDict, PyDict, PyFrozenDict};
|
||||||
use rustpython_vm::{AsObject, Py, PyPayload, PyResult, VirtualMachine};
|
use rustpython_vm::{AsObject, Py, PyPayload, PyResult, VirtualMachine};
|
||||||
|
|
||||||
define_py_check!(fn PyDict_Check, types.dict_type);
|
define_py_check!(fn PyDict_Check, types.dict_type);
|
||||||
@@ -13,8 +13,8 @@ define_py_check!(fn PyDictKeys_Check, types.dict_keys_type);
|
|||||||
define_py_check!(fn PyDictValues_Check, types.dict_values_type);
|
define_py_check!(fn PyDictValues_Check, types.dict_values_type);
|
||||||
define_py_check!(fn PyDictItems_Check, types.dict_items_type);
|
define_py_check!(fn PyDictItems_Check, types.dict_items_type);
|
||||||
|
|
||||||
fn any_dict(dict: &PyObject, vm: &VirtualMachine) -> PyResult<PyAnyDictRef> {
|
fn any_dict<'a>(dict: &'a PyObject, vm: &VirtualMachine) -> PyResult<&'a PyAnyDict> {
|
||||||
PyAnyDictRef::from_object(dict)
|
PyAnyDict::from_object(dict)
|
||||||
.ok_or_else(|| vm.new_system_error("bad argument to internal function"))
|
.ok_or_else(|| vm.new_system_error("bad argument to internal function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ pub unsafe extern "C" fn PyDict_SetItemString(
|
|||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
pub unsafe extern "C" fn PyDict_GetItem(dict: *mut PyObject, key: *mut PyObject) -> *mut PyObject {
|
pub unsafe extern "C" fn PyDict_GetItem(dict: *mut PyObject, key: *mut PyObject) -> *mut PyObject {
|
||||||
with_vm(|vm| {
|
with_vm(|vm| {
|
||||||
let Some(dict) = PyAnyDictRef::from_object(unsafe { dict.assume_borrowed() }) else {
|
let Some(dict) = PyAnyDict::from_object(unsafe { dict.assume_borrowed() }) else {
|
||||||
return core::ptr::null_mut();
|
return core::ptr::null_mut();
|
||||||
};
|
};
|
||||||
let key = unsafe { key.assume_borrowed() };
|
let key = unsafe { key.assume_borrowed() };
|
||||||
@@ -117,7 +117,7 @@ pub unsafe extern "C" fn PyDict_GetItemString(
|
|||||||
let Ok(key) = (unsafe { key.try_as_str(vm) }) else {
|
let Ok(key) = (unsafe { key.try_as_str(vm) }) else {
|
||||||
return core::ptr::null_mut();
|
return core::ptr::null_mut();
|
||||||
};
|
};
|
||||||
let Some(dict) = PyAnyDictRef::from_object(unsafe { dict.assume_borrowed() }) else {
|
let Some(dict) = PyAnyDict::from_object(unsafe { dict.assume_borrowed() }) else {
|
||||||
return core::ptr::null_mut();
|
return core::ptr::null_mut();
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -347,7 +347,7 @@ pub unsafe extern "C" fn PyDict_Next(
|
|||||||
value: *mut *mut PyObject,
|
value: *mut *mut PyObject,
|
||||||
) -> c_int {
|
) -> c_int {
|
||||||
with_vm(|_vm| {
|
with_vm(|_vm| {
|
||||||
let Some(dict) = PyAnyDictRef::from_object(unsafe { dict.assume_borrowed() }) else {
|
let Some(dict) = PyAnyDict::from_object(unsafe { dict.assume_borrowed() }) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
let index = unsafe { *pos } as usize;
|
let index = unsafe { *pos } as usize;
|
||||||
|
|||||||
+188
-84
@@ -41,9 +41,17 @@ pub(crate) fn key_hash<K: DictKey + ?Sized>(
|
|||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<PyHash> {
|
) -> PyResult<PyHash> {
|
||||||
match key.key_hash(vm) {
|
match key.key_hash(vm) {
|
||||||
|
// Exact TypeError only: exception subclasses retain their identity.
|
||||||
Err(exc) if exc.class().is(vm.ctx.exceptions.type_error) => {
|
Err(exc) if exc.class().is(vm.ctx.exceptions.type_error) => {
|
||||||
let name = key.to_pyobject(vm).class().fully_qualified_name(vm)?;
|
let name = key.to_pyobject(vm).class().fully_qualified_name(vm)?;
|
||||||
let message = exc.as_object().str(vm)?;
|
// Match BaseException_str without the diagnostic formatter's
|
||||||
|
// fallback: argument __str__/__repr__ failures must propagate.
|
||||||
|
let args = exc.args();
|
||||||
|
let message = match args.as_slice() {
|
||||||
|
[] => vm.ctx.empty_str.to_owned(),
|
||||||
|
[arg] => arg.str(vm)?,
|
||||||
|
_ => args.as_object().str(vm)?,
|
||||||
|
};
|
||||||
Err(vm.new_type_error(format!(
|
Err(vm.new_type_error(format!(
|
||||||
"cannot use '{name}' as a {mapping_name} key ({message})"
|
"cannot use '{name}' as a {mapping_name} key ({message})"
|
||||||
)))
|
)))
|
||||||
@@ -61,71 +69,108 @@ pub struct PyDict {
|
|||||||
}
|
}
|
||||||
pub type PyDictRef = PyRef<PyDict>;
|
pub type PyDictRef = PyRef<PyDict>;
|
||||||
|
|
||||||
/// A checked reference to a dict or frozendict, preserving the object's mutability.
|
/// A borrowed view of a dict or frozendict, including subclasses.
|
||||||
/// Its single-pointer layout also supports readonly object members such as __globals__.
|
///
|
||||||
|
/// This is an erased header view, not a shared payload layout. Every payload
|
||||||
|
/// access downcasts to the actual alternative, preserving its mutability.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Clone)]
|
pub struct PyAnyDict {
|
||||||
pub struct PyAnyDictRef(PyObjectRef);
|
object: PyObject,
|
||||||
|
|
||||||
impl super::descriptor::MemberLayout for PyAnyDictRef {
|
|
||||||
const KIND: super::descriptor::MemberKind = super::descriptor::MemberKind::Object;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl Traverse for PyAnyDictRef {
|
// SAFETY: this owner contributes exactly one strong reference to the object.
|
||||||
|
unsafe impl Traverse for PyRef<PyAnyDict> {
|
||||||
fn traverse(&self, tracer: &mut TraverseFn<'_>) {
|
fn traverse(&self, tracer: &mut TraverseFn<'_>) {
|
||||||
self.0.traverse(tracer);
|
tracer(self.as_object());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Borrow<PyObject> for PyAnyDictRef {
|
impl crate::convert::ToPyObject for PyRef<PyAnyDict> {
|
||||||
fn borrow(&self) -> &PyObject {
|
|
||||||
&self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<PyDictRef> for PyAnyDictRef {
|
|
||||||
fn from(dict: PyDictRef) -> Self {
|
|
||||||
Self(dict.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<PyRef<PyFrozenDict>> for PyAnyDictRef {
|
|
||||||
fn from(dict: PyRef<PyFrozenDict>) -> Self {
|
|
||||||
Self(dict.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl From<PyAnyDictRef> for PyObjectRef {
|
|
||||||
fn from(dict: PyAnyDictRef) -> Self {
|
|
||||||
dict.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl crate::convert::ToPyObject for PyAnyDictRef {
|
|
||||||
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
|
fn to_pyobject(self, _vm: &VirtualMachine) -> PyObjectRef {
|
||||||
self.into()
|
self.into_object()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl TryFromObject for PyAnyDictRef {
|
|
||||||
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
impl Borrow<PyObject> for PyAnyDict {
|
||||||
Self::from_object(&obj).ok_or_else(|| {
|
fn borrow(&self) -> &PyObject {
|
||||||
vm.new_type_error(format!(
|
&self.object
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Borrow<PyAnyDict> for PyRef<PyAnyDict> {
|
||||||
|
fn borrow(&self) -> &PyAnyDict {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToOwned for PyAnyDict {
|
||||||
|
type Owned = PyRef<Self>;
|
||||||
|
|
||||||
|
fn to_owned(&self) -> PyRef<Self> {
|
||||||
|
// SAFETY: this borrowed view already guarantees a dictionary payload.
|
||||||
|
unsafe { PyRef::<Self>::from_object_unchecked(self.object.to_owned()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PyDictRef> for PyRef<PyAnyDict> {
|
||||||
|
fn from(dict: PyDictRef) -> Self {
|
||||||
|
// SAFETY: the typed owner guarantees the dict alternative.
|
||||||
|
unsafe { Self::from_object_unchecked(dict.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PyRef<PyFrozenDict>> for PyRef<PyAnyDict> {
|
||||||
|
fn from(dict: PyRef<PyFrozenDict>) -> Self {
|
||||||
|
// SAFETY: the typed owner guarantees the frozendict alternative.
|
||||||
|
unsafe { Self::from_object_unchecked(dict.into()) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PyRef<PyAnyDict>> for PyObjectRef {
|
||||||
|
fn from(dict: PyRef<PyAnyDict>) -> Self {
|
||||||
|
dict.into_object()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SAFETY: the transparent PyRef owner has the same layout as PyObjectRef.
|
||||||
|
// A successful check proves the actual dict or frozendict payload invariant.
|
||||||
|
unsafe impl crate::convert::TransmuteFromObject for PyRef<PyAnyDict> {
|
||||||
|
fn check(vm: &VirtualMachine, obj: &PyObject) -> PyResult<()> {
|
||||||
|
if PyAnyDict::from_object(obj).is_some() {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(vm.new_type_error(format!(
|
||||||
"expected dict or frozendict, got {}",
|
"expected dict or frozendict, got {}",
|
||||||
obj.class().name()
|
obj.class().name()
|
||||||
))
|
)))
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyAnyDictRef {
|
impl TryFromObject for PyRef<PyAnyDict> {
|
||||||
pub fn from_object(obj: &PyObject) -> Option<Self> {
|
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
|
||||||
(obj.downcastable::<PyDict>() || obj.downcastable::<PyFrozenDict>())
|
<Self as crate::convert::TransmuteFromObject>::check(vm, &obj)?;
|
||||||
.then(|| Self(obj.to_owned()))
|
// SAFETY: the shared checker proved the union view's payload invariant.
|
||||||
|
Ok(unsafe { Self::from_object_unchecked(obj) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PyAnyDict {
|
||||||
|
pub fn from_object(obj: &PyObject) -> Option<&Self> {
|
||||||
|
if obj.downcast_ref::<PyDict>().is_some() || obj.downcast_ref::<PyFrozenDict>().is_some() {
|
||||||
|
// SAFETY: PyAnyDict is transparent over PyObject and the payload was checked.
|
||||||
|
Some(unsafe { &*(obj as *const PyObject).cast::<Self>() })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn as_mutable(&self) -> Option<&Py<PyDict>> {
|
pub(crate) fn as_mutable(&self) -> Option<&Py<PyDict>> {
|
||||||
self.0.downcast_ref()
|
self.object.downcast_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn is_frozen(&self) -> bool {
|
pub(crate) fn is_frozen(&self) -> bool {
|
||||||
self.0.downcastable::<PyFrozenDict>()
|
self.object.downcastable::<PyFrozenDict>()
|
||||||
}
|
}
|
||||||
|
|
||||||
// The raw payload is private: callers outside the VM cannot use it to mutate a frozendict.
|
// The raw payload is private: callers outside the VM cannot use it to mutate a frozendict.
|
||||||
@@ -133,7 +178,7 @@ impl PyAnyDictRef {
|
|||||||
if let Some(dict) = self.as_mutable() {
|
if let Some(dict) = self.as_mutable() {
|
||||||
dict
|
dict
|
||||||
} else {
|
} else {
|
||||||
&self.0.downcast_ref::<PyFrozenDict>().unwrap().dict
|
&self.object.downcast_ref::<PyFrozenDict>().unwrap().dict
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,7 +232,7 @@ impl PyAnyDictRef {
|
|||||||
if let Some(dict) = self.as_mutable() {
|
if let Some(dict) = self.as_mutable() {
|
||||||
dict.inner_getitem_opt(key, vm)
|
dict.inner_getitem_opt(key, vm)
|
||||||
} else {
|
} else {
|
||||||
self.0
|
self.object
|
||||||
.downcast_ref::<PyFrozenDict>()
|
.downcast_ref::<PyFrozenDict>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.inner_getitem_opt(key, vm)
|
.inner_getitem_opt(key, vm)
|
||||||
@@ -201,10 +246,10 @@ impl PyAnyDictRef {
|
|||||||
if let Some(dict) = self.as_mutable() {
|
if let Some(dict) = self.as_mutable() {
|
||||||
return dict.get_item_opt(key, vm);
|
return dict.get_item_opt(key, vm);
|
||||||
}
|
}
|
||||||
if self.0.class().is(vm.ctx.types.frozendict_type) {
|
if self.object.class().is(vm.ctx.types.frozendict_type) {
|
||||||
return self.inner_getitem_opt(key, vm);
|
return self.inner_getitem_opt(key, vm);
|
||||||
}
|
}
|
||||||
match self.0.get_item(key, vm) {
|
match self.object.get_item(key, vm) {
|
||||||
Ok(value) => Ok(Some(value)),
|
Ok(value) => Ok(Some(value)),
|
||||||
Err(exc) if exc.fast_isinstance(vm.ctx.exceptions.key_error) => Ok(None),
|
Err(exc) if exc.fast_isinstance(vm.ctx.exceptions.key_error) => Ok(None),
|
||||||
Err(exc) => Err(exc),
|
Err(exc) => Err(exc),
|
||||||
@@ -214,7 +259,7 @@ impl PyAnyDictRef {
|
|||||||
if let Some(dict) = self.as_mutable() {
|
if let Some(dict) = self.as_mutable() {
|
||||||
dict.get_item(key, vm)
|
dict.get_item(key, vm)
|
||||||
} else {
|
} else {
|
||||||
self.0.get_item(key, vm)
|
self.object.get_item(key, vm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pub fn contains_key<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> bool {
|
pub fn contains_key<K: DictKey + ?Sized>(&self, key: &K, vm: &VirtualMachine) -> bool {
|
||||||
@@ -240,7 +285,7 @@ impl PyAnyDictRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> IntoIterator for &'a PyAnyDictRef {
|
impl<'a> IntoIterator for &'a PyAnyDict {
|
||||||
type Item = (PyObjectRef, PyObjectRef);
|
type Item = (PyObjectRef, PyObjectRef);
|
||||||
type IntoIter = DictIter<'a>;
|
type IntoIter = DictIter<'a>;
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
@@ -354,7 +399,7 @@ impl PyDict {
|
|||||||
override_existing: bool,
|
override_existing: bool,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
if let Some(other_dict) = PyAnyDictRef::from_object(&other)
|
if let Some(other_dict) = PyAnyDict::from_object(&other)
|
||||||
&& other_dict.uses_builtin_iter(vm)
|
&& other_dict.uses_builtin_iter(vm)
|
||||||
{
|
{
|
||||||
return self.merge_dict(other_dict.as_dict(), override_existing, vm);
|
return self.merge_dict(other_dict.as_dict(), override_existing, vm);
|
||||||
@@ -520,22 +565,7 @@ impl PyDict {
|
|||||||
/// Hashing up front also means a `__hash__` that fails only intermittently
|
/// Hashing up front also means a `__hash__` that fails only intermittently
|
||||||
/// is still reported.
|
/// is still reported.
|
||||||
fn hash_or_unhashable<K: DictKey + ?Sized>(key: &K, vm: &VirtualMachine) -> PyResult<PyHash> {
|
fn hash_or_unhashable<K: DictKey + ?Sized>(key: &K, vm: &VirtualMachine) -> PyResult<PyHash> {
|
||||||
match key.key_hash(vm) {
|
key_hash(key, "dict", vm)
|
||||||
Ok(hash) => Ok(hash),
|
|
||||||
// Exact `TypeError` only: a `__hash__` raising a *subclass* of
|
|
||||||
// `TypeError` (e.g. a user `MyTypeError`) must propagate unchanged,
|
|
||||||
// matching CPython's exact-type check.
|
|
||||||
Err(cause) if cause.class().is(vm.ctx.exceptions.type_error) => {
|
|
||||||
let message = cause.as_object().str(vm)?;
|
|
||||||
let key = key.to_pyobject(vm);
|
|
||||||
let name = key.class().fully_qualified_name(vm)?;
|
|
||||||
let err =
|
|
||||||
vm.new_type_error(format!("cannot use '{name}' as a dict key ({message})"));
|
|
||||||
err.set_cause(Some(cause));
|
|
||||||
Err(err)
|
|
||||||
}
|
|
||||||
Err(other) => Err(other),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set item variant which can be called with multiple
|
/// Set item variant which can be called with multiple
|
||||||
@@ -945,7 +975,7 @@ impl Comparable for PyDict {
|
|||||||
op: PyComparisonOp,
|
op: PyComparisonOp,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<PyComparisonValue> {
|
) -> PyResult<PyComparisonValue> {
|
||||||
op.eq_only(|| match PyAnyDictRef::from_object(other) {
|
op.eq_only(|| match PyAnyDict::from_object(other) {
|
||||||
Some(other) => {
|
Some(other) => {
|
||||||
zelf.payload
|
zelf.payload
|
||||||
.compare_entries(other.as_dict(), PyComparisonOp::Eq, true, vm)
|
.compare_entries(other.as_dict(), PyComparisonOp::Eq, true, vm)
|
||||||
@@ -1411,7 +1441,7 @@ impl PyFrozenDict {
|
|||||||
other: PyObjectRef,
|
other: PyObjectRef,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
if let Some(other_dict) = PyAnyDictRef::from_object(&other)
|
if let Some(other_dict) = PyAnyDict::from_object(&other)
|
||||||
&& other_dict.uses_builtin_iter(vm)
|
&& other_dict.uses_builtin_iter(vm)
|
||||||
{
|
{
|
||||||
return dict.merge_dict(other_dict.as_dict(), true, vm);
|
return dict.merge_dict(other_dict.as_dict(), true, vm);
|
||||||
@@ -1436,8 +1466,7 @@ impl PyFrozenDict {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn union(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
|
pub(crate) fn union(a: &PyObject, b: &PyObject, vm: &VirtualMachine) -> PyResult {
|
||||||
let (Some(left), Some(right)) =
|
let (Some(left), Some(right)) = (PyAnyDict::from_object(a), PyAnyDict::from_object(b))
|
||||||
(PyAnyDictRef::from_object(a), PyAnyDictRef::from_object(b))
|
|
||||||
else {
|
else {
|
||||||
return Ok(vm.ctx.not_implemented());
|
return Ok(vm.ctx.not_implemented());
|
||||||
};
|
};
|
||||||
@@ -1649,7 +1678,7 @@ impl Comparable for PyFrozenDict {
|
|||||||
op: PyComparisonOp,
|
op: PyComparisonOp,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<PyComparisonValue> {
|
) -> PyResult<PyComparisonValue> {
|
||||||
op.eq_only(|| match PyAnyDictRef::from_object(other) {
|
op.eq_only(|| match PyAnyDict::from_object(other) {
|
||||||
Some(other) => zelf
|
Some(other) => zelf
|
||||||
.dict
|
.dict
|
||||||
.compare_entries(other.as_dict(), PyComparisonOp::Eq, true, vm),
|
.compare_entries(other.as_dict(), PyComparisonOp::Eq, true, vm),
|
||||||
@@ -1803,7 +1832,7 @@ impl ExactSizeIterator for DictIter<'_> {
|
|||||||
trait DictView: PyPayload + PyClassDef + Iterable + Representable {
|
trait DictView: PyPayload + PyClassDef + Iterable + Representable {
|
||||||
type ReverseIter: PyPayload + core::fmt::Debug;
|
type ReverseIter: PyPayload + core::fmt::Debug;
|
||||||
|
|
||||||
fn dict(&self) -> &PyAnyDictRef;
|
fn dict(&self) -> &PyAnyDict;
|
||||||
fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef;
|
fn item(vm: &VirtualMachine, key: PyObjectRef, value: PyObjectRef) -> PyObjectRef;
|
||||||
|
|
||||||
fn __len__(&self) -> usize {
|
fn __len__(&self) -> usize {
|
||||||
@@ -1832,11 +1861,11 @@ macro_rules! dict_view {
|
|||||||
#[pyclass(module = false, name = $class_name, unhashable = $unhashable, traverse)]
|
#[pyclass(module = false, name = $class_name, unhashable = $unhashable, traverse)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(crate) struct $name {
|
pub(crate) struct $name {
|
||||||
pub(crate) dict: PyAnyDictRef,
|
pub(crate) dict: PyRef<PyAnyDict>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl $name {
|
impl $name {
|
||||||
pub(crate) fn new(dict: PyAnyDictRef) -> Self {
|
pub(crate) fn new(dict: PyRef<PyAnyDict>) -> Self {
|
||||||
$name { dict }
|
$name { dict }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1844,7 +1873,7 @@ macro_rules! dict_view {
|
|||||||
impl DictView for $name {
|
impl DictView for $name {
|
||||||
type ReverseIter = $reverse_iter_name;
|
type ReverseIter = $reverse_iter_name;
|
||||||
|
|
||||||
fn dict(&self) -> &PyAnyDictRef {
|
fn dict(&self) -> &PyAnyDict {
|
||||||
&self.dict
|
&self.dict
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1907,7 +1936,7 @@ macro_rules! dict_view {
|
|||||||
/// from then on.
|
/// from then on.
|
||||||
#[pytraverse(skip)]
|
#[pytraverse(skip)]
|
||||||
changed: PyAtomic<bool>,
|
changed: PyAtomic<bool>,
|
||||||
pub(crate) internal: PyMutex<PositionIterInternal<PyAnyDictRef>>,
|
pub(crate) internal: PyMutex<PositionIterInternal<PyRef<PyAnyDict>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyPayload for $iter_name {
|
impl PyPayload for $iter_name {
|
||||||
@@ -1918,7 +1947,7 @@ macro_rules! dict_view {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl $iter_name {
|
impl $iter_name {
|
||||||
pub(crate) fn new(dict: PyAnyDictRef) -> Self {
|
pub(crate) fn new(dict: PyRef<PyAnyDict>) -> Self {
|
||||||
$iter_name {
|
$iter_name {
|
||||||
size: dict.size(),
|
size: dict.size(),
|
||||||
changed: Radium::new(false),
|
changed: Radium::new(false),
|
||||||
@@ -2014,7 +2043,7 @@ macro_rules! dict_view {
|
|||||||
changed: PyAtomic<bool>,
|
changed: PyAtomic<bool>,
|
||||||
#[pytraverse(skip)]
|
#[pytraverse(skip)]
|
||||||
remaining: PyAtomic<usize>,
|
remaining: PyAtomic<usize>,
|
||||||
internal: PyMutex<PositionIterInternal<PyAnyDictRef>>,
|
internal: PyMutex<PositionIterInternal<PyRef<PyAnyDict>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PyPayload for $reverse_iter_name {
|
impl PyPayload for $reverse_iter_name {
|
||||||
@@ -2025,7 +2054,7 @@ macro_rules! dict_view {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl $reverse_iter_name {
|
impl $reverse_iter_name {
|
||||||
pub(crate) fn new(dict: PyAnyDictRef) -> Self {
|
pub(crate) fn new(dict: PyRef<PyAnyDict>) -> Self {
|
||||||
let size = dict.size();
|
let size = dict.size();
|
||||||
// usize::MAX represents the position before the first entry.
|
// usize::MAX represents the position before the first entry.
|
||||||
let position = size.entries_size.wrapping_sub(1);
|
let position = size.entries_size.wrapping_sub(1);
|
||||||
@@ -2261,7 +2290,7 @@ impl PyDictKeys {
|
|||||||
|
|
||||||
#[pygetset]
|
#[pygetset]
|
||||||
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
||||||
PyMappingProxy::from_any_dict(zelf.dict().clone())
|
PyMappingProxy::from_any_dict(zelf.dict().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2324,7 +2353,7 @@ impl PyDictItems {
|
|||||||
}
|
}
|
||||||
#[pygetset]
|
#[pygetset]
|
||||||
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
||||||
PyMappingProxy::from_any_dict(zelf.dict().clone())
|
PyMappingProxy::from_any_dict(zelf.dict().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2386,7 +2415,7 @@ impl AsNumber for PyDictItems {
|
|||||||
impl PyDictValues {
|
impl PyDictValues {
|
||||||
#[pygetset]
|
#[pygetset]
|
||||||
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
fn mapping(zelf: PyRef<Self>) -> PyMappingProxy {
|
||||||
PyMappingProxy::from_any_dict(zelf.dict().clone())
|
PyMappingProxy::from_any_dict(zelf.dict().to_owned())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2607,3 +2636,78 @@ pub(crate) fn init(context: &'static Context) {
|
|||||||
.vectorcall
|
.vectorcall
|
||||||
.store(Some(vectorcall_frozendict));
|
.store(Some(vectorcall_frozendict));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anydict_reference_layout_and_ownership() {
|
||||||
|
static_assertions::assert_not_impl_any!(PyAnyDict: PyPayload);
|
||||||
|
static_assertions::assert_type_eq_all!(
|
||||||
|
<PyAnyDict as crate::object::PyRefTarget>::Target,
|
||||||
|
PyAnyDict
|
||||||
|
);
|
||||||
|
static_assertions::assert_eq_size!(PyRef<PyAnyDict>, PyObjectRef, Option<PyRef<PyAnyDict>>);
|
||||||
|
static_assertions::assert_eq_align!(PyRef<PyAnyDict>, PyObjectRef);
|
||||||
|
static_assertions::assert_eq_size!(PyAnyDict, PyObject);
|
||||||
|
static_assertions::assert_eq_align!(PyAnyDict, PyObject);
|
||||||
|
#[cfg(feature = "threading")]
|
||||||
|
static_assertions::assert_impl_all!(PyRef<PyAnyDict>: Send, Sync);
|
||||||
|
#[cfg(not(feature = "threading"))]
|
||||||
|
static_assertions::assert_not_impl_any!(PyRef<PyAnyDict>: Send, Sync);
|
||||||
|
|
||||||
|
let ctx = Context::genesis();
|
||||||
|
let dict: PyRef<PyAnyDict> = PyDict::default().into_ref(ctx).into();
|
||||||
|
let frozen: PyRef<PyAnyDict> = PyFrozenDict::from_dict(PyDict::default())
|
||||||
|
.into_ref(ctx)
|
||||||
|
.into();
|
||||||
|
for owner in [dict, frozen] {
|
||||||
|
let object = owner.as_object() as *const PyObject;
|
||||||
|
assert_eq!(owner.as_object().strong_count(), 1);
|
||||||
|
let borrowed = PyAnyDict::from_object(owner.as_object()).unwrap();
|
||||||
|
assert!(core::ptr::eq(borrowed.as_object(), object));
|
||||||
|
assert_eq!(owner.as_object().strong_count(), 1);
|
||||||
|
assert_eq!(borrowed.as_mutable().is_none(), borrowed.is_frozen());
|
||||||
|
let copy = borrowed.to_owned();
|
||||||
|
assert_eq!(owner.as_object().strong_count(), 2);
|
||||||
|
let clone = owner.clone();
|
||||||
|
assert_eq!(owner.as_object().strong_count(), 3);
|
||||||
|
let erased = clone.into_object();
|
||||||
|
assert_eq!(erased.strong_count(), 3);
|
||||||
|
assert!(core::ptr::eq(&*erased, object));
|
||||||
|
let mut visited = 0;
|
||||||
|
owner.traverse(&mut |edge| {
|
||||||
|
assert!(core::ptr::eq(edge, object));
|
||||||
|
visited += 1;
|
||||||
|
});
|
||||||
|
assert_eq!(visited, 1);
|
||||||
|
drop(copy);
|
||||||
|
drop(owner);
|
||||||
|
assert_eq!(erased.strong_count(), 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn anydict_checked_reference_conversion() {
|
||||||
|
crate::Interpreter::without_stdlib(Default::default()).enter(|vm| {
|
||||||
|
let dict = PyDict::default().into_pyobject(vm);
|
||||||
|
let frozen = PyFrozenDict::from_dict(PyDict::default()).into_pyobject(vm);
|
||||||
|
for object in [dict, frozen] {
|
||||||
|
let owner = PyRef::<PyAnyDict>::try_from_object(vm, object.clone()).unwrap();
|
||||||
|
assert_eq!(object.strong_count(), 2);
|
||||||
|
assert!(core::ptr::eq(owner.as_object(), &*object));
|
||||||
|
let erased: PyObjectRef = owner.into();
|
||||||
|
assert_eq!(object.strong_count(), 2);
|
||||||
|
assert!(core::ptr::eq(&*erased, &*object));
|
||||||
|
drop(erased);
|
||||||
|
assert_eq!(object.strong_count(), 1);
|
||||||
|
}
|
||||||
|
let unrelated: PyObjectRef = vm.ctx.new_int(1000).into();
|
||||||
|
assert!(PyAnyDict::from_object(&unrelated).is_none());
|
||||||
|
let error = PyRef::<PyAnyDict>::try_from_object(vm, unrelated.clone()).unwrap_err();
|
||||||
|
assert!(error.fast_isinstance(vm.ctx.exceptions.type_error));
|
||||||
|
assert_eq!(unrelated.strong_count(), 1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use super::{PyAnyDictRef, PyAsyncGen, PyCode, PyCoroutine, PyGenerator, PyIntRef};
|
use super::{PyAnyDict, PyAsyncGen, PyCode, PyCoroutine, PyGenerator, PyIntRef};
|
||||||
use crate::{
|
use crate::{
|
||||||
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
|
||||||
class::PyClassImpl,
|
class::PyClassImpl,
|
||||||
@@ -668,7 +668,7 @@ impl Py<FrameObject> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pygetset]
|
#[pygetset]
|
||||||
pub fn f_globals(&self) -> PyAnyDictRef {
|
pub fn f_globals(&self) -> PyRef<PyAnyDict> {
|
||||||
self.iframe().globals().to_owned()
|
self.iframe().globals().to_owned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
mod jit;
|
mod jit;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
PyAnyDictRef, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyList, PyModule, PyStr,
|
PyAnyDict, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyList, PyModule, PyStr,
|
||||||
PyStrRef, PyTuple, PyTupleRef, PyType, object,
|
PyStrRef, PyTuple, PyTupleRef, PyType, object,
|
||||||
};
|
};
|
||||||
use crate::common::hash::PyHash;
|
use crate::common::hash::PyHash;
|
||||||
@@ -66,7 +66,7 @@ fn format_missing_args(
|
|||||||
pub struct PyFunction {
|
pub struct PyFunction {
|
||||||
pub(crate) code: PyAtomicRef<PyCode>,
|
pub(crate) code: PyAtomicRef<PyCode>,
|
||||||
#[pymember(name = "__globals__")]
|
#[pymember(name = "__globals__")]
|
||||||
pub(crate) globals: PyAnyDictRef,
|
pub(crate) globals: PyRef<PyAnyDict>,
|
||||||
#[pymember(name = "__builtins__")]
|
#[pymember(name = "__builtins__")]
|
||||||
pub(crate) builtins: PyObjectRef,
|
pub(crate) builtins: PyObjectRef,
|
||||||
#[pymember(name = "__closure__")]
|
#[pymember(name = "__closure__")]
|
||||||
@@ -179,7 +179,7 @@ impl PyFunction {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
code: PyRef<PyCode>,
|
code: PyRef<PyCode>,
|
||||||
globals: PyAnyDictRef,
|
globals: PyRef<PyAnyDict>,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<Self> {
|
) -> PyResult<Self> {
|
||||||
let name = PyMutex::new(code.obj_name.to_owned());
|
let name = PyMutex::new(code.obj_name.to_owned());
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ impl Constructor for PyMappingProxy {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PyMappingProxy {
|
impl PyMappingProxy {
|
||||||
pub(crate) fn from_any_dict(dict: super::PyAnyDictRef) -> Self {
|
pub(crate) fn from_any_dict(dict: PyRef<super::PyAnyDict>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mapping: MappingProxyInner::Mapping(ArgMapping::new(dict.into())),
|
mapping: MappingProxyInner::Mapping(ArgMapping::new(dict.into())),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ pub use complex::PyComplex;
|
|||||||
pub(crate) mod coroutine;
|
pub(crate) mod coroutine;
|
||||||
pub use coroutine::PyCoroutine;
|
pub use coroutine::PyCoroutine;
|
||||||
pub(crate) mod dict;
|
pub(crate) mod dict;
|
||||||
pub use dict::{PyAnyDictRef, PyDict, PyDictRef, PyFrozenDict};
|
pub use dict::{PyAnyDict, PyDict, PyDictRef, PyFrozenDict};
|
||||||
pub(crate) mod enumerate;
|
pub(crate) mod enumerate;
|
||||||
pub use enumerate::PyEnumerate;
|
pub use enumerate::PyEnumerate;
|
||||||
pub(crate) mod filter;
|
pub(crate) mod filter;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
use super::{
|
use super::{
|
||||||
PyAnyDictRef, PyClassMethod, PyDict, PyDictRef, PyList, PyStaticMethod, PyStr, PyStrInterned,
|
PyAnyDict, PyClassMethod, PyDict, PyDictRef, PyList, PyStaticMethod, PyStr, PyStrInterned,
|
||||||
PyStrRef, PyTupleRef, PyUtf8StrRef, PyWeak, mappingproxy::PyMappingProxy, object, union_,
|
PyStrRef, PyTupleRef, PyUtf8StrRef, PyWeak, mappingproxy::PyMappingProxy, object, union_,
|
||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
@@ -1798,8 +1798,14 @@ impl Constructor for PyType {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
let (name, bases, dict, kwargs): (PyStrRef, PyTupleRef, PyAnyDictRef, KwArgs) =
|
let (name, bases, dict, kwargs): (PyStrRef, PyTupleRef, PyObjectRef, KwArgs) =
|
||||||
args.clone().bind_for(vm, Self::NAME)?;
|
args.clone().bind_for(vm, Self::NAME)?;
|
||||||
|
let Some(dict) = PyAnyDict::from_object(&dict) else {
|
||||||
|
return Err(vm.new_type_error(format!(
|
||||||
|
"type.__new__() argument 3 must be dict or frozendict, not {}",
|
||||||
|
dict.class().fully_qualified_name(vm)?
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
let (metatype, base, bases, base_is_type) = if bases.as_slice().is_empty() {
|
let (metatype, base, bases, base_is_type) = if bases.as_slice().is_empty() {
|
||||||
let base = vm.ctx.types.object_type.to_owned();
|
let base = vm.ctx.types.object_type.to_owned();
|
||||||
@@ -1849,7 +1855,7 @@ impl Constructor for PyType {
|
|||||||
let copied = vm.ctx.new_dict();
|
let copied = vm.ctx.new_dict();
|
||||||
// Like dict.copy(), an empty table bypasses subclass hooks.
|
// Like dict.copy(), an empty table bypasses subclass hooks.
|
||||||
if !dict.is_empty() {
|
if !dict.is_empty() {
|
||||||
copied.merge_object(dict.into(), vm)?;
|
copied.merge_object(dict.to_owned().into(), vm)?;
|
||||||
}
|
}
|
||||||
copied
|
copied
|
||||||
};
|
};
|
||||||
|
|||||||
+15
-15
@@ -6,10 +6,10 @@ use crate::{
|
|||||||
AsObject, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, PyStackRef,
|
AsObject, Py, PyExact, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, PyStackRef,
|
||||||
TryFromObject, VirtualMachine,
|
TryFromObject, VirtualMachine,
|
||||||
builtins::{
|
builtins::{
|
||||||
PyAnyDictRef, PyBaseException, PyBaseExceptionRef, PyBaseObject, PyBytes, PyCode,
|
PyAnyDict, PyBaseException, PyBaseExceptionRef, PyBaseObject, PyBytes, PyCode, PyCoroutine,
|
||||||
PyCoroutine, PyDict, PyDictRef, PyFloat, PyFrozenSet, PyGenerator, PyInt, PyInterpolation,
|
PyDict, PyDictRef, PyFloat, PyFrozenSet, PyGenerator, PyInt, PyInterpolation, PyList,
|
||||||
PyList, PyModule, PyProperty, PySet, PySlice, PyStr, PyStrInterned, PyTemplate,
|
PyModule, PyProperty, PySet, PySlice, PyStr, PyStrInterned, PyTemplate, PyTraceback,
|
||||||
PyTraceback, PyType, PyUtf8Str,
|
PyType, PyUtf8Str,
|
||||||
builtin_func::PyNativeFunction,
|
builtin_func::PyNativeFunction,
|
||||||
descriptor::{PyMemberDescriptor, PyMethodDescriptor},
|
descriptor::{PyMemberDescriptor, PyMethodDescriptor},
|
||||||
frame::stack_analysis,
|
frame::stack_analysis,
|
||||||
@@ -108,7 +108,7 @@ pub fn current_thread_frame_materialize(vm: &VirtualMachine) -> Option<FrameObje
|
|||||||
/// Read the globals dict from the topmost frame on this thread's chain.
|
/// Read the globals dict from the topmost frame on this thread's chain.
|
||||||
/// Returns `None` if the chain is empty.
|
/// Returns `None` if the chain is empty.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn current_globals() -> Option<PyAnyDictRef> {
|
pub fn current_globals() -> Option<PyRef<PyAnyDict>> {
|
||||||
let ptr = crate::vm::thread::get_current_frame();
|
let ptr = crate::vm::thread::get_current_frame();
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
return None;
|
return None;
|
||||||
@@ -923,7 +923,7 @@ pub struct InterpreterFrame {
|
|||||||
// Borrowed pointers — owned by FrameObject or by PyFunction on caller's stack.
|
// Borrowed pointers — owned by FrameObject or by PyFunction on caller's stack.
|
||||||
pub(crate) code: *const Py<PyCode>,
|
pub(crate) code: *const Py<PyCode>,
|
||||||
pub(crate) func_obj: *const PyObject, // nullable
|
pub(crate) func_obj: *const PyObject, // nullable
|
||||||
pub(crate) globals: *const PyAnyDictRef,
|
pub(crate) globals: *const PyAnyDict,
|
||||||
pub(crate) builtins: *const PyObject,
|
pub(crate) builtins: *const PyObject,
|
||||||
|
|
||||||
/// Unified storage for local variables and evaluation stack.
|
/// Unified storage for local variables and evaluation stack.
|
||||||
@@ -983,7 +983,7 @@ impl InterpreterFrame {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) unsafe fn new(
|
pub(crate) unsafe fn new(
|
||||||
code: &Py<PyCode>,
|
code: &Py<PyCode>,
|
||||||
globals: &PyAnyDictRef,
|
globals: &PyAnyDict,
|
||||||
builtins: &PyObject,
|
builtins: &PyObject,
|
||||||
func_obj: Option<&PyObject>,
|
func_obj: Option<&PyObject>,
|
||||||
localsplus: LocalsPlus,
|
localsplus: LocalsPlus,
|
||||||
@@ -1027,7 +1027,7 @@ impl InterpreterFrame {
|
|||||||
Some(obj) => obj as *const PyObject,
|
Some(obj) => obj as *const PyObject,
|
||||||
None => core::ptr::null(),
|
None => core::ptr::null(),
|
||||||
},
|
},
|
||||||
globals: globals as *const PyAnyDictRef,
|
globals: globals as *const PyAnyDict,
|
||||||
builtins: builtins as *const PyObject,
|
builtins: builtins as *const PyObject,
|
||||||
localsplus,
|
localsplus,
|
||||||
locals,
|
locals,
|
||||||
@@ -1060,7 +1060,7 @@ impl InterpreterFrame {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) unsafe fn new_on_datastack<'a>(
|
pub(crate) unsafe fn new_on_datastack<'a>(
|
||||||
code: &Py<PyCode>,
|
code: &Py<PyCode>,
|
||||||
globals: &PyAnyDictRef,
|
globals: &PyAnyDict,
|
||||||
builtins: &PyObject,
|
builtins: &PyObject,
|
||||||
func_obj: Option<&PyObject>,
|
func_obj: Option<&PyObject>,
|
||||||
locals: FrameLocals,
|
locals: FrameLocals,
|
||||||
@@ -1266,7 +1266,7 @@ impl InterpreterFrame {
|
|||||||
// Create a full FrameObject with its own InterpreterFrame copy.
|
// Create a full FrameObject with its own InterpreterFrame copy.
|
||||||
// The FrameObject owns references to the same objects (code, globals, etc).
|
// The FrameObject owns references to the same objects (code, globals, etc).
|
||||||
let code: PyRef<PyCode> = self.code().to_owned();
|
let code: PyRef<PyCode> = self.code().to_owned();
|
||||||
let globals = self.globals().clone();
|
let globals = self.globals().to_owned();
|
||||||
let builtins: PyObjectRef = self.builtins().to_owned();
|
let builtins: PyObjectRef = self.builtins().to_owned();
|
||||||
let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
|
let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
|
||||||
|
|
||||||
@@ -1362,7 +1362,7 @@ impl InterpreterFrame {
|
|||||||
#[cold]
|
#[cold]
|
||||||
fn materialize_slow_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
|
fn materialize_slow_chain(&self, vm: &VirtualMachine) -> FrameObjectRef {
|
||||||
let code: PyRef<PyCode> = self.code().to_owned();
|
let code: PyRef<PyCode> = self.code().to_owned();
|
||||||
let globals = self.globals().clone();
|
let globals = self.globals().to_owned();
|
||||||
let builtins: PyObjectRef = self.builtins().to_owned();
|
let builtins: PyObjectRef = self.builtins().to_owned();
|
||||||
let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
|
let func_obj: Option<PyObjectRef> = self.func_obj().map(|o| o.to_owned());
|
||||||
|
|
||||||
@@ -1419,7 +1419,7 @@ impl InterpreterFrame {
|
|||||||
|
|
||||||
/// Borrowed globals dict.
|
/// Borrowed globals dict.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn globals(&self) -> &PyAnyDictRef {
|
pub fn globals(&self) -> &PyAnyDict {
|
||||||
// SAFETY: established by `new` / `init_iframe_ptrs`.
|
// SAFETY: established by `new` / `init_iframe_ptrs`.
|
||||||
unsafe { &*self.globals }
|
unsafe { &*self.globals }
|
||||||
}
|
}
|
||||||
@@ -1534,7 +1534,7 @@ pub struct FrameObject {
|
|||||||
// raw pointers. Wrapped in Option so Traverse::clear can release them,
|
// raw pointers. Wrapped in Option so Traverse::clear can release them,
|
||||||
// allowing GC cycle collection to reclaim referenced objects.
|
// allowing GC cycle collection to reclaim referenced objects.
|
||||||
pub(crate) owned_code: Option<PyRef<PyCode>>,
|
pub(crate) owned_code: Option<PyRef<PyCode>>,
|
||||||
pub(crate) owned_globals: Option<PyAnyDictRef>,
|
pub(crate) owned_globals: Option<PyRef<PyAnyDict>>,
|
||||||
pub(crate) owned_builtins: Option<PyObjectRef>,
|
pub(crate) owned_builtins: Option<PyObjectRef>,
|
||||||
pub(crate) owned_func_obj: Option<PyObjectRef>,
|
pub(crate) owned_func_obj: Option<PyObjectRef>,
|
||||||
|
|
||||||
@@ -1853,7 +1853,7 @@ impl FrameObject {
|
|||||||
fn init_iframe_ptrs(self_: &Py<Self>) {
|
fn init_iframe_ptrs(self_: &Py<Self>) {
|
||||||
let iframe = unsafe { self_.iframe_mut() };
|
let iframe = unsafe { self_.iframe_mut() };
|
||||||
iframe.code = &**self_.owned_code.as_ref().unwrap() as *const Py<PyCode>;
|
iframe.code = &**self_.owned_code.as_ref().unwrap() as *const Py<PyCode>;
|
||||||
iframe.globals = self_.owned_globals.as_ref().unwrap() as *const PyAnyDictRef;
|
iframe.globals = &**self_.owned_globals.as_ref().unwrap() as *const PyAnyDict;
|
||||||
iframe.builtins = &**self_.owned_builtins.as_ref().unwrap() as *const PyObject;
|
iframe.builtins = &**self_.owned_builtins.as_ref().unwrap() as *const PyObject;
|
||||||
iframe.func_obj = match &self_.owned_func_obj {
|
iframe.func_obj = match &self_.owned_func_obj {
|
||||||
Some(obj) => &**obj as *const PyObject,
|
Some(obj) => &**obj as *const PyObject,
|
||||||
@@ -2931,7 +2931,7 @@ pub(crate) struct ExecutingFrame<'a> {
|
|||||||
code: &'a Py<PyCode>,
|
code: &'a Py<PyCode>,
|
||||||
localsplus: &'a mut LocalsPlus,
|
localsplus: &'a mut LocalsPlus,
|
||||||
locals: &'a FrameLocals,
|
locals: &'a FrameLocals,
|
||||||
globals: &'a PyAnyDictRef,
|
globals: &'a PyAnyDict,
|
||||||
builtins: &'a PyObject,
|
builtins: &'a PyObject,
|
||||||
/// Cached downcast of builtins to PyDict for fast LOAD_GLOBAL.
|
/// Cached downcast of builtins to PyDict for fast LOAD_GLOBAL.
|
||||||
/// Only set when both globals and builtins are exact dict types (not
|
/// Only set when both globals and builtins are exact dict types (not
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use super::IntoFuncArgs;
|
use super::IntoFuncArgs;
|
||||||
use crate::{
|
use crate::{
|
||||||
AsObject, PyObject, PyObjectRef, PyPayload, PyResult, TryFromObject, VirtualMachine,
|
AsObject, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||||
builtins::{PyAnyDictRef, PyDictRef, iter::PySequenceIterator},
|
builtins::{PyAnyDict, PyDictRef, iter::PySequenceIterator},
|
||||||
convert::ToPyObject,
|
convert::ToPyObject,
|
||||||
object::{Traverse, TraverseFn},
|
object::{Traverse, TraverseFn},
|
||||||
protocol::{PyIter, PyIterIter, PyMapping},
|
protocol::{PyIter, PyIterIter, PyMapping},
|
||||||
@@ -157,7 +157,7 @@ impl ArgMapping {
|
|||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn from_anydict_exact(dict: PyAnyDictRef) -> Self {
|
pub fn from_anydict_exact(dict: PyRef<PyAnyDict>) -> Self {
|
||||||
Self { obj: dict.into() }
|
Self { obj: dict.into() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2666,6 +2666,7 @@ mod ref_target_sealed {
|
|||||||
impl<T: super::PyPayload> Sealed for T {}
|
impl<T: super::PyPayload> Sealed for T {}
|
||||||
impl Sealed for super::PyObject {}
|
impl Sealed for super::PyObject {}
|
||||||
impl Sealed for crate::builtins::set::PyAnySet {}
|
impl Sealed for crate::builtins::set::PyAnySet {}
|
||||||
|
impl Sealed for crate::builtins::PyAnyDict {}
|
||||||
impl<T: super::PyTypedRefTarget> Sealed for super::PyTuple<super::PyRef<T>> {}
|
impl<T: super::PyTypedRefTarget> Sealed for super::PyTuple<super::PyRef<T>> {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2709,6 +2710,11 @@ pub trait PyTypedRefTarget: PyRefTarget {}
|
|||||||
|
|
||||||
impl<T: PyRefTarget<Target = Py<T>>> PyTypedRefTarget for T {}
|
impl<T: PyRefTarget<Target = Py<T>>> PyTypedRefTarget for T {}
|
||||||
impl PyTypedRefTarget for crate::builtins::set::PyAnySet {}
|
impl PyTypedRefTarget for crate::builtins::set::PyAnySet {}
|
||||||
|
impl PyTypedRefTarget for crate::builtins::PyAnyDict {}
|
||||||
|
|
||||||
|
impl PyRefTarget for crate::builtins::PyAnyDict {
|
||||||
|
type Target = Self;
|
||||||
|
}
|
||||||
|
|
||||||
impl PyRefTarget for crate::builtins::set::PyAnySet {
|
impl PyRefTarget for crate::builtins::set::PyAnySet {
|
||||||
type Target = Self;
|
type Target = Self;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
VirtualMachine,
|
PyRef, VirtualMachine,
|
||||||
builtins::{PyAnyDictRef, PyDictRef},
|
builtins::{PyAnyDict, PyDictRef},
|
||||||
function::ArgMapping,
|
function::ArgMapping,
|
||||||
};
|
};
|
||||||
use alloc::fmt;
|
use alloc::fmt;
|
||||||
@@ -8,7 +8,7 @@ use alloc::fmt;
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
pub locals: Option<ArgMapping>,
|
pub locals: Option<ArgMapping>,
|
||||||
pub globals: PyAnyDictRef,
|
pub globals: PyRef<PyAnyDict>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Scope {
|
impl fmt::Debug for Scope {
|
||||||
@@ -21,7 +21,7 @@ impl fmt::Debug for Scope {
|
|||||||
impl Scope {
|
impl Scope {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(locals: Option<ArgMapping>, globals: impl Into<PyAnyDictRef>) -> Self {
|
pub fn new(locals: Option<ArgMapping>, globals: impl Into<PyRef<PyAnyDict>>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
locals,
|
locals,
|
||||||
globals: globals.into(),
|
globals: globals.into(),
|
||||||
|
|||||||
@@ -185,8 +185,8 @@ mod _testinternalcapi {
|
|||||||
&mut counts,
|
&mut counts,
|
||||||
args.globalnames.into_option(),
|
args.globalnames.into_option(),
|
||||||
args.attrnames.into_option(),
|
args.attrnames.into_option(),
|
||||||
globalsns.as_ref(),
|
globalsns.as_deref(),
|
||||||
builtinsns.as_ref(),
|
builtinsns.as_deref(),
|
||||||
vm,
|
vm,
|
||||||
)?;
|
)?;
|
||||||
counts_to_dict(&counts, vm)
|
counts_to_dict(&counts, vm)
|
||||||
@@ -214,8 +214,8 @@ mod _testinternalcapi {
|
|||||||
&mut counts,
|
&mut counts,
|
||||||
args.globalnames.into_option(),
|
args.globalnames.into_option(),
|
||||||
None,
|
None,
|
||||||
globalsns.as_ref(),
|
globalsns.as_deref(),
|
||||||
builtinsns.as_ref(),
|
builtinsns.as_deref(),
|
||||||
vm,
|
vm,
|
||||||
)?;
|
)?;
|
||||||
if builtinsns.is_some() {
|
if builtinsns.is_some() {
|
||||||
@@ -1127,13 +1127,14 @@ fn expandtabs(input: &str, tab_size: usize) -> String {
|
|||||||
|
|
||||||
type CodeNamespaces = (
|
type CodeNamespaces = (
|
||||||
PyRef<PyCode>,
|
PyRef<PyCode>,
|
||||||
Option<crate::builtins::PyAnyDictRef>,
|
Option<PyRef<crate::builtins::PyAnyDict>>,
|
||||||
Option<crate::builtins::PyAnyDictRef>,
|
Option<PyRef<crate::builtins::PyAnyDict>>,
|
||||||
);
|
);
|
||||||
|
|
||||||
fn code_or_function(obj: &PyObject, vm: &VirtualMachine) -> PyResult<CodeNamespaces> {
|
fn code_or_function(obj: &PyObject, vm: &VirtualMachine) -> PyResult<CodeNamespaces> {
|
||||||
if let Ok(func) = obj.to_owned().downcast::<PyFunction>() {
|
if let Ok(func) = obj.to_owned().downcast::<PyFunction>() {
|
||||||
let builtins = crate::builtins::PyAnyDictRef::from_object(&func.builtins);
|
let builtins =
|
||||||
|
crate::builtins::PyAnyDict::from_object(&func.builtins).map(ToOwned::to_owned);
|
||||||
return Ok((
|
return Ok((
|
||||||
(*func.code).to_owned(),
|
(*func.code).to_owned(),
|
||||||
Some(func.globals.clone()),
|
Some(func.globals.clone()),
|
||||||
@@ -1287,8 +1288,8 @@ fn set_unbound_var_counts(
|
|||||||
counts: &mut VarCounts,
|
counts: &mut VarCounts,
|
||||||
globalnames: Option<PyObjectRef>,
|
globalnames: Option<PyObjectRef>,
|
||||||
attrnames: Option<PyObjectRef>,
|
attrnames: Option<PyObjectRef>,
|
||||||
globalsns: Option<&crate::builtins::PyAnyDictRef>,
|
globalsns: Option<&crate::builtins::PyAnyDict>,
|
||||||
builtinsns: Option<&crate::builtins::PyAnyDictRef>,
|
builtinsns: Option<&crate::builtins::PyAnyDict>,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<()> {
|
) -> PyResult<()> {
|
||||||
let globalnames = optional_set(globalnames, "globalnames", vm)?;
|
let globalnames = optional_set(globalnames, "globalnames", vm)?;
|
||||||
@@ -1324,8 +1325,8 @@ fn identify_unbound_names(
|
|||||||
code: &Py<PyCode>,
|
code: &Py<PyCode>,
|
||||||
globalnames: Option<PyRef<PySet>>,
|
globalnames: Option<PyRef<PySet>>,
|
||||||
attrnames: Option<PyRef<PySet>>,
|
attrnames: Option<PyRef<PySet>>,
|
||||||
globalsns: Option<&crate::builtins::PyAnyDictRef>,
|
globalsns: Option<&crate::builtins::PyAnyDict>,
|
||||||
builtinsns: Option<&crate::builtins::PyAnyDictRef>,
|
builtinsns: Option<&crate::builtins::PyAnyDict>,
|
||||||
vm: &VirtualMachine,
|
vm: &VirtualMachine,
|
||||||
) -> PyResult<(UnboundCounts, i32)> {
|
) -> PyResult<(UnboundCounts, i32)> {
|
||||||
let mut seen_globals = HashSet::new();
|
let mut seen_globals = HashSet::new();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ mod builtins {
|
|||||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
|
||||||
VirtualMachine,
|
VirtualMachine,
|
||||||
builtins::{
|
builtins::{
|
||||||
PyAnyDictRef, PyByteArray, PyBytes, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
|
PyAnyDict, PyByteArray, PyBytes, PyStr, PyStrRef, PyTuple, PyTupleRef, PyType,
|
||||||
PyUtf8StrRef,
|
PyUtf8StrRef,
|
||||||
enumerate::PyReverseSequenceIterator,
|
enumerate::PyReverseSequenceIterator,
|
||||||
function::{PyCell, PyCellRef, PyFunction},
|
function::{PyCell, PyCellRef, PyFunction},
|
||||||
@@ -462,7 +462,7 @@ mod builtins {
|
|||||||
Some(globals) => {
|
Some(globals) => {
|
||||||
validate_globals_dict(&globals, vm, func_name)?;
|
validate_globals_dict(&globals, vm, func_name)?;
|
||||||
|
|
||||||
let globals = PyAnyDictRef::try_from_object(vm, globals)?;
|
let globals = PyRef::<PyAnyDict>::try_from_object(vm, globals)?;
|
||||||
(
|
(
|
||||||
globals.clone(),
|
globals.clone(),
|
||||||
self.locals
|
self.locals
|
||||||
@@ -754,7 +754,7 @@ mod builtins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[pyfunction]
|
#[pyfunction]
|
||||||
fn globals(vm: &VirtualMachine) -> PyAnyDictRef {
|
fn globals(vm: &VirtualMachine) -> PyRef<PyAnyDict> {
|
||||||
vm.current_globals()
|
vm.current_globals()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ mod vm_ops;
|
|||||||
use crate::{
|
use crate::{
|
||||||
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult,
|
||||||
builtins::{
|
builtins::{
|
||||||
self, PyAnyDictRef, PyBaseExceptionRef, PyBaseObject, PyDict, PyFrozenSet, PyInt, PyList,
|
self, PyAnyDict, PyBaseExceptionRef, PyBaseObject, PyDict, PyFrozenSet, PyInt, PyList,
|
||||||
PyModule, PySet, PyStr, PyStrInterned, PyStrRef, PyTypeRef, PyUtf8Str, PyUtf8StrInterned,
|
PyModule, PySet, PyStr, PyStrInterned, PyStrRef, PyTypeRef, PyUtf8Str, PyUtf8StrInterned,
|
||||||
PyWeak,
|
PyWeak,
|
||||||
code::PyCode,
|
code::PyCode,
|
||||||
@@ -3113,7 +3113,7 @@ impl VirtualMachine {
|
|||||||
.locals(self)
|
.locals(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn current_globals(&self) -> PyAnyDictRef {
|
pub fn current_globals(&self) -> PyRef<PyAnyDict> {
|
||||||
let ptr = crate::vm::thread::get_current_frame();
|
let ptr = crate::vm::thread::get_current_frame();
|
||||||
if !ptr.is_null() {
|
if !ptr.is_null() {
|
||||||
return unsafe { (*ptr).globals().to_owned() };
|
return unsafe { (*ptr).globals().to_owned() };
|
||||||
|
|||||||
@@ -2125,3 +2125,106 @@ frozendict(**kwargs) -> new immutable dictionary initialized with the name=value
|
|||||||
frozen_result = unittest.TextTestRunner(verbosity=2).run(frozen_suite)
|
frozen_result = unittest.TextTestRunner(verbosity=2).run(frozen_suite)
|
||||||
assert frozen_result.wasSuccessful()
|
assert frozen_result.wasSuccessful()
|
||||||
assert not frozen_result.skipped
|
assert not frozen_result.skipped
|
||||||
|
|
||||||
|
|
||||||
|
# Final 3.15 rewrites exact hash TypeError without an explicit cause, and
|
||||||
|
# propagates formatting failures instead of replacing them with diagnostics.
|
||||||
|
def check_mapping_hash_error_context():
|
||||||
|
class SpecialTypeError(TypeError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
for mapping_type in (dict, frozendict):
|
||||||
|
for case in (
|
||||||
|
"plain",
|
||||||
|
"empty",
|
||||||
|
"multiple",
|
||||||
|
"bad_str",
|
||||||
|
"bad_repr",
|
||||||
|
"subclass",
|
||||||
|
"subclass_bad_str",
|
||||||
|
"list",
|
||||||
|
):
|
||||||
|
for handled in (False, True):
|
||||||
|
for operation in ("getitem", "get", "contains"):
|
||||||
|
callback_error = RuntimeError("format failed")
|
||||||
|
callbacks = []
|
||||||
|
|
||||||
|
class BadText:
|
||||||
|
def __str__(self):
|
||||||
|
callbacks.append("str")
|
||||||
|
raise callback_error
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
callbacks.append("repr")
|
||||||
|
raise callback_error
|
||||||
|
|
||||||
|
args = {
|
||||||
|
"plain": ("hash failed",),
|
||||||
|
"empty": (),
|
||||||
|
"multiple": ("one", 2),
|
||||||
|
"bad_str": (BadText(),),
|
||||||
|
"bad_repr": ("one", BadText()),
|
||||||
|
"subclass": ("hash failed",),
|
||||||
|
"subclass_bad_str": (BadText(),),
|
||||||
|
"list": (),
|
||||||
|
}[case]
|
||||||
|
error_type = (
|
||||||
|
SpecialTypeError if case.startswith("subclass") else TypeError
|
||||||
|
)
|
||||||
|
original = error_type(*args)
|
||||||
|
outer = ValueError("outer")
|
||||||
|
|
||||||
|
class Key:
|
||||||
|
def __hash__(self):
|
||||||
|
raise original
|
||||||
|
|
||||||
|
key = [] if case == "list" else Key()
|
||||||
|
mapping = mapping_type()
|
||||||
|
|
||||||
|
def invoke():
|
||||||
|
try:
|
||||||
|
if operation == "getitem":
|
||||||
|
mapping[key]
|
||||||
|
elif operation == "get":
|
||||||
|
mapping.get(key)
|
||||||
|
else:
|
||||||
|
key in mapping
|
||||||
|
except Exception as error:
|
||||||
|
assert error.__cause__ is None
|
||||||
|
assert error.__context__ is (outer if handled else None)
|
||||||
|
assert error.__suppress_context__ is False
|
||||||
|
if case.startswith("subclass"):
|
||||||
|
assert error is original
|
||||||
|
assert callbacks == []
|
||||||
|
elif case in ("bad_str", "bad_repr"):
|
||||||
|
assert error is callback_error
|
||||||
|
assert callbacks == [
|
||||||
|
"str" if case == "bad_str" else "repr"
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
assert type(error) is TypeError
|
||||||
|
assert error is not original
|
||||||
|
name = "list" if case == "list" else Key.__qualname__
|
||||||
|
message = {
|
||||||
|
"plain": "hash failed",
|
||||||
|
"empty": "",
|
||||||
|
"multiple": "('one', 2)",
|
||||||
|
"list": "unhashable type: 'list'",
|
||||||
|
}[case]
|
||||||
|
assert str(error) == (
|
||||||
|
f"cannot use '{name}' as a {mapping_type.__name__} key ({message})"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
raise AssertionError("hash failure was not propagated")
|
||||||
|
|
||||||
|
if handled:
|
||||||
|
try:
|
||||||
|
raise outer
|
||||||
|
except ValueError:
|
||||||
|
invoke()
|
||||||
|
else:
|
||||||
|
invoke()
|
||||||
|
|
||||||
|
|
||||||
|
if hasattr(builtins, "frozendict"):
|
||||||
|
check_mapping_hash_error_context()
|
||||||
+4
-4
@@ -1,14 +1,14 @@
|
|||||||
#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
|
#![cfg_attr(target_arch = "wasm32", allow(dead_code))]
|
||||||
use rustpython_vm::{
|
use rustpython_vm::{
|
||||||
AsObject, PyResult, TryFromObject, VirtualMachine,
|
AsObject, PyRef, PyResult, TryFromObject, VirtualMachine,
|
||||||
builtins::{PyAnyDictRef, PyStrRef},
|
builtins::{PyAnyDict, PyStrRef},
|
||||||
function::ArgIterable,
|
function::ArgIterable,
|
||||||
identifier,
|
identifier,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(super) struct ShellHelper<'vm> {
|
pub(super) struct ShellHelper<'vm> {
|
||||||
vm: &'vm VirtualMachine,
|
vm: &'vm VirtualMachine,
|
||||||
globals: PyAnyDictRef,
|
globals: PyRef<PyAnyDict>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn reverse_string(s: &mut String) {
|
fn reverse_string(s: &mut String) {
|
||||||
@@ -54,7 +54,7 @@ fn split_idents_on_dot(line: &str) -> Option<(usize, Vec<String>)> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'vm> ShellHelper<'vm> {
|
impl<'vm> ShellHelper<'vm> {
|
||||||
pub(super) const fn new(vm: &'vm VirtualMachine, globals: PyAnyDictRef) -> Self {
|
pub(super) const fn new(vm: &'vm VirtualMachine, globals: PyRef<PyAnyDict>) -> Self {
|
||||||
ShellHelper { vm, globals }
|
ShellHelper { vm, globals }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user