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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions crates/capi/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use rustpython_vm::builtins::{PyStr, object_generic_set_dict, object_get_dict};
use rustpython_vm::bytecode::ComparisonOperator;
use rustpython_vm::function::PySetterValue;
use rustpython_vm::types::{PyComparisonOp, hash_not_implemented};
use rustpython_vm::{AsObject, PyPayload, PyResult, VirtualMachine};
use rustpython_vm::{AsObject, PyResult, VirtualMachine};

mod pytype;

Expand Down Expand Up @@ -355,9 +355,8 @@ pub unsafe extern "C" fn PyObject_ClearWeakRefs(obj: *mut PyObject) {
#[unsafe(no_mangle)]
pub unsafe extern "C" fn PyObject_Dir(obj: *mut PyObject) -> *mut PyObject {
with_vm(|vm| {
unsafe { obj.assume_borrowed_or_opt() }
.map_or_else(|| vm.dir(None), |obj| obj.to_owned().dir(vm))
.map(|list| list.into_ref(&vm.ctx))
let obj = unsafe { obj.assume_borrowed_or_opt() }.map(ToOwned::to_owned);
vm.dir(obj)
})
}

Expand Down
41 changes: 35 additions & 6 deletions crates/vm/src/builtins/dict.rs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ impl PyAnyDict {
return Ok(self.as_dict().copy());
}
let copied = PyDict::default();
copied.merge_object(self.as_object().to_owned(), vm)?;
copied.merge_mapping(self.as_object().to_owned(), vm)?;
Ok(copied)
}

Expand Down Expand Up @@ -409,6 +409,22 @@ impl PyDict {
override_existing: bool,
mapping_name: &str,
vm: &VirtualMachine,
) -> PyResult<()> {
self.merge_arg(other, override_existing, mapping_name, true, vm)
}

// PyDict_Update accepts mappings, without dict.update's sequence fallback.
pub(crate) fn merge_mapping(&self, other: PyObjectRef, vm: &VirtualMachine) -> PyResult<()> {
self.merge_arg(other, true, "dict", false, vm)
}

fn merge_arg(
&self,
other: PyObjectRef,
override_existing: bool,
mapping_name: &str,
allow_sequence: bool,
vm: &VirtualMachine,
) -> PyResult<()> {
if let Some(other_dict) = PyAnyDict::from_object(&other)
&& other_dict.uses_builtin_iter(vm)
Expand All @@ -419,14 +435,16 @@ impl PyDict {
// Use get_attr to properly invoke __getattribute__ for proxy objects
let keys_method = match other.get_attr(vm.ctx.intern_str("keys"), vm) {
Ok(keys_method) => keys_method,
Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => {
Err(e) if allow_sequence && e.fast_isinstance(vm.ctx.exceptions.attribute_error) => {
return self.merge_seq2(&other, override_existing, mapping_name, vm);
}
Err(e) => return Err(e),
};
let keys_result = keys_method.call((), vm);
drop(keys_method);
let keys = other.mapping_unchecked().collect_method_output(
vm.ctx.intern_str("keys"),
keys_method.call((), vm)?,
keys_result?,
vm,
)?;
let keys = PyIter::try_from_object(vm, keys)?;
Expand All @@ -437,9 +455,20 @@ impl PyDict {
continue;
}
}
let value = other.get_item(&*key, vm)?;
let hash = key_hash(&*key, mapping_name, vm)?;
dict.insert(vm, &*key, hash, value)?;
let value = match other.get_item(&*key, vm) {
Ok(value) => value,
Err(exc) => {
drop(keys);
drop(key);
return Err(exc);
}
};
let outcome = key_hash(&*key, mapping_name, vm)
.and_then(|hash| dict.insert(vm, &*key, hash, value.clone()));
// Keep both owners until insertion returns, including on failure.
drop(key);
drop(value);
outcome?;
}
Ok(())
}
Expand Down
53 changes: 8 additions & 45 deletions crates/vm/src/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
mod jit;

