mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-25 01:02:15 +00:00
146 lines
3.5 KiB
Rust
146 lines
3.5 KiB
Rust
use std::ffi::CStr;
|
|
|
|
use pyo3::prelude::*;
|
|
use pyo3::types::PyDict;
|
|
use rstest::rstest;
|
|
|
|
use super::{PendingLogging, PendingSuccess};
|
|
use crate::PythonLogger;
|
|
use crate::test_support::{local, namespace, run};
|
|
|
|
/// A deferred success for the namespace's `logger` and `response`, bound as `pending`.
|
|
fn defer<'py>(py: Python<'py>, script: &CStr) -> Bound<'py, PyDict> {
|
|
let locals = namespace(py, c"response = object()");
|
|
run(py, &locals, script);
|
|
let pending = Py::new(
|
|
py,
|
|
PendingLogging {
|
|
pending: Some(PendingSuccess {
|
|
logger: PythonLogger::new(local(&locals, "logger").unbind()),
|
|
response: Some(local(&locals, "response").unbind()),
|
|
start: py.None(),
|
|
end: Some(py.None()),
|
|
}),
|
|
},
|
|
)
|
|
.unwrap();
|
|
locals.set_item("pending", pending).unwrap();
|
|
locals
|
|
}
|
|
|
|
#[test]
|
|
fn release_enqueues_the_success_once_in_the_releasing_context() {
|
|
Python::initialize();
|
|
Python::attach(|py| {
|
|
let locals = defer(
|
|
py,
|
|
c"
|
|
from contextvars import ContextVar
|
|
|
|
marker = ContextVar('marker', default='unset')
|
|
observed = []
|
|
|
|
def on_enqueue(coroutine):
|
|
observed.append(marker.get())
|
|
pending.release(True)
|
|
|
|
logger.on_enqueue = on_enqueue
|
|
",
|
|
);
|
|
run(
|
|
py,
|
|
&locals,
|
|
c"
|
|
marker.set('release')
|
|
pending.release(True)
|
|
pending.release(True)
|
|
assert observed == ['release'], observed
|
|
assert logger.names() == ['async_success_handler', 'enqueued'], logger.calls
|
|
assert logger.calls[0][1] is response
|
|
",
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn a_blocked_release_drops_the_success_for_good() {
|
|
Python::initialize();
|
|
Python::attach(|py| {
|
|
let locals = defer(py, c"");
|
|
run(
|
|
py,
|
|
&locals,
|
|
c"
|
|
pending.release(False)
|
|
pending.release(True)
|
|
assert logger.calls == [], logger.calls
|
|
",
|
|
);
|
|
});
|
|
}
|
|
|
|
#[rstest]
|
|
#[case::ordinary_error(c"RuntimeError('queue full')", false)]
|
|
#[case::cancellation(c"asyncio.CancelledError()", true)]
|
|
fn a_failed_enqueue_closes_the_coroutine_and_is_never_replayed(
|
|
#[case] failure: &CStr,
|
|
#[case] propagates: bool,
|
|
) {
|
|
Python::initialize();
|
|
Python::attach(|py| {
|
|
let locals = defer(
|
|
py,
|
|
c"
|
|
import asyncio
|
|
|
|
def on_enqueue(coroutine):
|
|
raise failure
|
|
|
|
logger.on_enqueue = on_enqueue
|
|
",
|
|
);
|
|
locals
|
|
.set_item("failure", py.eval(failure, None, Some(&locals)).unwrap())
|
|
.unwrap();
|
|
let released = local(&locals, "pending").call_method1("release", (true,));
|
|
match released {
|
|
Ok(_) => assert!(!propagates),
|
|
Err(error) => {
|
|
assert!(propagates);
|
|
assert!(error.value(py).is(local(&locals, "failure")));
|
|
}
|
|
}
|
|
locals.set_item("propagates", propagates).unwrap();
|
|
run(
|
|
py,
|
|
&locals,
|
|
c"
|
|
pending.release(True)
|
|
assert logger.names() == ['async_success_handler', 'enqueued', 'closed'], logger.calls
|
|
assert unraisable_from(logger) == ([] if propagates else [failure])
|
|
",
|
|
);
|
|
});
|
|
}
|
|
|
|
#[test]
|
|
fn an_unreleased_success_does_not_keep_its_logger_alive() {
|
|
Python::initialize();
|
|
Python::attach(|py| {
|
|
let locals = defer(py, c"");
|
|
run(
|
|
py,
|
|
&locals,
|
|
c"
|
|
import gc
|
|
import weakref
|
|
|
|
logger.pending = pending
|
|
reference = weakref.ref(logger)
|
|
del logger, pending
|
|
gc.collect()
|
|
assert reference() is None
|
|
",
|
|
);
|
|
});
|
|
}
|