Skip to content

Commit b4608e8

Browse files
author
Andrew J Westlake
committed
Cleaned up some unwrap calls
1 parent e8e4512 commit b4608e8

File tree

1 file changed

+57
-24
lines changed

1 file changed

+57
-24
lines changed

src/lib.rs

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ lazy_static! {
9898
};
9999
}
100100

101+
const EXPECT_INIT: &'static str = "PyO3 Asyncio has not been initialized";
102+
103+
static ASYNCIO: OnceCell<PyObject> = OnceCell::new();
101104
static EVENT_LOOP: OnceCell<PyObject> = OnceCell::new();
102105
static EXECUTOR: OnceCell<PyObject> = OnceCell::new();
103106
static CALL_SOON: OnceCell<PyObject> = OnceCell::new();
@@ -161,6 +164,7 @@ fn try_init(py: Python) -> PyResult<()> {
161164
let create_task = asyncio.getattr("run_coroutine_threadsafe")?;
162165
let create_future = event_loop.getattr("create_future")?;
163166

167+
ASYNCIO.get_or_init(|| asyncio.into());
164168
EVENT_LOOP.get_or_init(|| event_loop.into());
165169
EXECUTOR.get_or_init(|| executor.into());
166170
CALL_SOON.get_or_init(|| call_soon.into());
@@ -176,10 +180,7 @@ fn try_init(py: Python) -> PyResult<()> {
176180

177181
/// Get a reference to the Python Event Loop from Rust
178182
pub fn get_event_loop(py: Python) -> &PyAny {
179-
EVENT_LOOP
180-
.get()
181-
.expect("PyO3 Asyncio Event Loop has not been initialized")
182-
.as_ref(py)
183+
EVENT_LOOP.get().expect(EXPECT_INIT).as_ref(py)
183184
}
184185

185186
/// Run the event loop forever
@@ -227,7 +228,7 @@ pub fn get_event_loop(py: Python) -> &PyAny {
227228
/// # .unwrap();
228229
/// # })
229230
pub fn run_forever(py: Python) -> PyResult<()> {
230-
if let Err(e) = EVENT_LOOP.get().unwrap().call_method0(py, "run_forever") {
231+
if let Err(e) = get_event_loop(py).call_method0("run_forever") {
231232
if e.is_instance::<PyKeyboardInterrupt>(py) {
232233
Ok(())
233234
} else {
@@ -279,21 +280,21 @@ where
279280
Ok(Python::with_gil(|py| py.None()))
280281
})?;
281282

282-
EVENT_LOOP
283-
.get()
284-
.unwrap()
285-
.call_method1(py, "run_until_complete", (coro,))?;
283+
get_event_loop(py).call_method1("run_until_complete", (coro,))?;
286284

287285
Ok(())
288286
}
289287

290288
/// Shutdown the event loops and perform any necessary cleanup
291289
fn try_close(py: Python) -> PyResult<()> {
292290
// Shutdown the executor and wait until all threads are cleaned up
293-
EXECUTOR.get().unwrap().call_method0(py, "shutdown")?;
291+
EXECUTOR
292+
.get()
293+
.expect(EXPECT_INIT)
294+
.call_method0(py, "shutdown")?;
294295

295-
EVENT_LOOP.get().unwrap().call_method0(py, "stop")?;
296-
EVENT_LOOP.get().unwrap().call_method0(py, "close")?;
296+
get_event_loop(py).call_method0("stop")?;
297+
get_event_loop(py).call_method0("close")?;
297298
Ok(())
298299
}
299300

@@ -399,8 +400,14 @@ impl PyTaskCompleter {
399400
Err(e) => Err(e),
400401
};
401402

402-
if self.tx.take().unwrap().send(result).is_err() {
403-
// cancellation is not an error
403+
// unclear to me whether or not this should be a panic or silent error.
404+
//
405+
// calling PyTaskCompleter twice should not be possible, but I don't think it really hurts
406+
// anything if it happens.
407+
if let Some(tx) = self.tx.take() {
408+
if tx.send(result).is_err() {
409+
// cancellation is not an error
410+
}
404411
}
405412

406413
Ok(())
@@ -460,24 +467,43 @@ pub fn into_future(
460467

461468
let task = CREATE_TASK
462469
.get()
463-
.unwrap()
464-
.call1(py, (coro, EVENT_LOOP.get().unwrap()))?;
470+
.expect(EXPECT_INIT)
471+
.call1(py, (coro, get_event_loop(py)))?;
465472
let on_complete = PyTaskCompleter { tx: Some(tx) };
466473

467474
task.call_method1(py, "add_done_callback", (on_complete,))?;
468475

469-
Ok(async move { rx.await.unwrap() })
476+
Ok(async move {
477+
match rx.await {
478+
Ok(item) => item,
479+
Err(_) => Python::with_gil(|py| {
480+
Err(PyErr::from_instance(
481+
ASYNCIO
482+
.get()
483+
.expect(EXPECT_INIT)
484+
.call_method0(py, "CancelledError")?
485+
.as_ref(py),
486+
))
487+
}),
488+
}
489+
})
470490
}
471491

472492
fn set_result(py: Python, future: &PyAny, result: PyResult<PyObject>) -> PyResult<()> {
473493
match result {
474494
Ok(val) => {
475495
let set_result = future.getattr("set_result")?;
476-
CALL_SOON.get().unwrap().call1(py, (set_result, val))?;
496+
CALL_SOON
497+
.get()
498+
.expect(EXPECT_INIT)
499+
.call1(py, (set_result, val))?;
477500
}
478501
Err(err) => {
479502
let set_exception = future.getattr("set_exception")?;
480-
CALL_SOON.get().unwrap().call1(py, (set_exception, err))?;
503+
CALL_SOON
504+
.get()
505+
.expect(EXPECT_INIT)
506+
.call1(py, (set_exception, err))?;
481507
}
482508
}
483509

@@ -520,7 +546,7 @@ pub fn into_coroutine<F>(py: Python, fut: F) -> PyResult<PyObject>
520546
where
521547
F: Future<Output = PyResult<PyObject>> + Send + 'static,
522548
{
523-
let future_rx = CREATE_FUTURE.get().unwrap().call0(py)?;
549+
let future_rx = CREATE_FUTURE.get().expect(EXPECT_INIT).call0(py)?;
524550
let future_tx1 = future_rx.clone();
525551
let future_tx2 = future_rx.clone();
526552

@@ -529,22 +555,29 @@ where
529555
let result = fut.await;
530556

531557
Python::with_gil(move |py| {
532-
set_result(py, future_tx1.as_ref(py), result)
558+
if set_result(py, future_tx1.as_ref(py), result)
533559
.map_err(dump_err(py))
534-
.unwrap()
560+
.is_err()
561+
{
562+
563+
// Cancelled
564+
}
535565
});
536566
})
537567
.await
538568
{
539569
if e.is_panic() {
540570
Python::with_gil(move |py| {
541-
set_result(
571+
if set_result(
542572
py,
543573
future_tx2.as_ref(py),
544574
Err(PyException::new_err("rust future panicked")),
545575
)
546576
.map_err(dump_err(py))
547-
.unwrap()
577+
.is_err()
578+
{
579+
// Cancelled
580+
}
548581
});
549582
}
550583
}

0 commit comments

Comments
 (0)