mirror of
https://github.com/RustPython/RustPython.git
synced 2026-06-02 19:39:49 +09:00
generator is borrowed
This commit is contained in:
2
Lib/test/test_asyncgen.py
vendored
2
Lib/test/test_asyncgen.py
vendored
@@ -1026,7 +1026,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||
fut.cancel()
|
||||
self.loop.run_until_complete(asyncio.sleep(0.01))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; gc_collect doesn't finalize async generators
|
||||
def test_async_gen_asyncio_gc_aclose_09(self):
|
||||
DONE = 0
|
||||
|
||||
@@ -1513,7 +1512,6 @@ class AsyncGenAsyncioTest(unittest.TestCase):
|
||||
self.assertIn('an error occurred during closing of asynchronous generator',
|
||||
message['message'])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; gc_collect doesn't finalize async generators, different cleanup path
|
||||
def test_async_gen_asyncio_shutdown_exception_02(self):
|
||||
messages = []
|
||||
|
||||
|
||||
8
Lib/test/test_asyncio/test_free_threading.py
vendored
8
Lib/test/test_asyncio/test_free_threading.py
vendored
@@ -189,10 +189,6 @@ class TestPyFreeThreading(TestFreeThreading, TestCase):
|
||||
def factory(self, loop, coro, **kwargs):
|
||||
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference timing issue
|
||||
def test_task_different_thread_finalized(self):
|
||||
return super().test_task_different_thread_finalized()
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON; hangs - Python _current_tasks dict not thread-safe")
|
||||
def test_all_tasks_race(self):
|
||||
return super().test_all_tasks_race()
|
||||
@@ -228,10 +224,6 @@ class TestEagerPyFreeThreading(TestPyFreeThreading):
|
||||
def factory(self, loop, coro, eager_start=True, **kwargs):
|
||||
return asyncio.tasks._PyTask(coro, loop=loop, **kwargs, eager_start=eager_start)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference timing issue
|
||||
def test_task_different_thread_finalized(self):
|
||||
return super().test_task_different_thread_finalized()
|
||||
|
||||
@unittest.skip("TODO: RUSTPYTHON; hangs - Python _current_tasks dict not thread-safe")
|
||||
def test_all_tasks_race(self):
|
||||
return super().test_all_tasks_race()
|
||||
|
||||
1
Lib/test/test_asyncio/test_streams.py
vendored
1
Lib/test/test_asyncio/test_streams.py
vendored
@@ -1078,7 +1078,6 @@ class StreamTests(test_utils.TestCase):
|
||||
|
||||
self.assertEqual(messages, [])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC finalization timing issue
|
||||
def test_unclosed_resource_warnings(self):
|
||||
async def inner(httpd):
|
||||
rd, wr = await asyncio.open_connection(*httpd.address)
|
||||
|
||||
@@ -72,7 +72,6 @@ class AsCompletedTests:
|
||||
]
|
||||
self.assertEqual(len(completed), 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference not collected
|
||||
def test_free_reference_yielded_future(self):
|
||||
# Issue #14406: Generator should not keep references
|
||||
# to finished futures.
|
||||
|
||||
@@ -136,11 +136,9 @@ class FailingInitializerResourcesTest(unittest.TestCase):
|
||||
|
||||
self.assertEqual(_resource_tracker._exitcode, 0)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; resource tracker exit code mismatch
|
||||
def test_spawn(self):
|
||||
self._test(ProcessPoolSpawnFailingInitializerTest)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; resource tracker exit code mismatch
|
||||
@support.skip_if_sanitizer("TSAN doesn't support threads after fork", thread=True)
|
||||
def test_forkserver(self):
|
||||
self._test(ProcessPoolForkserverFailingInitializerTest)
|
||||
|
||||
8
Lib/test/test_copy.py
vendored
8
Lib/test/test_copy.py
vendored
@@ -837,15 +837,15 @@ class TestCopy(unittest.TestCase):
|
||||
v[x] = y
|
||||
self.assertNotIn(x, u)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
|
||||
def test_copy_weakkeydict(self):
|
||||
self._check_copy_weakdict(weakref.WeakKeyDictionary)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
|
||||
def test_copy_weakvaluedict(self):
|
||||
self._check_copy_weakdict(weakref.WeakValueDictionary)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
|
||||
def test_deepcopy_weakkeydict(self):
|
||||
class C(object):
|
||||
def __init__(self, i):
|
||||
@@ -866,7 +866,7 @@ class TestCopy(unittest.TestCase):
|
||||
support.gc_collect() # For PyPy or other GCs.
|
||||
self.assertEqual(len(v), 1)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
|
||||
def test_deepcopy_weakvaluedict(self):
|
||||
class C(object):
|
||||
def __init__(self, i):
|
||||
|
||||
1
Lib/test/test_exceptions.py
vendored
1
Lib/test/test_exceptions.py
vendored
@@ -1076,7 +1076,6 @@ class ExceptionTests(unittest.TestCase):
|
||||
g.close()
|
||||
self._check_generator_cleanup_exc_state(do_close)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC generator cleanup timing
|
||||
def test_generator_del_cleanup_exc_state(self):
|
||||
def do_del(g):
|
||||
g = None
|
||||
|
||||
2
Lib/test/test_subprocess.py
vendored
2
Lib/test/test_subprocess.py
vendored
@@ -3282,7 +3282,6 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||
finally:
|
||||
p.wait()
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC Popen.__del__ timing
|
||||
def test_zombie_fast_process_del(self):
|
||||
# Issue #12650: on Unix, if Popen.__del__() was called before the
|
||||
# process exited, it wouldn't be added to subprocess._active, and would
|
||||
@@ -3307,7 +3306,6 @@ class POSIXProcessTestCase(BaseTestCase):
|
||||
# check that p is in the active processes list
|
||||
self.assertIn(ident, [id(o) for o in subprocess._active])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC Popen.__del__ timing
|
||||
def test_leak_fast_process_del_killed(self):
|
||||
# Issue #12650: on Unix, if Popen.__del__() was called before the
|
||||
# process exited, and the process got killed by a signal, it would never
|
||||
|
||||
2
Lib/test/test_unittest/test_suite.py
vendored
2
Lib/test/test_unittest/test_suite.py
vendored
@@ -374,11 +374,9 @@ class Test_TestSuite(unittest.TestCase, TestEquality):
|
||||
self.assertEqual(suite._tests, [None])
|
||||
self.assertIsNone(wref())
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC test not collected after run
|
||||
def test_garbage_collect_test_after_run_BaseTestSuite(self):
|
||||
self.assert_garbage_collect_test_after_run(unittest.BaseTestSuite)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC test not collected after run
|
||||
def test_garbage_collect_test_after_run_TestSuite(self):
|
||||
self.assert_garbage_collect_test_after_run(unittest.TestSuite)
|
||||
|
||||
|
||||
4
Lib/test/test_weakref.py
vendored
4
Lib/test/test_weakref.py
vendored
@@ -1368,7 +1368,6 @@ class MappingTestCase(TestBase):
|
||||
def test_weak_valued_len_race(self):
|
||||
self.check_len_race(weakref.WeakValueDictionary, lambda k: (1, k))
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
def test_weak_values(self):
|
||||
#
|
||||
# This exercises d.copy(), d.items(), d[], del d[], len(d).
|
||||
@@ -1401,7 +1400,6 @@ class MappingTestCase(TestBase):
|
||||
gc_collect() # For PyPy or other GCs.
|
||||
self.assertRaises(KeyError, dict.__getitem__, 2)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
def test_weak_keys(self):
|
||||
#
|
||||
# This exercises d.copy(), d.items(), d[] = v, d[], del d[],
|
||||
@@ -1767,7 +1765,6 @@ class MappingTestCase(TestBase):
|
||||
self.assertEqual(list(d.keys()), [kw])
|
||||
self.assertEqual(d[kw], o)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
def test_weak_valued_union_operators(self):
|
||||
a = C()
|
||||
b = C()
|
||||
@@ -1820,7 +1817,6 @@ class MappingTestCase(TestBase):
|
||||
self.assertEqual(len(d), 1)
|
||||
self.assertEqual(list(d.keys()), [o2])
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; weakref callback not fired immediately by gc_collect
|
||||
def test_weak_keyed_union_operators(self):
|
||||
o1 = C()
|
||||
o2 = C()
|
||||
|
||||
3
Lib/test/test_weakset.py
vendored
3
Lib/test/test_weakset.py
vendored
@@ -69,7 +69,6 @@ class TestWeakSet(unittest.TestCase):
|
||||
support.gc_collect() # For PyPy or other GCs.
|
||||
self.assertNotIn(ustr('F'), self.fs)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference not collected
|
||||
def test_union(self):
|
||||
u = self.s.union(self.items2)
|
||||
for c in self.letters:
|
||||
@@ -92,7 +91,6 @@ class TestWeakSet(unittest.TestCase):
|
||||
self.assertEqual(self.s | set(self.items2), i)
|
||||
self.assertEqual(self.s | frozenset(self.items2), i)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference not collected
|
||||
def test_intersection(self):
|
||||
s = WeakSet(self.letters)
|
||||
i = s.intersection(self.items2)
|
||||
@@ -130,7 +128,6 @@ class TestWeakSet(unittest.TestCase):
|
||||
self.assertEqual(self.s - set(self.items2), i)
|
||||
self.assertEqual(self.s - frozenset(self.items2), i)
|
||||
|
||||
@unittest.expectedFailure # TODO: RUSTPYTHON; GC weak reference not collected
|
||||
def test_symmetric_difference(self):
|
||||
i = self.s.symmetric_difference(self.items2)
|
||||
for c in self.letters:
|
||||
|
||||
@@ -513,9 +513,7 @@ impl PyAsyncGenAThrow {
|
||||
) -> PyResult {
|
||||
match self.state.load() {
|
||||
AwaitableState::Closed => {
|
||||
return Err(
|
||||
vm.new_runtime_error("cannot reuse already awaited aclose()/athrow()")
|
||||
);
|
||||
return Err(vm.new_runtime_error("cannot reuse already awaited aclose()/athrow()"));
|
||||
}
|
||||
AwaitableState::Init => {
|
||||
if self.ag.running_async.load() {
|
||||
@@ -816,6 +814,12 @@ impl Destructor for PyAsyncGen {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PyAsyncGen {
|
||||
fn drop(&mut self) {
|
||||
self.inner.frame().clear_generator();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ctx: &Context) {
|
||||
PyAsyncGen::extend_class(ctx, ctx.types.async_generator);
|
||||
PyAsyncGenASend::extend_class(ctx, ctx.types.async_generator_asend);
|
||||
|
||||
@@ -222,6 +222,12 @@ impl IterNext for PyCoroutineWrapper {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PyCoroutine {
|
||||
fn drop(&mut self) {
|
||||
self.inner.frame().clear_generator();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ctx: &Context) {
|
||||
PyCoroutine::extend_class(ctx, ctx.types.coroutine_type);
|
||||
PyCoroutineWrapper::extend_class(ctx, ctx.types.coroutine_wrapper_type);
|
||||
|
||||
@@ -153,7 +153,7 @@ impl Frame {
|
||||
impl Py<Frame> {
|
||||
#[pygetset]
|
||||
fn f_generator(&self) -> Option<PyObjectRef> {
|
||||
self.generator.lock().clone()
|
||||
self.generator.to_owned()
|
||||
}
|
||||
|
||||
#[pygetset]
|
||||
|
||||
@@ -531,19 +531,19 @@ impl Py<PyFunction> {
|
||||
(true, false) => {
|
||||
let obj = PyGenerator::new(frame.clone(), self.__name__(), self.__qualname__())
|
||||
.into_pyobject(vm);
|
||||
frame.set_generator(obj.clone());
|
||||
frame.set_generator(&obj);
|
||||
Ok(obj)
|
||||
}
|
||||
(false, true) => {
|
||||
let obj = PyCoroutine::new(frame.clone(), self.__name__(), self.__qualname__())
|
||||
.into_pyobject(vm);
|
||||
frame.set_generator(obj.clone());
|
||||
frame.set_generator(&obj);
|
||||
Ok(obj)
|
||||
}
|
||||
(true, true) => {
|
||||
let obj = PyAsyncGen::new(frame.clone(), self.__name__(), self.__qualname__())
|
||||
.into_pyobject(vm);
|
||||
frame.set_generator(obj.clone());
|
||||
frame.set_generator(&obj);
|
||||
Ok(obj)
|
||||
}
|
||||
(false, false) => vm.run_frame(frame),
|
||||
|
||||
@@ -140,6 +140,12 @@ impl IterNext for PyGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PyGenerator {
|
||||
fn drop(&mut self) {
|
||||
self.inner.frame().clear_generator();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(ctx: &Context) {
|
||||
PyGenerator::extend_class(ctx, ctx.types.generator_type);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ use crate::{
|
||||
coroutine::Coro,
|
||||
exceptions::ExceptionCtor,
|
||||
function::{ArgMapping, Either, FuncArgs},
|
||||
object::PyAtomicBorrow,
|
||||
object::{Traverse, TraverseFn},
|
||||
protocol::{PyIter, PyIterReturn},
|
||||
scope::Scope,
|
||||
@@ -85,8 +86,10 @@ pub struct Frame {
|
||||
pub trace_lines: PyMutex<bool>,
|
||||
pub trace_opcodes: PyMutex<bool>,
|
||||
pub temporary_refs: PyMutex<Vec<PyObjectRef>>,
|
||||
/// Back-reference to owning generator/coroutine/async generator
|
||||
pub generator: PyMutex<Option<PyObjectRef>>,
|
||||
/// Back-reference to owning generator/coroutine/async generator.
|
||||
/// Borrowed reference (not ref-counted) to avoid Generator↔Frame cycle.
|
||||
/// Cleared by the generator's Drop impl.
|
||||
pub generator: PyAtomicBorrow,
|
||||
}
|
||||
|
||||
impl PyPayload for Frame {
|
||||
@@ -114,7 +117,7 @@ unsafe impl Traverse for Frame {
|
||||
self.trace.traverse(tracer_fn);
|
||||
self.state.traverse(tracer_fn);
|
||||
self.temporary_refs.traverse(tracer_fn);
|
||||
self.generator.traverse(tracer_fn);
|
||||
// generator is a borrowed reference, not traversed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -175,12 +178,19 @@ impl Frame {
|
||||
trace_lines: PyMutex::new(true),
|
||||
trace_opcodes: PyMutex::new(false),
|
||||
temporary_refs: PyMutex::new(vec![]),
|
||||
generator: PyMutex::new(None),
|
||||
generator: PyAtomicBorrow::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_generator(&self, generator: PyObjectRef) {
|
||||
*self.generator.lock() = Some(generator);
|
||||
/// Store a borrowed back-reference to the owning generator/coroutine.
|
||||
/// The caller must ensure the generator outlives the frame.
|
||||
pub fn set_generator(&self, generator: &PyObject) {
|
||||
self.generator.store(generator);
|
||||
}
|
||||
|
||||
/// Clear the generator back-reference. Called when the generator is finalized.
|
||||
pub fn clear_generator(&self) {
|
||||
self.generator.clear();
|
||||
}
|
||||
|
||||
pub fn current_location(&self) -> SourceLocation {
|
||||
|
||||
Reference in New Issue
Block a user