diff --git a/.cspell.dict/python-more.txt b/.cspell.dict/python-more.txt
index 2dd31f8f579..2ce5d246d72 100644
--- a/.cspell.dict/python-more.txt
+++ b/.cspell.dict/python-more.txt
@@ -1,8 +1,10 @@
abiflags
abstractmethods
+addcompare
aenter
aexit
aiter
+altzone
anext
anextawaitable
annotationlib
@@ -24,6 +26,7 @@ breakpointhook
cformat
chunksize
classcell
+classmethods
closefd
closesocket
codepoint
@@ -32,6 +35,8 @@ codesize
contextvar
cpython
cratio
+ctype
+ctypes
dealloc
debugbuild
decompressor
@@ -74,6 +79,8 @@ fstring
fstrings
ftruncate
genexpr
+genexpressions
+getargs
getattro
getcodesize
getdefaultencoding
@@ -83,14 +90,17 @@ getformat
getframe
getframemodulename
getnewargs
+getopt
getpip
getrandom
getrecursionlimit
getrefcount
getsizeof
getswitchinterval
+getweakref
getweakrefcount
getweakrefs
+getweakrefs
getwindowsversion
gmtoff
groupdict
@@ -103,8 +113,12 @@ idxs
impls
indexgroup
infj
+inittab
+Inittab
instancecheck
instanceof
+interpchannels
+interpqueues
irepeat
isabstractmethod
isbytes
@@ -129,6 +143,7 @@ listcomp
longrange
lvalue
mappingproxy
+markupbase
maskpri
maxdigits
MAXGROUPS
@@ -144,6 +159,7 @@ mformat
mro
mros
multiarch
+mymodule
namereplace
nanj
nbytes
@@ -156,6 +172,7 @@ nlocals
NOARGS
nonbytes
Nonprintable
+onceregistry
origname
ospath
pendingcr
@@ -170,7 +187,10 @@ profilefunc
pycache
pycodecs
pycs
+pydatetime
pyexpat
+pyio
+pymain
PYTHONAPI
PYTHONBREAKPOINT
PYTHONDEBUG
@@ -220,10 +240,13 @@ scproxy
seennl
setattro
setcomp
+setprofileallthreads
setrecursionlimit
setswitchinterval
+settraceallthreads
showwarnmsg
signum
+sitebuiltins
slotnames
STACKLESS
stacklevel
@@ -232,14 +255,17 @@ startpos
subclassable
subclasscheck
subclasshook
+subclassing
suboffset
suboffsets
SUBPATTERN
+subpatterns
sumprod
surrogateescape
surrogatepass
sysconf
sysconfigdata
+sysdict
sysvars
teedata
thisclass
@@ -266,35 +292,10 @@ warnopts
weaklist
weakproxy
weakrefs
+weakrefset
winver
withdata
xmlcharrefreplace
xoptions
xopts
yieldfrom
-addcompare
-altzone
-classmethods
-ctype
-ctypes
-genexpressions
-getargs
-getopt
-getweakref
-getweakrefs
-inittab
-Inittab
-interpchannels
-interpqueues
-markupbase
-mymodule
-pydatetime
-pyio
-pymain
-setprofileallthreads
-settraceallthreads
-sitebuiltins
-subclassing
-subpatterns
-sysdict
-weakrefset
diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py
index 56ed457882c..01951e6247b 100644
--- a/Lib/test/test_site.py
+++ b/Lib/test/test_site.py
@@ -591,7 +591,6 @@ def test_lazy_imports(self):
class StartupImportTests(unittest.TestCase):
- @unittest.expectedFailure # TODO: RUSTPYTHON
@support.requires_subprocess()
def test_startup_imports(self):
# Get sys.path in isolated mode (python3 -I)
diff --git a/Lib/test/test_warnings/__init__.py b/Lib/test/test_warnings/__init__.py
index 83a84f6871a..16703835806 100644
--- a/Lib/test/test_warnings/__init__.py
+++ b/Lib/test/test_warnings/__init__.py
@@ -807,7 +807,6 @@ class CWarnTests(WarnTests, unittest.TestCase):
# As an early adopter, we sanity check the
# test.import_helper.import_fresh_module utility function
- @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: 'function' object has unexpected attribute '__code__'
def test_accelerated(self):
self.assertIsNot(original_warnings, self.module)
self.assertNotHasAttr(self.module.warn, '__code__')
@@ -1012,7 +1011,6 @@ def test_showwarning_missing(self):
result = stream.getvalue()
self.assertIn(text, result)
- @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: module 'warnings' has no attribute '_showwarnmsg'. Did you mean: 'showwarning'?
def test_showwarnmsg_missing(self):
# Test that _showwarnmsg() missing is okay.
text = 'del _showwarnmsg test'
@@ -1458,7 +1456,6 @@ class PyCatchWarningTests(CatchWarningTests, unittest.TestCase):
class EnvironmentVariableTests(BaseTest):
- @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'[]' != b"['ignore::DeprecationWarning']"
def test_single_warning(self):
rc, stdout, stderr = assert_python_ok("-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
@@ -1466,7 +1463,6 @@ def test_single_warning(self):
PYTHONDEVMODE="")
self.assertEqual(stdout, b"['ignore::DeprecationWarning']")
- @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'[]' != b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']"
def test_comma_separated_warnings(self):
rc, stdout, stderr = assert_python_ok("-c",
"import sys; sys.stdout.write(str(sys.warnoptions))",
@@ -1475,7 +1471,6 @@ def test_comma_separated_warnings(self):
self.assertEqual(stdout,
b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']")
- @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b"['ignore::UnicodeWarning']" != b"['ignore::DeprecationWarning', 'ignore::UnicodeWarning']"
@force_not_colorized
def test_envvar_and_command_line(self):
rc, stdout, stderr = assert_python_ok("-Wignore::UnicodeWarning", "-c",
@@ -1535,7 +1530,6 @@ def test_default_filter_configuration(self):
self.assertEqual(stdout_lines, expected_output)
- @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: b'[]' != b"['ignore:DeprecationWarning\xc3\xa6']"
@unittest.skipUnless(sys.getfilesystemencoding() != 'ascii',
'requires non-ascii filesystemencoding')
def test_nonascii(self):
@@ -1550,10 +1544,6 @@ def test_nonascii(self):
class CEnvironmentVariableTests(EnvironmentVariableTests, unittest.TestCase):
module = c_warnings
- @unittest.expectedFailure # TODO: RUSTPYTHON; Lists differ
- def test_default_filter_configuration(self):
- return super().test_default_filter_configuration()
-
class PyEnvironmentVariableTests(EnvironmentVariableTests, unittest.TestCase):
module = py_warnings
@@ -1851,7 +1841,6 @@ def h(x):
self.assertEqual(len(overloads), 2)
self.assertEqual(overloads[0].__deprecated__, "no more ints")
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_class(self):
@deprecated("A will go away soon")
class A:
@@ -1863,7 +1852,6 @@ class A:
with self.assertRaises(TypeError):
A(42)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_class_with_init(self):
@deprecated("HasInit will go away soon")
class HasInit:
@@ -1874,7 +1862,6 @@ def __init__(self, x):
instance = HasInit(42)
self.assertEqual(instance.x, 42)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_class_with_new(self):
has_new_called = False
@@ -1893,7 +1880,6 @@ def __init__(self, x) -> None:
self.assertEqual(instance.x, 42)
self.assertTrue(has_new_called)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_class_with_inherited_new(self):
new_base_called = False
@@ -1915,7 +1901,6 @@ class HasInheritedNew(NewBase):
self.assertEqual(instance.x, 42)
self.assertTrue(new_base_called)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_class_with_new_but_no_init(self):
new_called = False
@@ -1933,7 +1918,6 @@ def __new__(cls, x):
self.assertEqual(instance.x, 42)
self.assertTrue(new_called)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_mixin_class(self):
@deprecated("Mixin will go away soon")
class Mixin:
@@ -1950,7 +1934,6 @@ class Child(Base, Mixin):
instance = Child(42)
self.assertEqual(instance.a, 42)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_do_not_shadow_user_arguments(self):
new_called = False
new_called_cls = None
@@ -1970,7 +1953,6 @@ class Foo(metaclass=MyMeta, cls='haha'):
self.assertTrue(new_called)
self.assertEqual(new_called_cls, 'haha')
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_existing_init_subclass(self):
@deprecated("C will go away soon")
class C:
@@ -1987,7 +1969,6 @@ class D(C):
self.assertTrue(D.inited)
self.assertIsInstance(D(), D) # no deprecation
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_existing_init_subclass_in_base(self):
class Base:
def __init_subclass__(cls, x) -> None:
@@ -2008,7 +1989,6 @@ class D(C, x=3):
self.assertEqual(D.inited, 3)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_existing_init_subclass_in_sibling_base(self):
@deprecated("A will go away soon")
class A:
@@ -2028,7 +2008,6 @@ class D(B, A, x=42):
pass
self.assertEqual(D.inited, 42)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_init_subclass_has_correct_cls(self):
init_subclass_saw = None
@@ -2046,7 +2025,6 @@ class C(Base):
self.assertIs(init_subclass_saw, C)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_init_subclass_with_explicit_classmethod(self):
init_subclass_saw = None
@@ -2065,7 +2043,6 @@ class C(Base):
self.assertIs(init_subclass_saw, C)
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_function(self):
@deprecated("b will go away soon")
def b():
@@ -2074,7 +2051,6 @@ def b():
with self.assertWarnsRegex(DeprecationWarning, "b will go away soon"):
b()
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_method(self):
class Capybara:
@deprecated("x will go away soon")
@@ -2085,7 +2061,6 @@ def x(self):
with self.assertWarnsRegex(DeprecationWarning, "x will go away soon"):
instance.x()
- @unittest.expectedFailure # TODO: RUSTPYTHON; DeprecationWarning not triggered
def test_property(self):
class Capybara:
@property
@@ -2113,7 +2088,6 @@ def no_more_setting(self, value):
with self.assertWarnsRegex(DeprecationWarning, "no more setting"):
instance.no_more_setting = 42
- @unittest.expectedFailure # TODO: RUSTPYTHON; RuntimeWarning not triggered
def test_category(self):
@deprecated("c will go away soon", category=RuntimeWarning)
def c():
diff --git a/crates/stdlib/src/_asyncio.rs b/crates/stdlib/src/_asyncio.rs
index 6e7a8c6e0e5..cc2e78bc35f 100644
--- a/crates/stdlib/src/_asyncio.rs
+++ b/crates/stdlib/src/_asyncio.rs
@@ -1014,10 +1014,12 @@ pub(crate) mod _asyncio {
// Warn about deprecated (type, val, tb) signature
if exc_val.is_present() || exc_tb.is_present() {
warn::warn(
- vm.ctx.new_str(
- "the (type, val, tb) signature of throw() is deprecated, \
+ vm.ctx
+ .new_str(
+ "the (type, val, tb) signature of throw() is deprecated, \
use throw(val) instead",
- ),
+ )
+ .into(),
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
1,
None,
diff --git a/crates/stdlib/src/socket.rs b/crates/stdlib/src/socket.rs
index 0d67d3680ad..32fba216e97 100644
--- a/crates/stdlib/src/socket.rs
+++ b/crates/stdlib/src/socket.rs
@@ -2144,7 +2144,7 @@ mod _socket {
laddr
);
let _ = crate::vm::warn::warn(
- vm.ctx.new_str(msg),
+ vm.ctx.new_str(msg).into(),
Some(vm.ctx.exceptions.resource_warning.to_owned()),
1,
None,
diff --git a/crates/vm/src/coroutine.rs b/crates/vm/src/coroutine.rs
index 67325283f3a..e2dd849c161 100644
--- a/crates/vm/src/coroutine.rs
+++ b/crates/vm/src/coroutine.rs
@@ -300,10 +300,12 @@ pub fn warn_deprecated_throw_signature(
) -> 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, \
+ vm.ctx
+ .new_str(
+ "the (type, val, tb) signature of throw() is deprecated, \
use throw(val) instead",
- ),
+ )
+ .into(),
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
1,
None,
diff --git a/crates/vm/src/frame.rs b/crates/vm/src/frame.rs
index da918af18c8..034dd8ce7f0 100644
--- a/crates/vm/src/frame.rs
+++ b/crates/vm/src/frame.rs
@@ -328,19 +328,14 @@ impl Py {
}
pub fn next_external_frame(&self, vm: &VirtualMachine) -> Option {
- self.f_back(vm).map(|mut back| {
- loop {
- back = if let Some(back) = back.to_owned().f_back(vm) {
- back
- } else {
- break back;
- };
-
- if !back.is_internal_frame() {
- break back;
- }
+ let mut frame = self.f_back(vm);
+ while let Some(ref f) = frame {
+ if !f.is_internal_frame() {
+ break;
}
- })
+ frame = f.f_back(vm);
+ }
+ frame
}
}
diff --git a/crates/vm/src/stdlib/ast/python.rs b/crates/vm/src/stdlib/ast/python.rs
index 772240451e5..ab21fb8f0dc 100644
--- a/crates/vm/src/stdlib/ast/python.rs
+++ b/crates/vm/src/stdlib/ast/python.rs
@@ -336,7 +336,7 @@ Support for arbitrary keyword arguments is deprecated and will be removed in Pyt
key
));
warn::warn(
- message,
+ message.into(),
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
1,
None,
@@ -387,7 +387,7 @@ Support for arbitrary keyword arguments is deprecated and will be removed in Pyt
field.as_str()
));
warn::warn(
- message,
+ message.into(),
Some(vm.ctx.exceptions.deprecation_warning.to_owned()),
1,
None,
diff --git a/crates/vm/src/stdlib/ast/string.rs b/crates/vm/src/stdlib/ast/string.rs
index bfeaad82f9c..4b6a6e8489f 100644
--- a/crates/vm/src/stdlib/ast/string.rs
+++ b/crates/vm/src/stdlib/ast/string.rs
@@ -224,7 +224,7 @@ fn warn_invalid_escape_sequences_in_format_spec(
"\"\\{next}\" is an invalid escape sequence. Such sequences will not work in the future. Did you mean \"\\\\{next}\"? A raw string is also an option."
));
let _ = warn::warn(
- message,
+ message.into(),
Some(vm.ctx.exceptions.syntax_warning.to_owned()),
1,
None,
diff --git a/crates/vm/src/stdlib/warnings.rs b/crates/vm/src/stdlib/warnings.rs
index 198df07d6c0..1725fefd2a8 100644
--- a/crates/vm/src/stdlib/warnings.rs
+++ b/crates/vm/src/stdlib/warnings.rs
@@ -20,29 +20,195 @@ pub fn warn(
#[pymodule]
mod _warnings {
use crate::{
- PyResult, VirtualMachine,
- builtins::{PyStrRef, PyTypeRef},
+ AsObject, PyObjectRef, PyResult, VirtualMachine,
+ builtins::{PyDictRef, PyListRef, PyStrRef, PyTupleRef, PyTypeRef},
+ convert::TryFromObject,
function::OptionalArg,
};
+ #[pyattr]
+ fn filters(vm: &VirtualMachine) -> PyListRef {
+ vm.state.warnings.filters.clone()
+ }
+
+ #[pyattr(name = "_defaultaction")]
+ fn default_action(vm: &VirtualMachine) -> PyStrRef {
+ vm.state.warnings.default_action.clone()
+ }
+
+ #[pyattr(name = "_onceregistry")]
+ fn once_registry(vm: &VirtualMachine) -> PyDictRef {
+ vm.state.warnings.once_registry.clone()
+ }
+
+ #[pyattr(name = "_warnings_context")]
+ fn warnings_context(vm: &VirtualMachine) -> PyObjectRef {
+ vm.state
+ .warnings
+ .context_var
+ .get_or_init(|| {
+ // Try to create a real ContextVar if _contextvars is available.
+ // During early startup it may not be importable yet, in which
+ // case we fall back to None. This is safe because
+ // context_aware_warnings defaults to False.
+ if let Ok(contextvars) = vm.import("_contextvars", 0)
+ && let Ok(cv_cls) = contextvars.get_attr("ContextVar", vm)
+ && let Ok(cv) = cv_cls.call(("_warnings_context",), vm)
+ {
+ cv
+ } else {
+ vm.ctx.none()
+ }
+ })
+ .clone()
+ }
+
+ #[pyfunction(name = "_acquire_lock")]
+ fn acquire_lock(vm: &VirtualMachine) {
+ vm.state.warnings.acquire_lock();
+ }
+
+ #[pyfunction(name = "_release_lock")]
+ fn release_lock(vm: &VirtualMachine) -> PyResult<()> {
+ if !vm.state.warnings.release_lock() {
+ return Err(vm.new_runtime_error("cannot release un-acquired lock".to_owned()));
+ }
+ Ok(())
+ }
+
+ #[pyfunction(name = "_filters_mutated_lock_held")]
+ fn filters_mutated_lock_held(vm: &VirtualMachine) {
+ vm.state.warnings.filters_mutated();
+ }
+
#[derive(FromArgs)]
struct WarnArgs {
#[pyarg(positional)]
- message: PyStrRef,
+ message: PyObjectRef,
#[pyarg(any, optional)]
- category: OptionalArg,
+ category: OptionalArg,
#[pyarg(any, optional)]
- stacklevel: OptionalArg,
+ stacklevel: OptionalArg,
+ #[pyarg(named, optional)]
+ source: OptionalArg,
+ #[pyarg(named, optional)]
+ skip_file_prefixes: OptionalArg,
+ }
+
+ /// Validate and resolve the category argument, matching get_category() in C.
+ fn get_category(
+ message: &PyObjectRef,
+ category: Option,
+ vm: &VirtualMachine,
+ ) -> PyResult