nautilus_system/python/
registry.rs1use std::{collections::HashMap, sync::Mutex};
19
20use nautilus_common::factories::{
21 ClientConfig, DataClientFactory, ExecutionClientFactory, SimulatedExecutionClientFactory,
22};
23use nautilus_core::{MUTEX_POISONED, python::to_pynotimplemented_err};
24use pyo3::prelude::*;
25
26pub type FactoryExtractor =
28 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn DataClientFactory>>;
29
30pub type ExecFactoryExtractor =
32 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn ExecutionClientFactory>>;
33
34pub type SimExecFactoryExtractor =
36 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn SimulatedExecutionClientFactory>>;
37
38pub type ConfigExtractor = fn(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>>;
40
41#[derive(Debug)]
47pub struct FactoryRegistry {
48 factory_extractors: Mutex<HashMap<String, FactoryExtractor>>,
49 exec_factory_extractors: Mutex<HashMap<String, ExecFactoryExtractor>>,
50 sim_exec_factory_extractors: Mutex<HashMap<String, SimExecFactoryExtractor>>,
51 config_extractors_by_type: Mutex<HashMap<String, ConfigExtractor>>,
52}
53
54impl FactoryRegistry {
55 #[must_use]
57 pub fn new() -> Self {
58 Self {
59 factory_extractors: Mutex::new(HashMap::new()),
60 exec_factory_extractors: Mutex::new(HashMap::new()),
61 sim_exec_factory_extractors: Mutex::new(HashMap::new()),
62 config_extractors_by_type: Mutex::new(HashMap::new()),
63 }
64 }
65
66 pub fn register_factory_extractor(
76 &self,
77 name: String,
78 extractor: FactoryExtractor,
79 ) -> anyhow::Result<()> {
80 let mut extractors = self.factory_extractors.lock().expect(MUTEX_POISONED);
81
82 if extractors.contains_key(&name) {
83 anyhow::bail!("Factory extractor '{name}' is already registered");
84 }
85 extractors.insert(name, extractor);
86 Ok(())
87 }
88
89 pub fn register_config_extractor(
99 &self,
100 type_name: String,
101 extractor: ConfigExtractor,
102 ) -> anyhow::Result<()> {
103 let mut extractors = self.config_extractors_by_type.lock().expect(MUTEX_POISONED);
104
105 if extractors.contains_key(&type_name) {
106 anyhow::bail!("Config extractor '{type_name}' is already registered");
107 }
108
109 extractors.insert(type_name, extractor);
110 Ok(())
111 }
112
113 pub fn register_exec_factory_extractor(
123 &self,
124 name: String,
125 extractor: ExecFactoryExtractor,
126 ) -> anyhow::Result<()> {
127 let mut extractors = self.exec_factory_extractors.lock().expect(MUTEX_POISONED);
128
129 if extractors.contains_key(&name) {
130 anyhow::bail!("Execution factory extractor '{name}' is already registered");
131 }
132 extractors.insert(name, extractor);
133 Ok(())
134 }
135
136 pub fn register_sim_exec_factory_extractor(
146 &self,
147 name: String,
148 extractor: SimExecFactoryExtractor,
149 ) -> anyhow::Result<()> {
150 let mut extractors = self
151 .sim_exec_factory_extractors
152 .lock()
153 .expect(MUTEX_POISONED);
154
155 if extractors.contains_key(&name) {
156 anyhow::bail!("Simulated execution factory extractor '{name}' is already registered");
157 }
158 extractors.insert(name, extractor);
159 Ok(())
160 }
161
162 pub fn extract_factory(
172 &self,
173 py: Python<'_>,
174 factory: Py<PyAny>,
175 ) -> PyResult<Box<dyn DataClientFactory>> {
176 let factory_name = factory
178 .getattr(py, "name")?
179 .call0(py)?
180 .extract::<String>(py)?;
181
182 let extractors = self.factory_extractors.lock().expect(MUTEX_POISONED);
183 if let Some(extractor) = extractors.get(&factory_name) {
184 extractor(py, factory)
185 } else {
186 Err(to_pynotimplemented_err(format!(
187 "No factory extractor registered for '{factory_name}'"
188 )))
189 }
190 }
191
192 pub fn extract_exec_factory(
202 &self,
203 py: Python<'_>,
204 factory: Py<PyAny>,
205 ) -> PyResult<Box<dyn ExecutionClientFactory>> {
206 let factory_name = factory
207 .getattr(py, "name")?
208 .call0(py)?
209 .extract::<String>(py)?;
210
211 let extractors = self.exec_factory_extractors.lock().expect(MUTEX_POISONED);
212 if let Some(extractor) = extractors.get(&factory_name) {
213 extractor(py, factory)
214 } else {
215 Err(to_pynotimplemented_err(format!(
216 "No execution factory extractor registered for '{factory_name}'"
217 )))
218 }
219 }
220
221 pub fn extract_sim_exec_factory(
231 &self,
232 py: Python<'_>,
233 factory: Py<PyAny>,
234 ) -> PyResult<Box<dyn SimulatedExecutionClientFactory>> {
235 let factory_name = factory
236 .getattr(py, "name")?
237 .call0(py)?
238 .extract::<String>(py)?;
239
240 let extractors = self
241 .sim_exec_factory_extractors
242 .lock()
243 .expect(MUTEX_POISONED);
244
245 if let Some(extractor) = extractors.get(&factory_name) {
246 extractor(py, factory)
247 } else {
248 Err(to_pynotimplemented_err(format!(
249 "No simulated execution factory extractor registered for '{factory_name}'"
250 )))
251 }
252 }
253
254 pub fn extract_config(
264 &self,
265 py: Python<'_>,
266 config: Py<PyAny>,
267 ) -> PyResult<Box<dyn ClientConfig>> {
268 let config_type_name = config
270 .getattr(py, "__class__")?
271 .getattr(py, "__name__")?
272 .extract::<String>(py)?;
273
274 let extractors = self.config_extractors_by_type.lock().expect(MUTEX_POISONED);
275 if let Some(extractor) = extractors.get(&config_type_name) {
276 extractor(py, config)
277 } else {
278 Err(to_pynotimplemented_err(format!(
279 "No config extractor registered for '{config_type_name}'"
280 )))
281 }
282 }
283}
284
285impl Default for FactoryRegistry {
286 fn default() -> Self {
287 Self::new()
288 }
289}
290
291static GLOBAL_PYO3_REGISTRY: std::sync::LazyLock<FactoryRegistry> =
293 std::sync::LazyLock::new(FactoryRegistry::new);
294
295#[must_use]
297pub fn get_global_pyo3_registry() -> &'static FactoryRegistry {
298 &GLOBAL_PYO3_REGISTRY
299}