GeezerVison is showing github.com — Merge remote-tracking branch 'upstream/main' into skip-flaky-mp-tests · RustPython/RustPython@3f79cb5
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: .cspell.dict/cpython.txt +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -43,6 +43,7 @@ CONVFUNC | 43 | 43 | convparam | 44 | 44 | copyslot | 45 | 45 | cpucount | | 46 | +datastack | 46 | 47 | defaultdict | 47 | 48 | denom | 48 | 49 | deopt | @@ -118,13 +119,15 @@ mult | 118 | 119 | multibytecodec | 119 | 120 | nameobj | 120 | 121 | nameop | | 122 | +ncells | 121 | 123 | nconsts | 122 | 124 | newargs | 123 | 125 | newfree | 124 | 126 | NEWLOCALS | 125 | 127 | newsemlockobject | 126 | 128 | nfrees | 127 | 129 | nkwargs | | 130 | +nlocalsplus | 128 | 131 | nkwelts | 129 | 132 | Nondescriptor | 130 | 133 | noninteger | @@ -192,6 +195,7 @@ testconsole | 192 | 195 | ticketer | 193 | 196 | tmptype | 194 | 197 | tok_oldval | | 198 | +tstate | 195 | 199 | tvars | 196 | 200 | typeobject | 197 | 201 | typeparam | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/mapping_tests.py +12-7Lines changed: 12 additions & 7 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | # tests common to dict and UserDict | 2 | 2 | import unittest | 3 | 3 | import collections | 4 | | -import sys | | 4 | +from test import support | 5 | 5 |
| 6 | 6 |
| 7 | 7 | class BasicTestMappingProtocol(unittest.TestCase): | @@ -70,8 +70,8 @@ def test_read(self): | 70 | 70 | if not d: self.fail("Full mapping must compare to True") | 71 | 71 | # keys(), items(), iterkeys() ... | 72 | 72 | def check_iterandlist(iter, lst, ref): | 73 | | - self.assertTrue(hasattr(iter, '__next__')) | 74 | | - self.assertTrue(hasattr(iter, '__iter__')) | | 73 | + self.assertHasAttr(iter, '__next__') | | 74 | + self.assertHasAttr(iter, '__iter__') | 75 | 75 | x = list(iter) | 76 | 76 | self.assertTrue(set(x)==set(lst)==set(ref)) | 77 | 77 | check_iterandlist(iter(d.keys()), list(d.keys()), | @@ -448,7 +448,7 @@ def __new__(cls): | 448 | 448 | class Exc(Exception): pass | 449 | 449 |
| 450 | 450 | class baddict1(self.type2test): | 451 | | - def __init__(self): | | 451 | + def __init__(self, *args, **kwargs): | 452 | 452 | raise Exc() | 453 | 453 |
| 454 | 454 | self.assertRaises(Exc, baddict1.fromkeys, [1]) | @@ -595,12 +595,14 @@ def test_mutatingiteration(self): | 595 | 595 | d = self._empty_mapping() | 596 | 596 | d[1] = 1 | 597 | 597 | try: | | 598 | + count = 0 | 598 | 599 | for i in d: | 599 | 600 | d[i+1] = 1 | | 601 | + if count >= 1: | | 602 | + self.fail("changing dict size during iteration doesn't raise Error") | | 603 | + count += 1 | 600 | 604 | except RuntimeError: | 601 | 605 | pass | 602 | | - else: | 603 | | - self.fail("changing dict size during iteration doesn't raise Error") | 604 | 606 |
| 605 | 607 | def test_repr(self): | 606 | 608 | d = self._empty_mapping() | @@ -620,9 +622,12 @@ def __repr__(self): | 620 | 622 | d = self._full_mapping({1: BadRepr()}) | 621 | 623 | self.assertRaises(Exc, repr, d) | 622 | 624 |
| | 625 | + @support.skip_wasi_stack_overflow() | | 626 | + @support.skip_emscripten_stack_overflow() | | 627 | + @support.skip_if_sanitizer("requires deep stack", ub=True) | 623 | 628 | def test_repr_deep(self): | 624 | 629 | d = self._empty_mapping() | 625 | | - for i in range(sys.getrecursionlimit() + 100): | | 630 | + for i in range(support.exceeds_recursion_limit()): | 626 | 631 | d0 = d | 627 | 632 | d = self._empty_mapping() | 628 | 633 | d[1] = d0 | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_collections.py +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -262,7 +262,7 @@ def __contains__(self, key): | 262 | 262 | d = c.new_child(b=20, c=30) | 263 | 263 | self.assertEqual(d.maps, [{'b': 20, 'c': 30}, {'a': 1, 'b': 2}]) | 264 | 264 |
| 265 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | | 265 | + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: <class 'test.test_collections.TestChainMap.test_union_operators.<locals>.Subclass'> is not <class 'collections.ChainMap'> | 266 | 266 | def test_union_operators(self): | 267 | 267 | cm1 = ChainMap(dict(a=1, b=2), dict(c=3, d=4)) | 268 | 268 | cm2 = ChainMap(dict(a=10, e=5), dict(b=20, d=4)) | @@ -2029,7 +2029,7 @@ def insert(self, index, value): | 2029 | 2029 | self.assertEqual(len(mss), len(mss2)) | 2030 | 2030 | self.assertEqual(list(mss), list(mss2)) | 2031 | 2031 |
| 2032 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | | 2032 | + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: TypeError not raised | 2033 | 2033 | def test_illegal_patma_flags(self): | 2034 | 2034 | with self.assertRaises(TypeError): | 2035 | 2035 | class Both(Collection): | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_deque.py +3-3Lines changed: 3 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -726,7 +726,7 @@ def test_gc_doesnt_blowup(self): | 726 | 726 | d.append(1) | 727 | 727 | gc.collect() | 728 | 728 |
| 729 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | | 729 | + @unittest.expectedFailure # TODO: RUSTPYTHON; AssertionError: False is not true : Cycle was not collected | 730 | 730 | def test_container_iterator(self): | 731 | 731 | # Bug #3680: tp_traverse was not implemented for deque iterator objects | 732 | 732 | class C(object): | @@ -817,7 +817,7 @@ def test_basics(self): | 817 | 817 | d.clear() | 818 | 818 | self.assertEqual(len(d), 0) | 819 | 819 |
| 820 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | | 820 | + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'Deque' object has no attribute 'x' | 821 | 821 | def test_copy_pickle(self): | 822 | 822 | for cls in Deque, DequeWithSlots: | 823 | 823 | for d in cls('abc'), cls('abcde', maxlen=4): | @@ -842,7 +842,7 @@ def test_copy_pickle(self): | 842 | 842 | self.assertEqual(e.z, d.z) | 843 | 843 | self.assertNotHasAttr(e, 'y') | 844 | 844 |
| 845 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | | 845 | + @unittest.expectedFailure # TODO: RUSTPYTHON; AttributeError: 'Deque' object has no attribute 'x' | 846 | 846 | def test_pickle_recursive(self): | 847 | 847 | for proto in range(pickle.HIGHEST_PROTOCOL + 1): | 848 | 848 | for d in Deque('abc'), Deque('abc', 3): | |
Collapse file Copy file name to clipboardExpand all lines: Lib/test/test_pickle.py -4Lines changed: 0 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -85,10 +85,6 @@ def dumps(self, arg, proto=None, **kwargs): | 85 | 85 | f.seek(0) | 86 | 86 | return bytes(f.read()) | 87 | 87 |
| 88 | | - @unittest.expectedFailure # TODO: RUSTPYTHON | 89 | | - def test_bad_newobj_args(self): | 90 | | - return super().test_bad_newobj_args() | 91 | | - | 92 | 88 | @unittest.expectedFailure # TODO: RUSTPYTHON | 93 | 89 | def test_buffer_callback_error(self): | 94 | 90 | return super().test_buffer_callback_error() | |
Collapse file Copy file name to clipboardExpand all lines: crates/common/Cargo.toml +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -11,7 +11,7 @@ license.workspace = true | 11 | 11 | [features] | 12 | 12 | default = ["std"] | 13 | 13 | std = [] | 14 | | -threading = ["parking_lot"] | | 14 | +threading = ["parking_lot", "std"] | 15 | 15 | wasm_js = ["getrandom/wasm_js"] | 16 | 16 |
| 17 | 17 | [dependencies] | |
Collapse file Copy file name to clipboardExpand all lines: crates/common/src/linked_list.rs +33-2Lines changed: 33 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -157,6 +157,15 @@ impl<L: Link> LinkedList<L, L::Target> { | 157 | 157 | let ptr = L::as_raw(&val); | 158 | 158 | assert_ne!(self.head, Some(ptr)); | 159 | 159 | unsafe { | | 160 | + // Verify the node is not already in a list (pointers must be clean) | | 161 | + debug_assert!( | | 162 | + L::pointers(ptr).as_ref().get_prev().is_none(), | | 163 | + "push_front: node already has prev pointer (double-insert?)" | | 164 | + ); | | 165 | + debug_assert!( | | 166 | + L::pointers(ptr).as_ref().get_next().is_none(), | | 167 | + "push_front: node already has next pointer (double-insert?)" | | 168 | + ); | 160 | 169 | L::pointers(ptr).as_mut().set_next(self.head); | 161 | 170 | L::pointers(ptr).as_mut().set_prev(None); | 162 | 171 |
| @@ -192,6 +201,20 @@ impl<L: Link> LinkedList<L, L::Target> { | 192 | 201 | // } | 193 | 202 | // } | 194 | 203 |
| | 204 | + /// Removes the first element from the list and returns it, or None if empty. | | 205 | + pub fn pop_front(&mut self) -> Option<L::Handle> { | | 206 | + let head = self.head?; | | 207 | + unsafe { | | 208 | + self.head = L::pointers(head).as_ref().get_next(); | | 209 | + if let Some(new_head) = self.head { | | 210 | + L::pointers(new_head).as_mut().set_prev(None); | | 211 | + } | | 212 | + L::pointers(head).as_mut().set_next(None); | | 213 | + L::pointers(head).as_mut().set_prev(None); | | 214 | + Some(L::from_raw(head)) | | 215 | + } | | 216 | + } | | 217 | + | 195 | 218 | /// Returns whether the linked list does not contain any node | 196 | 219 | pub const fn is_empty(&self) -> bool { | 197 | 220 | self.head.is_none() | @@ -212,7 +235,11 @@ impl<L: Link> LinkedList<L, L::Target> { | 212 | 235 | pub unsafe fn remove(&mut self, node: NonNull<L::Target>) -> Option<L::Handle> { | 213 | 236 | unsafe { | 214 | 237 | if let Some(prev) = L::pointers(node).as_ref().get_prev() { | 215 | | - debug_assert_eq!(L::pointers(prev).as_ref().get_next(), Some(node)); | | 238 | + debug_assert_eq!( | | 239 | + L::pointers(prev).as_ref().get_next(), | | 240 | + Some(node), | | 241 | + "linked list corruption: prev->next != node (prev={prev:p}, node={node:p})" | | 242 | + ); | 216 | 243 | L::pointers(prev) | 217 | 244 | .as_mut() | 218 | 245 | .set_next(L::pointers(node).as_ref().get_next()); | @@ -225,7 +252,11 @@ impl<L: Link> LinkedList<L, L::Target> { | 225 | 252 | } | 226 | 253 |
| 227 | 254 | if let Some(next) = L::pointers(node).as_ref().get_next() { | 228 | | - debug_assert_eq!(L::pointers(next).as_ref().get_prev(), Some(node)); | | 255 | + debug_assert_eq!( | | 256 | + L::pointers(next).as_ref().get_prev(), | | 257 | + Some(node), | | 258 | + "linked list corruption: next->prev != node (next={next:p}, node={node:p})" | | 259 | + ); | 229 | 260 | L::pointers(next) | 230 | 261 | .as_mut() | 231 | 262 | .set_prev(L::pointers(node).as_ref().get_prev()); | |
Collapse file Copy file name to clipboardExpand all lines: crates/common/src/lock.rs +14-4Lines changed: 14 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -10,18 +10,28 @@ cfg_if::cfg_if! { | 10 | 10 | if #[cfg(feature = "threading")] { | 11 | 11 | pub use parking_lot::{RawMutex, RawRwLock, RawThreadId}; | 12 | 12 |
| 13 | | - pub use std::sync::{LazyLock, OnceLock as OnceCell}; | | 13 | + pub use std::sync::OnceLock as OnceCell; | 14 | 14 | pub use core::cell::LazyCell; | 15 | 15 | } else { | 16 | 16 | mod cell_lock; | 17 | 17 | pub use cell_lock::{RawCellMutex as RawMutex, RawCellRwLock as RawRwLock, SingleThreadId as RawThreadId}; | 18 | 18 |
| 19 | 19 | pub use core::cell::{LazyCell, OnceCell}; | | 20 | + } | | 21 | +} | 20 | 22 |
| 21 | | - /// `core::cell::LazyCell` with `Sync` for use in `static` items. | 22 | | - /// SAFETY: Without threading, there can be no concurrent access. | | 23 | +// LazyLock: uses std::sync::LazyLock when std is available (even without | | 24 | +// threading, because Rust test runner uses parallel threads). | | 25 | +// Without std, uses a LazyCell wrapper (truly single-threaded only). | | 26 | +cfg_if::cfg_if! { | | 27 | + if #[cfg(any(feature = "threading", feature = "std"))] { | | 28 | + pub use std::sync::LazyLock; | | 29 | + } else { | 23 | 30 | pub struct LazyLock<T, F = fn() -> T>(core::cell::LazyCell<T, F>); | 24 | | - // SAFETY: Without threading, there can be no concurrent access. | | 31 | + // SAFETY: This branch is only active when both "std" and "threading" | | 32 | + // features are absent — i.e., truly single-threaded no_std environments | | 33 | + // (e.g., embedded or bare-metal WASM). Without std, the Rust runtime | | 34 | + // cannot spawn threads, so Sync is trivially satisfied. | 25 | 35 | unsafe impl<T, F> Sync for LazyLock<T, F> {} | 26 | 36 |
| 27 | 37 | impl<T, F: FnOnce() -> T> LazyLock<T, F> { | |
|