pyo3_object_store/
store.rs1use std::sync::Arc;
2
3use object_store::ObjectStore;
4use pyo3::exceptions::{PyRuntimeWarning, PyValueError};
5use pyo3::prelude::*;
6use pyo3::pybacked::PyBackedStr;
7use pyo3::types::{PyDict, PyTuple};
8use pyo3::{intern, PyTypeInfo};
9
10use crate::{PyAzureStore, PyGCSStore, PyHttpStore, PyLocalStore, PyMemoryStore, PyS3Store};
11
12pub struct PyObjectStore(Arc<dyn ObjectStore>);
18
19impl<'py> FromPyObject<'_, 'py> for PyObjectStore {
20 type Error = PyErr;
21
22 fn extract(obj: Borrowed<'_, 'py, pyo3::PyAny>) -> PyResult<Self> {
23 if let Ok(store) = obj.cast::<PyS3Store>() {
24 Ok(Self(store.get().as_ref().clone()))
25 } else if let Ok(store) = obj.cast::<PyAzureStore>() {
26 Ok(Self(store.get().as_ref().clone()))
27 } else if let Ok(store) = obj.cast::<PyGCSStore>() {
28 Ok(Self(store.get().as_ref().clone()))
29 } else if let Ok(store) = obj.cast::<PyHttpStore>() {
30 Ok(Self(store.get().as_ref().clone()))
31 } else if let Ok(store) = obj.cast::<PyLocalStore>() {
32 Ok(Self(store.get().as_ref().clone()))
33 } else if let Ok(store) = obj.cast::<PyMemoryStore>() {
34 Ok(Self(store.get().as_ref().clone()))
35 } else {
36 let py = obj.py();
37 let cls_name = obj
39 .getattr(intern!(py, "__class__"))?
40 .getattr(intern!(py, "__name__"))?
41 .extract::<PyBackedStr>()?;
42 if [
43 PyAzureStore::NAME,
44 PyGCSStore::NAME,
45 PyHttpStore::NAME,
46 PyLocalStore::NAME,
47 PyMemoryStore::NAME,
48 PyS3Store::NAME,
49 ]
50 .contains(&cls_name.as_ref())
51 {
52 return Err(PyValueError::new_err("You must use an object store instance exported from **the same library** as this function. They cannot be used across libraries.\nThis is because object store instances are compiled with a specific version of Rust and Python." ));
53 }
54
55 Err(PyValueError::new_err(format!(
56 "Expected an object store instance, got {}",
57 obj.repr()?
58 )))
59 }
60 }
61}
62
63impl AsRef<Arc<dyn ObjectStore>> for PyObjectStore {
64 fn as_ref(&self) -> &Arc<dyn ObjectStore> {
65 &self.0
66 }
67}
68
69impl From<PyObjectStore> for Arc<dyn ObjectStore> {
70 fn from(value: PyObjectStore) -> Self {
71 value.0
72 }
73}
74
75impl PyObjectStore {
76 pub fn into_inner(self) -> Arc<dyn ObjectStore> {
78 self.0
79 }
80
81 pub fn into_dyn(self) -> Arc<dyn ObjectStore> {
83 self.0
84 }
85}
86
87#[derive(Debug, Clone)]
88struct PyExternalObjectStoreInner(Arc<dyn ObjectStore>);
89
90impl<'py> FromPyObject<'_, 'py> for PyExternalObjectStoreInner {
91 type Error = PyErr;
92
93 fn extract(obj: Borrowed<'_, 'py, pyo3::PyAny>) -> PyResult<Self> {
94 let py = obj.py();
95 let cls_name = obj
97 .getattr(intern!(py, "__class__"))?
98 .getattr(intern!(py, "__name__"))?
99 .extract::<PyBackedStr>()?;
100
101 if cls_name == PyAzureStore::NAME {
102 let (args, kwargs): (Bound<PyTuple>, Bound<PyDict>) = obj
103 .call_method0(intern!(py, "__getnewargs_ex__"))?
104 .extract()?;
105 let store = PyAzureStore::type_object(py)
106 .call(args, Some(&kwargs))?
107 .cast::<PyAzureStore>()?
108 .get()
109 .clone();
110 return Ok(Self(store.into_inner()));
111 }
112
113 if cls_name == PyGCSStore::NAME {
114 let (args, kwargs): (Bound<PyTuple>, Bound<PyDict>) = obj
115 .call_method0(intern!(py, "__getnewargs_ex__"))?
116 .extract()?;
117 let store = PyGCSStore::type_object(py)
118 .call(args, Some(&kwargs))?
119 .cast::<PyGCSStore>()?
120 .get()
121 .clone();
122 return Ok(Self(store.into_inner()));
123 }
124
125 if cls_name == PyHttpStore::NAME {
126 let (args, kwargs): (Bound<PyTuple>, Bound<PyDict>) = obj
127 .call_method0(intern!(py, "__getnewargs_ex__"))?
128 .extract()?;
129 let store = PyHttpStore::type_object(py)
130 .call(args, Some(&kwargs))?
131 .cast::<PyHttpStore>()?
132 .get()
133 .clone();
134 return Ok(Self(store.into_inner()));
135 }
136
137 if cls_name == PyLocalStore::NAME {
138 let (args, kwargs): (Bound<PyTuple>, Bound<PyDict>) = obj
139 .call_method0(intern!(py, "__getnewargs_ex__"))?
140 .extract()?;
141 let store = PyLocalStore::type_object(py)
142 .call(args, Some(&kwargs))?
143 .cast::<PyLocalStore>()?
144 .get()
145 .clone();
146 return Ok(Self(store.into_inner()));
147 }
148
149 if cls_name == PyS3Store::NAME {
150 let (args, kwargs): (Bound<PyTuple>, Bound<PyDict>) = obj
151 .call_method0(intern!(py, "__getnewargs_ex__"))?
152 .extract()?;
153 let store = PyS3Store::type_object(py)
154 .call(args, Some(&kwargs))?
155 .cast::<PyS3Store>()?
156 .get()
157 .clone();
158 return Ok(Self(store.into_inner()));
159 }
160
161 Err(PyValueError::new_err(format!(
162 "Expected an object store-compatible instance, got {}",
163 obj.repr()?
164 )))
165 }
166}
167
168#[derive(Debug, Clone)]
194pub struct PyExternalObjectStore(PyExternalObjectStoreInner);
195
196impl From<PyExternalObjectStore> for Arc<dyn ObjectStore> {
197 fn from(value: PyExternalObjectStore) -> Self {
198 value.0 .0
199 }
200}
201
202impl PyExternalObjectStore {
203 pub fn into_dyn(self) -> Arc<dyn ObjectStore> {
205 self.into()
206 }
207}
208
209impl<'py> FromPyObject<'_, 'py> for PyExternalObjectStore {
210 type Error = PyErr;
211
212 fn extract(obj: Borrowed<'_, 'py, pyo3::PyAny>) -> PyResult<Self> {
213 match obj.extract() {
214 Ok(inner) => {
215 #[cfg(feature = "external-store-warning")]
216 {
217 let py = obj.py();
218
219 let warnings_mod = py.import(intern!(py, "warnings"))?;
220 let warning = PyRuntimeWarning::new_err(
221 "Successfully reconstructed a store defined in another Python module. Connection pooling will not be shared across store instances.",
222 );
223 let args = PyTuple::new(py, vec![warning])?;
224 warnings_mod.call_method1(intern!(py, "warn"), args)?;
225 }
226 Ok(Self(inner))
227 }
228 Err(err) => Err(err),
229 }
230 }
231}
232
233#[derive(FromPyObject)]
239pub enum AnyObjectStore {
240 PyObjectStore(PyObjectStore),
242 PyExternalObjectStore(PyExternalObjectStore),
244}
245
246impl From<AnyObjectStore> for Arc<dyn ObjectStore> {
247 fn from(value: AnyObjectStore) -> Self {
248 match value {
249 AnyObjectStore::PyObjectStore(store) => store.into(),
250 AnyObjectStore::PyExternalObjectStore(store) => store.into(),
251 }
252 }
253}
254
255impl AnyObjectStore {
256 pub fn into_dyn(self) -> Arc<dyn ObjectStore> {
258 self.into()
259 }
260}