use super::{
PyAnyDict, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyList, PyModule, PyStr,
PyStrRef, PyTuple, PyTupleRef, PyType, object,
PyAnyDict, PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyModule, PyStr, PyStrRef,
PyTuple, PyTupleRef, PyType, object,
};
use crate::common::hash::PyHash;
use crate::common::lock::PyMutex;
Expand Down Expand Up @@ -1373,10 +1373,10 @@ impl Constructor for PyFunction {
#[pyclass(module = false, name = "method", traverse)]
#[derive(Debug)]
pub struct PyBoundMethod {
#[pymember(name = "__self__")]
object: PyObjectRef,
#[pymember(name = "__func__")]
function: PyObjectRef,
#[pymember(name = "__self__")]
object: PyObjectRef,
}

impl Callable for PyBoundMethod {
Expand All @@ -1393,12 +1393,13 @@ impl Comparable for PyBoundMethod {
zelf: &Py<Self>,
other: &PyObject,
op: PyComparisonOp,
_vm: &VirtualMachine,
vm: &VirtualMachine,
) -> PyResult<PyComparisonValue> {
op.eq_only(|| {
let other = class_or_notimplemented!(Self, other);
Ok(PyComparisonValue::Implemented(
zelf.function.is(&other.function) && zelf.object.is(&other.object),
vm.identical_or_equal(&zelf.function, &other.function)?
&& zelf.object.is(&other.object),
))
})
}
Expand Down Expand Up @@ -1465,7 +1466,7 @@ impl Constructor for PyBoundMethod {
impl PyBoundMethod {
#[must_use]
pub const fn new(object: PyObjectRef, function: PyObjectRef) -> Self {
Self { object, function }
Self { function, object }
}

#[inline]
Expand Down Expand Up @@ -1531,44 +1532,6 @@ impl Py<PyBoundMethod> {
fn __doc__(&self, vm: &VirtualMachine) -> PyResult {
self.function.get_attr("__doc__", vm)
}

#[pygetset]
fn __module__(&self, vm: &VirtualMachine) -> Option<PyObjectRef> {
self.function.get_attr("__module__", vm).ok()
}

#[pymethod]
fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyList> {
let func_dir = vm.dir(Some(self.function.clone()))?;

let bound_only = [
"__self__",
"__func__",
"__doc__",
"__module__",
"__call__",
"__get__",
"__repr__",
];

let mut seen = std::collections::HashSet::new();
let mut result: Vec<PyObjectRef> = Vec::new();

for item in func_dir.borrow_vec().iter() {
if let Ok(s) = item.clone().downcast::<PyStr>() {
seen.insert(s.as_wtf8().to_string());
}
result.push(item.clone());
}

for name in bound_only {
if seen.insert(name.to_owned()) {
result.push(vm.ctx.new_str(name).into());
}
}

Ok(PyList::from(result))
}
}

impl PyPayload for PyBoundMethod {
Expand Down
4 changes: 2 additions & 2 deletions crates/vm/src/builtins/genericalias.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use super::type_;
use crate::{
AsObject, Context, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
VirtualMachine, atomic_func,
builtins::{PyList, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef},
builtins::{PyList, PyListRef, PyStr, PyTuple, PyTupleRef, PyType, PyTypeRef},
class::{PyClassDef, PyClassImpl},
common::hash,
convert::ToPyObject,
Expand Down Expand Up @@ -254,7 +254,7 @@ impl Py<PyGenericAlias> {
}

#[pymethod]
fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyList> {
fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyListRef> {
let dir = vm.dir(Some(self.origin.clone()))?;
for exc in &ATTR_EXCEPTIONS {
let exc_obj = (*exc).to_pyobject(vm);
Expand Down
64 changes: 34 additions & 30 deletions crates/vm/src/builtins/object.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use super::{PyDict, PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef};
use super::{
PyAnyDict, PyDict, PyDictRef, PyList, PyStr, PyStrRef, PyType, PyTypeRef, PyUtf8StrRef,
};
use crate::common::hash::PyHash;
use crate::types::PyTypeFlags;
use crate::{
Expand Down Expand Up @@ -390,36 +392,18 @@ impl PyBaseObject {

#[pymethod]
pub fn __dir__(zelf: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyList> {
let mut names: Vec<PyObjectRef> = Vec::new();

match zelf.get_attr(identifier!(vm, __dict__), vm) {
Ok(obj) => {
if let Ok(dict) = obj.downcast::<PyDict>() {
names.extend(
dict.into_iter()
.filter_map(|(k, _)| k.downcast_ref::<PyStr>().is_some().then_some(k)),
);
}
}
Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => {}
Err(e) => return Err(e),
}

match zelf.get_attr(identifier!(vm, __class__), vm) {
Ok(cls_obj) => {
if let Some(cls) = cls_obj.downcast_ref::<PyType>() {
for (name, _) in cls.get_attributes(&vm.ctx) {
names.push(name.to_object());
}
}
}
Err(e) if e.fast_isinstance(vm.ctx.exceptions.attribute_error) => {}
Err(e) => return Err(e),
let dict = match vm.get_attribute_opt(&zelf, identifier!(vm, __dict__))? {
Some(obj) => match obj.downcast::<PyDict>() {
Ok(dict) => PyRef::<PyAnyDict>::from(dict).copy_as_dict(vm)?,
Err(_) => PyDict::default(),
},
None => PyDict::default(),
};
let class = vm.get_attribute_opt(&zelf, identifier!(vm, __class__))?;
if let Some(class) = &class {
merge_class_dict(&dict, class, vm)?;
}

let lst = PyList::from(names);
lst.sort(Default::default(), vm)?;
Ok(lst)
Ok(PyList::from(dict.keys_vec()))
}

#[pymethod]
Expand Down Expand Up @@ -534,6 +518,26 @@ impl PyBaseObject {
}
}

pub(crate) fn merge_class_dict(
dict: &PyDict,
class: &PyObject,
vm: &VirtualMachine,
) -> PyResult<()> {
vm.with_recursion(" while collecting directory entries", || {
if let Some(class_dict) = vm.get_attribute_opt(class, identifier!(vm, __dict__))? {
dict.merge_mapping(class_dict, vm)?;
}
if let Some(bases) = vm.get_attribute_opt(class, identifier!(vm, __bases__))? {
let sequence = bases.sequence_unchecked();
for index in 0..sequence.length(vm)? {
let base = sequence.get_item(index as isize, vm)?;
merge_class_dict(dict, &base, vm)?;
}
}
Ok(())
})
}

pub fn object_get_dict(obj: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyDictRef> {
if let Some(dict) = obj.dict() {
Ok(dict)
Expand Down
11 changes: 4 additions & 7 deletions crates/vm/src/builtins/type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2701,13 +2701,10 @@ impl Py<PyType> {
}

#[pymethod]
fn __dir__(&self, vm: &VirtualMachine) -> PyList {
let attributes: Vec<PyObjectRef> = self
.get_attributes(&vm.ctx)
.into_iter()
.map(|(k, _)| k.to_object())
.collect();
PyList::from(attributes)
fn __dir__(&self, vm: &VirtualMachine) -> PyResult<PyList> {
let dict = PyDict::default();
object::merge_class_dict(&dict, self.as_object(), vm)?;
Ok(PyList::from(dict.keys_vec()))
}

#[pymethod]
Expand Down
21 changes: 3 additions & 18 deletions crates/vm/src/protocol/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
use crate::{
AsObject, Py, PyObject, PyObjectRef, PyRef, PyResult, TryFromObject, VirtualMachine,
builtins::{
PyBaseObject, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyList, PyStr, PyTuple,
PyBaseObject, PyBytes, PyDict, PyDictRef, PyGenericAlias, PyInt, PyListRef, PyStr, PyTuple,
PyTupleRef, PyType, PyTypeRef, PyUtf8Str, int::check_int_to_str_digits, pystr::AsPyStr,
},
common::{hash::PyHash, str::to_ascii},
convert::ToPyObject,
dict_inner::DictKey,
function::{Either, FuncArgs, PyArithmeticValue, PySetterValue},
object::PyPayload,
protocol::PyIter,
types::{Constructor, PyComparisonOp},
};
Expand Down Expand Up @@ -70,22 +69,8 @@ impl PyObjectRef {
}

// PyObject *PyObject_Dir(PyObject *o)
pub fn dir(self, vm: &VirtualMachine) -> PyResult<PyList> {
let attributes = self.class().get_attributes(&vm.ctx);

let dict = PyDict::from_attributes(attributes, vm)?.into_ref(&vm.ctx);

if let Some(object_dict) = self.dict() {
vm.call_method(
dict.as_object(),
identifier!(vm, update).as_str(),
(object_dict,),
)?;
}

let attributes = dict.into_iter().map(|(k, _v)| k).collect::<Vec<_>>();

Ok(PyList::from(attributes))
pub fn dir(self, vm: &VirtualMachine) -> PyResult<PyListRef> {
vm.dir(Some(self))
}
}

Expand Down
8 changes: 4 additions & 4 deletions crates/vm/src/stdlib/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ mod builtins {
enumerate::PyReverseSequenceIterator,
function::{PyCell, PyCellRef, PyFunction},
iter::PyCallableIterator,
list::{PyList, SortOptions},
list::{PyList, PyListRef, SortOptions},
try_bigint_to_f64,
},
bytecode,
Expand Down Expand Up @@ -410,7 +410,7 @@ mod builtins {
}

#[pyfunction]
fn dir(obj: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyList> {
fn dir(obj: OptionalArg<PyObjectRef>, vm: &VirtualMachine) -> PyResult<PyListRef> {
vm.dir(obj.into_option())
}

Expand Down Expand Up @@ -1496,8 +1496,8 @@ mod builtins {
#[pyfunction]
fn vars(obj: OptionalArg, vm: &VirtualMachine) -> PyResult {
if let OptionalArg::Present(obj) = obj {
obj.get_attr(identifier!(vm, __dict__), vm)
.map_err(|_| vm.new_type_error("vars() argument must have __dict__ attribute"))
vm.get_attribute_opt(&obj, identifier!(vm, __dict__))?
.ok_or_else(|| vm.new_type_error("vars() argument must have __dict__ attribute"))
} else {
Ok(vm.current_locals()?.into())
}
Expand Down
Loading
Loading