mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
Fix function attribute tests: __defaults__ del, __code__ free vars ch… (#7749)
* Fix function attribute tests: __defaults__ del, __code__ free vars check, bound method dir() - Support `del func.__defaults__` and `del func.__kwdefaults__` by changing setter signatures to `PySetterValue<Option<...>>` so Delete is treated as setting the value to None (matching CPython behaviour) - Validate free-variable count when assigning to `func.__code__`: raise ValueError when the new code object's freevars count doesn't match the existing closure size - Add `__dir__` to `PyBoundMethod` that delegates to the underlying function's dir(), so attributes set on the function show up in `dir(bound_method)` - Remove `@unittest.expectedFailure` from the four tests that now pass: test_blank_func_defaults, test_func_default_args, test___code__, test_dir_includes_correct_attrs * Remove expectedFailure markers for now-passing tests
This commit is contained in:
4
Lib/test/test_funcattrs.py
vendored
4
Lib/test/test_funcattrs.py
vendored
@@ -50,7 +50,6 @@ class FunctionPropertiesTest(FuncAttrsTest):
|
||||
def test_module(self):
|
||||
self.assertEqual(self.b.__module__, __name__)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_dir_includes_correct_attrs(self):
|
||||
self.b.known_attr = 7
|
||||
self.assertIn('known_attr', dir(self.b),
|
||||
@@ -238,7 +237,6 @@ class FunctionPropertiesTest(FuncAttrsTest):
|
||||
func.__type_params__ = (T,)
|
||||
self.assertEqual(func.__type_params__, (T,))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test___code__(self):
|
||||
num_one, num_two = 7, 8
|
||||
def a(): pass
|
||||
@@ -269,13 +267,11 @@ class FunctionPropertiesTest(FuncAttrsTest):
|
||||
self.fail("__code__ with different numbers of free vars should "
|
||||
"not be possible")
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_blank_func_defaults(self):
|
||||
self.assertEqual(self.b.__defaults__, None)
|
||||
del self.b.__defaults__
|
||||
self.assertEqual(self.b.__defaults__, None)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_func_default_args(self):
|
||||
def first_func(a, b):
|
||||
return a+b
|
||||
|
||||
1
Lib/test/test_unittest/testmock/testpatch.py
vendored
1
Lib/test/test_unittest/testmock/testpatch.py
vendored
@@ -2012,7 +2012,6 @@ class PatchTest(unittest.TestCase):
|
||||
self.assertEqual(dic2, origdic2)
|
||||
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON
|
||||
def test_special_attrs(self):
|
||||
def foo(x=0):
|
||||
"""TEST"""
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
mod jit;
|
||||
|
||||
use super::{
|
||||
PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyModule, PyStr, PyStrRef, PyTuple,
|
||||
PyTupleRef, PyType, object,
|
||||
PyAsyncGen, PyCode, PyCoroutine, PyDictRef, PyGenerator, PyList, PyModule, PyStr, PyStrRef,
|
||||
PyTuple, PyTupleRef, PyType, object,
|
||||
};
|
||||
use crate::common::hash::PyHash;
|
||||
use crate::common::lock::PyMutex;
|
||||
@@ -788,7 +788,17 @@ impl PyFunction {
|
||||
}
|
||||
|
||||
#[pygetset(setter)]
|
||||
fn set___code__(&self, code: PyRef<PyCode>, vm: &VirtualMachine) {
|
||||
fn set___code__(&self, code: PyRef<PyCode>, vm: &VirtualMachine) -> PyResult<()> {
|
||||
let n_free = code.freevars.len();
|
||||
let n_closure = self.closure.as_ref().map_or(0, |c| c.len());
|
||||
if n_closure != n_free {
|
||||
return Err(vm.new_value_error(format!(
|
||||
"{}() requires a code object with {} free vars, not {}",
|
||||
self.qualname.lock(),
|
||||
n_closure,
|
||||
n_free,
|
||||
)));
|
||||
}
|
||||
#[cfg(feature = "jit")]
|
||||
let mut jit_guard = self.jitted_code.lock();
|
||||
self.code.swap_to_temporary_refs(code, vm);
|
||||
@@ -797,6 +807,7 @@ impl PyFunction {
|
||||
*jit_guard = None;
|
||||
}
|
||||
self.func_version.store(0, Relaxed);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
@@ -804,8 +815,11 @@ impl PyFunction {
|
||||
self.defaults_and_kwdefaults.lock().0.clone()
|
||||
}
|
||||
#[pygetset(setter)]
|
||||
fn set___defaults__(&self, defaults: Option<PyTupleRef>) {
|
||||
self.defaults_and_kwdefaults.lock().0 = defaults;
|
||||
fn set___defaults__(&self, defaults: PySetterValue<Option<PyTupleRef>>) {
|
||||
self.defaults_and_kwdefaults.lock().0 = match defaults {
|
||||
PySetterValue::Assign(d) => d,
|
||||
PySetterValue::Delete => None,
|
||||
};
|
||||
self.func_version.store(0, Relaxed);
|
||||
}
|
||||
|
||||
@@ -814,8 +828,11 @@ impl PyFunction {
|
||||
self.defaults_and_kwdefaults.lock().1.clone()
|
||||
}
|
||||
#[pygetset(setter)]
|
||||
fn set___kwdefaults__(&self, kwdefaults: Option<PyDictRef>) {
|
||||
self.defaults_and_kwdefaults.lock().1 = kwdefaults;
|
||||
fn set___kwdefaults__(&self, kwdefaults: PySetterValue<Option<PyDictRef>>) {
|
||||
self.defaults_and_kwdefaults.lock().1 = match kwdefaults {
|
||||
PySetterValue::Assign(d) => d,
|
||||
PySetterValue::Delete => None,
|
||||
};
|
||||
self.func_version.store(0, Relaxed);
|
||||
}
|
||||
|
||||
@@ -1308,6 +1325,39 @@ impl PyBoundMethod {
|
||||
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 {
|
||||
|
||||
Reference in New Issue
Block a user