mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-27 01:22:18 +00:00
fix(rust): preserve nested optional import failures (#43265)
* fix(rust): preserve nested optional import failures Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> * test(rust): restore Python modules after settings tests Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --------- Co-authored-by: Yujong Lee <yujong@berri.ai> Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
parent
2530255624
commit
e1a9378093
1 changed files with 172 additions and 3 deletions
|
|
@ -45,8 +45,13 @@ impl PythonSettings {
|
|||
pub(crate) fn read_or_unset(self, py: Python<'_>) -> PyResult<Option<Snapshot<'_>>> {
|
||||
match self.read(py) {
|
||||
Ok(snapshot) => Ok(Some(snapshot)),
|
||||
Err(error) if error.is_instance_of::<PyModuleNotFoundError>(py) => Ok(None),
|
||||
Err(error) => Err(error),
|
||||
Err(error) => {
|
||||
if missing_module(py, &error, "litellm")? {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,9 +61,24 @@ impl PythonSettings {
|
|||
}
|
||||
}
|
||||
|
||||
fn missing_module(py: Python<'_>, error: &PyErr, expected: &str) -> PyResult<bool> {
|
||||
if !error.is_instance_of::<PyModuleNotFoundError>(py) {
|
||||
return Ok(false);
|
||||
}
|
||||
Ok(error
|
||||
.value(py)
|
||||
.getattr("name")?
|
||||
.extract::<Option<String>>()?
|
||||
.is_some_and(|name| name == expected))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use pyo3::{exceptions::PyRuntimeError, prelude::*, types::PyDict};
|
||||
use pyo3::{
|
||||
exceptions::{PyImportError, PyModuleNotFoundError, PyRuntimeError},
|
||||
prelude::*,
|
||||
types::PyDict,
|
||||
};
|
||||
|
||||
use super::PythonSettings;
|
||||
use crate::coercion::FieldSpec;
|
||||
|
|
@ -150,4 +170,153 @@ values = (Descriptor(), SimpleNamespace(flag=Truth()))
|
|||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_or_unset_returns_none_when_litellm_is_missing() {
|
||||
Python::initialize();
|
||||
Python::attach(|py| {
|
||||
let locals = PyDict::new(py);
|
||||
py.run(
|
||||
c"
|
||||
import sys
|
||||
class MissingLitellm:
|
||||
def find_spec(self, fullname, path=None, target=None):
|
||||
if fullname == 'litellm':
|
||||
raise ModuleNotFoundError('No module named litellm', name='litellm')
|
||||
finder = MissingLitellm()
|
||||
previous_litellm = sys.modules.get('litellm')
|
||||
had_litellm = 'litellm' in sys.modules
|
||||
sys.meta_path.insert(0, finder)
|
||||
sys.modules.pop('litellm', None)
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
let result = PythonSettings::Http.read_or_unset(py);
|
||||
assert!(result.unwrap().is_none());
|
||||
py.run(
|
||||
c"
|
||||
sys.meta_path.remove(finder)
|
||||
if had_litellm:
|
||||
sys.modules['litellm'] = previous_litellm
|
||||
else:
|
||||
sys.modules.pop('litellm', None)
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_or_unset_propagates_nested_module_not_found_errors() {
|
||||
Python::initialize();
|
||||
Python::attach(|py| {
|
||||
let locals = PyDict::new(py);
|
||||
py.run(
|
||||
c"
|
||||
import sys
|
||||
import types
|
||||
previous_modules = {
|
||||
name: sys.modules[name]
|
||||
for name in ('litellm', 'litellm.rust_bridge', 'litellm.rust_bridge.settings')
|
||||
if name in sys.modules
|
||||
}
|
||||
litellm = types.ModuleType('litellm')
|
||||
litellm.__path__ = []
|
||||
rust_bridge = types.ModuleType('litellm.rust_bridge')
|
||||
rust_bridge.__path__ = []
|
||||
settings = types.ModuleType('litellm.rust_bridge.settings')
|
||||
def http_settings():
|
||||
raise ModuleNotFoundError('No module named certifi', name='certifi')
|
||||
settings.http_settings = http_settings
|
||||
litellm.rust_bridge = rust_bridge
|
||||
rust_bridge.settings = settings
|
||||
sys.modules['litellm'] = litellm
|
||||
sys.modules['litellm.rust_bridge'] = rust_bridge
|
||||
sys.modules['litellm.rust_bridge.settings'] = settings
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
let error = match PythonSettings::Http.read_or_unset(py) {
|
||||
Ok(_) => panic!("nested module errors must propagate"),
|
||||
Err(error) => error,
|
||||
};
|
||||
assert!(error.is_instance_of::<PyModuleNotFoundError>(py));
|
||||
assert_eq!(
|
||||
error
|
||||
.value(py)
|
||||
.getattr("name")
|
||||
.unwrap()
|
||||
.extract::<String>()
|
||||
.unwrap(),
|
||||
"certifi"
|
||||
);
|
||||
py.run(
|
||||
c"
|
||||
for name in ('litellm.rust_bridge.settings', 'litellm.rust_bridge', 'litellm'):
|
||||
sys.modules.pop(name, None)
|
||||
sys.modules.update(previous_modules)
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_or_unset_propagates_import_errors() {
|
||||
Python::initialize();
|
||||
Python::attach(|py| {
|
||||
let locals = PyDict::new(py);
|
||||
py.run(
|
||||
c"
|
||||
import sys
|
||||
import types
|
||||
previous_modules = {
|
||||
name: sys.modules[name]
|
||||
for name in ('litellm', 'litellm.rust_bridge', 'litellm.rust_bridge.settings')
|
||||
if name in sys.modules
|
||||
}
|
||||
litellm = types.ModuleType('litellm')
|
||||
litellm.__path__ = []
|
||||
rust_bridge = types.ModuleType('litellm.rust_bridge')
|
||||
rust_bridge.__path__ = []
|
||||
settings = types.ModuleType('litellm.rust_bridge.settings')
|
||||
def http_settings():
|
||||
raise ImportError('cannot import name setting')
|
||||
settings.http_settings = http_settings
|
||||
litellm.rust_bridge = rust_bridge
|
||||
rust_bridge.settings = settings
|
||||
sys.modules['litellm'] = litellm
|
||||
sys.modules['litellm.rust_bridge'] = rust_bridge
|
||||
sys.modules['litellm.rust_bridge.settings'] = settings
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
let error = match PythonSettings::Http.read_or_unset(py) {
|
||||
Ok(_) => panic!("import errors must propagate"),
|
||||
Err(error) => error,
|
||||
};
|
||||
assert!(error.is_instance_of::<PyImportError>(py));
|
||||
assert_eq!(error.to_string(), "ImportError: cannot import name setting");
|
||||
py.run(
|
||||
c"
|
||||
for name in ('litellm.rust_bridge.settings', 'litellm.rust_bridge', 'litellm'):
|
||||
sys.modules.pop(name, None)
|
||||
sys.modules.update(previous_modules)
|
||||
",
|
||||
Some(&locals),
|
||||
Some(&locals),
|
||||
)
|
||||
.unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue