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
2 changes: 0 additions & 2 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4728,8 +4728,6 @@ class C:
b: int = field(kw_only=True)
self.assertEqual(C(42, b=10).__match_args__, ('a',))

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_KW_ONLY(self):
@dataclass
class A:
Expand Down
2 changes: 0 additions & 2 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -465,11 +465,9 @@ def __str__(self):
self.assertIn('astr', r)
self.assertIn("['sth']", r)

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_repr(self):
return super().test_repr()

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_recursive_repr(self):
return super().test_recursive_repr()

Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,6 @@ def generator():
with self.assertRaises(StopIteration):
gen.throw(E)

@unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: DeprecationWarning not triggered
def test_gen_3_arg_deprecation_warning(self):
def g():
yield 42
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_keywordonlyarg.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ def testSyntaxForManyArguments(self):
fundef = "def f(*, %s):\n pass\n" % ', '.join('i%d' % i for i in range(300))
compile(fundef, "<test>", "single")

@unittest.expectedFailure # TODO: RUSTPYTHON
def testTooManyPositionalErrorMessage(self):
def f(a, b=None, *, c=None):
pass
Expand Down
4 changes: 0 additions & 4 deletions Lib/test/test_positional_only_arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,6 @@ def f(a, b, /, c):
with self.assertRaisesRegex(TypeError, r"f\(\) takes 3 positional arguments but 4 were given"):
f(1, 2, 3, 4)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_positional_only_and_optional_arg_invalid_calls(self):
def f(a, b, /, c=3):
pass
Expand Down Expand Up @@ -198,8 +196,6 @@ def f(a, b, /):
with self.assertRaisesRegex(TypeError, r"f\(\) takes 2 positional arguments but 3 were given"):
f(1, 2, 3)

# TODO: RUSTPYTHON
@unittest.expectedFailure
def test_positional_only_with_optional_invalid_calls(self):
def f(a, b=2, /):
pass
Expand Down
1 change: 0 additions & 1 deletion Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8144,7 +8144,6 @@ class CNT(NamedTuple):
self.assertEqual(struct.__annotations__, {})
self.assertIsInstance(struct(), struct)

@unittest.expectedFailure # TODO: RUSTPYTHON
def test_namedtuple_errors(self):
with self.assertRaises(TypeError):
NamedTuple.__new__()
Expand Down
5 changes: 4 additions & 1 deletion crates/vm/src/builtins/asyncgenerator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
builtins::PyBaseExceptionRef,
class::PyClassImpl,
common::lock::PyMutex,
coroutine::Coro,
coroutine::{Coro, warn_deprecated_throw_signature},
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
Expand Down Expand Up @@ -312,6 +312,7 @@ impl PyAsyncGenASend {
return Err(vm.new_runtime_error("cannot reuse already awaited __anext__()/asend()"));
}

warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
let res = self.ag.inner.throw(
self.ag.as_object(),
exc_type,
Expand Down Expand Up @@ -431,6 +432,7 @@ impl PyAsyncGenAThrow {
exc_tb: OptionalArg,
vm: &VirtualMachine,
) -> PyResult {
warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
let ret = self.ag.inner.throw(
self.ag.as_object(),
exc_type,
Expand Down Expand Up @@ -601,6 +603,7 @@ impl PyAnextAwaitable {
vm: &VirtualMachine,
) -> PyResult {
self.check_closed(vm)?;
warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
self.state.store(AwaitableState::Iter);
let awaitable = self.get_awaitable_iter(vm)?;
let result = vm.call_method(
Expand Down
4 changes: 3 additions & 1 deletion crates/vm/src/builtins/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{PyCode, PyGenericAlias, PyStrRef, PyType, PyTypeRef};
use crate::{
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
class::PyClassImpl,
coroutine::Coro,
coroutine::{Coro, warn_deprecated_throw_signature},
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
Expand Down Expand Up @@ -108,6 +108,7 @@ impl Py<PyCoroutine> {
exc_tb: OptionalArg,
vm: &VirtualMachine,
) -> PyResult<PyIterReturn> {
warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
self.inner.throw(
self.as_object(),
exc_type,
Expand Down Expand Up @@ -181,6 +182,7 @@ impl PyCoroutineWrapper {
vm: &VirtualMachine,
) -> PyResult<PyIterReturn> {
self.check_closed(vm)?;
warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
let result = self.coro.throw(exc_type, exc_val, exc_tb, vm);
// Mark as closed if exhausted
if let Ok(PyIterReturn::StopIteration(_)) = &result {
Expand Down
20 changes: 17 additions & 3 deletions crates/vm/src/builtins/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,25 @@ impl PyFunction {
} else {
// Check the number of positional arguments
if nargs > n_expected_args {
let n_defaults = self
.defaults_and_kwdefaults
.lock()
.0
.as_ref()
.map_or(0, |d| d.len());
let n_required = n_expected_args - n_defaults;
let takes_msg = if n_defaults > 0 {
format!("from {} to {}", n_required, n_expected_args)
} else {
n_expected_args.to_string()
};
return Err(vm.new_type_error(format!(
"{}() takes {} positional arguments but {} were given",
"{}() takes {} positional argument{} but {} {} given",
self.__qualname__(),
n_expected_args,
nargs
takes_msg,
if n_expected_args == 1 { "" } else { "s" },
nargs,
if nargs == 1 { "was" } else { "were" }
)));
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/vm/src/builtins/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use super::{PyCode, PyGenericAlias, PyStrRef, PyType, PyTypeRef};
use crate::{
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
class::PyClassImpl,
coroutine::Coro,
coroutine::{Coro, warn_deprecated_throw_signature},
frame::FrameRef,
function::OptionalArg,
protocol::PyIterReturn,
Expand Down Expand Up @@ -99,6 +99,7 @@ impl Py<PyGenerator> {
exc_tb: OptionalArg,
vm: &VirtualMachine,
) -> PyResult<PyIterReturn> {
warn_deprecated_throw_signature(&exc_val, &exc_tb, vm)?;
self.inner.throw(
self.as_object(),
exc_type,
Expand Down
22 changes: 22 additions & 0 deletions crates/vm/src/coroutine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::{
common::lock::PyMutex,
exceptions::types::PyBaseException,
frame::{ExecutionResult, FrameRef},
function::OptionalArg,
protocol::PyIterReturn,
};
use crossbeam_utils::atomic::AtomicCell;
Expand Down Expand Up @@ -211,3 +212,24 @@ impl Coro {
pub fn is_gen_exit(exc: &Py<PyBaseException>, vm: &VirtualMachine) -> bool {
exc.fast_isinstance(vm.ctx.exceptions.generator_exit)
}

/// Emit DeprecationWarning for the deprecated 3-argument throw() signature.
pub fn warn_deprecated_throw_signature(
exc_val: &OptionalArg,
exc_tb: &OptionalArg,
vm: &VirtualMachine,
) -> PyResult<()> {
if exc_val.is_present() || exc_tb.is_present() {
crate::warn::warn(
vm.ctx.new_str(
"the (type, val, tb) signature of throw() is deprecated, \
use throw(val) instead",
),
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
1,
None,
vm,
)?;
}
Ok(())
}
32 changes: 13 additions & 19 deletions crates/vm/src/stdlib/functools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ mod _functools {
}

#[pyattr]
#[pyclass(name = "partial", module = "_functools")]
#[pyclass(name = "partial", module = "functools")]
#[derive(Debug, PyPayload)]
pub struct PyPartial {
inner: PyRwLock<PyPartialInner>,
Expand Down Expand Up @@ -319,28 +319,22 @@ mod _functools {
));
}

let class_name = zelf.class().name();
let qualname = zelf.class().__qualname__(vm);
let qualname_str = qualname
.downcast::<crate::builtins::PyStr>()
.map(|s| s.as_str().to_owned())
.unwrap_or_else(|_| zelf.class().name().to_owned());
let module = zelf.class().__module__(vm);

let qualified_name = if zelf.class().is(Self::class(&vm.ctx)) {
// For the base partial class, always use functools.partial
"functools.partial".to_owned()
} else {
// For subclasses, check if they're defined in __main__ or test modules
match module.downcast::<crate::builtins::PyStr>() {
Ok(module_str) => {
let module_name = module_str.as_str();
match module_name {
"builtins" | "" | "__main__" => class_name.to_owned(),
name if name.starts_with("test.") || name == "test" => {
// For test modules, just use the class name without module prefix
class_name.to_owned()
}
_ => format!("{module_name}.{class_name}"),
}
let qualified_name = match module.downcast::<crate::builtins::PyStr>() {
Ok(module_str) => {
let module_name = module_str.as_str();
match module_name {
"builtins" | "" => qualname_str,
_ => format!("{module_name}.{qualname_str}"),
}
Err(_) => class_name.to_owned(),
}
Err(_) => qualname_str,
};

Ok(format!(
Expand Down
Loading