Skip to content

Commit f19fe28

Browse files
committed
sys.set_asyncgen_hook
1 parent 3fbf54f commit f19fe28

File tree

2 files changed

+66
-13
lines changed

2 files changed

+66
-13
lines changed

Lib/test/test_asyncgen.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1485,8 +1485,6 @@ async def main():
14851485

14861486
self.assertEqual(messages, [])
14871487

1488-
# TODO: RUSTPYTHON, ValueError: not enough values to unpack (expected 1, got 0)
1489-
@unittest.expectedFailure
14901488
def test_async_gen_asyncio_shutdown_exception_01(self):
14911489
messages = []
14921490

crates/vm/src/builtins/asyncgenerator.rs

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use crate::{
33
AsObject, Context, Py, PyObjectRef, PyPayload, PyRef, PyResult, VirtualMachine,
44
builtins::PyBaseExceptionRef,
55
class::PyClassImpl,
6+
common::lock::PyMutex,
67
coroutine::Coro,
78
frame::FrameRef,
89
function::OptionalArg,
@@ -17,6 +18,10 @@ use crossbeam_utils::atomic::AtomicCell;
1718
pub struct PyAsyncGen {
1819
inner: Coro,
1920
running_async: AtomicCell<bool>,
21+
// whether hooks have been initialized
22+
ag_hooks_inited: AtomicCell<bool>,
23+
// ag_origin_or_finalizer - stores the finalizer callback
24+
ag_finalizer: PyMutex<Option<PyObjectRef>>,
2025
}
2126
type PyAsyncGenRef = PyRef<PyAsyncGen>;
2227

@@ -37,6 +42,48 @@ impl PyAsyncGen {
3742
Self {
3843
inner: Coro::new(frame, name, qualname),
3944
running_async: AtomicCell::new(false),
45+
ag_hooks_inited: AtomicCell::new(false),
46+
ag_finalizer: PyMutex::new(None),
47+
}
48+
}
49+
50+
/// Initialize async generator hooks.
51+
/// Returns Ok(()) if successful, Err if firstiter hook raised an exception.
52+
fn init_hooks(zelf: &Py<Self>, vm: &VirtualMachine) -> PyResult<()> {
53+
// = async_gen_init_hooks
54+
if zelf.ag_hooks_inited.load() {
55+
return Ok(());
56+
}
57+
58+
zelf.ag_hooks_inited.store(true);
59+
60+
// Get and store finalizer from thread-local storage
61+
let finalizer = crate::vm::thread::ASYNC_GEN_FINALIZER.with_borrow(|f| f.as_ref().cloned());
62+
if let Some(finalizer) = finalizer {
63+
*zelf.ag_finalizer.lock() = Some(finalizer);
64+
}
65+
66+
// Call firstiter hook
67+
let firstiter = crate::vm::thread::ASYNC_GEN_FIRSTITER.with_borrow(|f| f.as_ref().cloned());
68+
if let Some(firstiter) = firstiter {
69+
let obj: PyObjectRef = zelf.to_owned().into();
70+
firstiter.call((obj,), vm)?;
71+
}
72+
73+
Ok(())
74+
}
75+
76+
/// Call finalizer hook if set
77+
#[allow(dead_code)]
78+
fn call_finalizer(zelf: &Py<Self>, vm: &VirtualMachine) {
79+
// = gen_dealloc
80+
let finalizer = zelf.ag_finalizer.lock().clone();
81+
if let Some(finalizer) = finalizer
82+
&& !zelf.inner.closed.load()
83+
{
84+
// Call finalizer, ignore any errors (PyErr_WriteUnraisable)
85+
let obj: PyObjectRef = zelf.to_owned().into();
86+
let _ = finalizer.call((obj,), vm);
4087
}
4188
}
4289

@@ -91,17 +138,23 @@ impl PyRef<PyAsyncGen> {
91138
}
92139

93140
#[pymethod]
94-
fn __anext__(self, vm: &VirtualMachine) -> PyAsyncGenASend {
95-
Self::asend(self, vm.ctx.none(), vm)
141+
fn __anext__(self, vm: &VirtualMachine) -> PyResult<PyAsyncGenASend> {
142+
PyAsyncGen::init_hooks(&self, vm)?;
143+
Ok(PyAsyncGenASend {
144+
ag: self,
145+
state: AtomicCell::new(AwaitableState::Init),
146+
value: vm.ctx.none(),
147+
})
96148
}
97149

98150
#[pymethod]
99-
const fn asend(self, value: PyObjectRef, _vm: &VirtualMachine) -> PyAsyncGenASend {
100-
PyAsyncGenASend {
151+
fn asend(self, value: PyObjectRef, vm: &VirtualMachine) -> PyResult<PyAsyncGenASend> {
152+
PyAsyncGen::init_hooks(&self, vm)?;
153+
Ok(PyAsyncGenASend {
101154
ag: self,
102155
state: AtomicCell::new(AwaitableState::Init),
103156
value,
104-
}
157+
})
105158
}
106159

107160
#[pymethod]
@@ -111,8 +164,9 @@ impl PyRef<PyAsyncGen> {
111164
exc_val: OptionalArg,
112165
exc_tb: OptionalArg,
113166
vm: &VirtualMachine,
114-
) -> PyAsyncGenAThrow {
115-
PyAsyncGenAThrow {
167+
) -> PyResult<PyAsyncGenAThrow> {
168+
PyAsyncGen::init_hooks(&self, vm)?;
169+
Ok(PyAsyncGenAThrow {
116170
ag: self,
117171
aclose: false,
118172
state: AtomicCell::new(AwaitableState::Init),
@@ -121,12 +175,13 @@ impl PyRef<PyAsyncGen> {
121175
exc_val.unwrap_or_none(vm),
122176
exc_tb.unwrap_or_none(vm),
123177
),
124-
}
178+
})
125179
}
126180

127181
#[pymethod]
128-
fn aclose(self, vm: &VirtualMachine) -> PyAsyncGenAThrow {
129-
PyAsyncGenAThrow {
182+
fn aclose(self, vm: &VirtualMachine) -> PyResult<PyAsyncGenAThrow> {
183+
PyAsyncGen::init_hooks(&self, vm)?;
184+
Ok(PyAsyncGenAThrow {
130185
ag: self,
131186
aclose: true,
132187
state: AtomicCell::new(AwaitableState::Init),
@@ -135,7 +190,7 @@ impl PyRef<PyAsyncGen> {
135190
vm.ctx.none(),
136191
vm.ctx.none(),
137192
),
138-
}
193+
})
139194
}
140195
}
141196

0 commit comments

Comments
 (0)