nautilus_system/python/
registry.rs1use std::collections::HashMap;
19
20use nautilus_common::factories::{
21 ClientConfig, DataClientFactory, ExecutionClientFactory, SimulatedExecutionClientFactory,
22};
23use nautilus_core::python::to_pynotimplemented_err;
24use parking_lot::Mutex;
25use pyo3::prelude::*;
26
27pub type FactoryExtractor =
29 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn DataClientFactory>>;
30
31pub type ExecutionFactoryExtractor =
33 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn ExecutionClientFactory>>;
34
35pub type SimulatedExecutionFactoryExtractor =
37 fn(py: Python<'_>, factory: Py<PyAny>) -> PyResult<Box<dyn SimulatedExecutionClientFactory>>;
38
39pub type ConfigExtractor = fn(py: Python<'_>, config: Py<PyAny>) -> PyResult<Box<dyn ClientConfig>>;
41
42#[derive(Debug)]
48pub struct FactoryRegistry {
49 factory_extractors: Mutex<HashMap<String, FactoryExtractor>>,
50 exec_factory_extractors: Mutex<HashMap<String, ExecutionFactoryExtractor>>,
51 sim_exec_factory_extractors: Mutex<HashMap<String, SimulatedExecutionFactoryExtractor>>,
52 config_extractors_by_type: Mutex<HashMap<String, ConfigExtractor>>,
53}
54
55impl FactoryRegistry {
56 #[must_use]
58 pub fn new() -> Self {
59 Self {
60 factory_extractors: Mutex::new(HashMap::new()),
61 exec_factory_extractors: Mutex::new(HashMap::new()),
62 sim_exec_factory_extractors: Mutex::new(HashMap::new()),
63 config_extractors_by_type: Mutex::new(HashMap::new()),
64 }
65 }
66
67 pub fn register_factory_extractor(
73 &self,
74 name: String,
75 extractor: FactoryExtractor,
76 ) -> anyhow::Result<()> {
77 let mut extractors = self.factory_extractors.lock();
78
79 if extractors.contains_key(&name) {
80 anyhow::bail!("Factory extractor '{name}' is already registered");
81 }
82 extractors.insert(name, extractor);
83 Ok(())
84 }
85
86 pub fn register_config_extractor(
92 &self,
93 type_name: String,
94 extractor: ConfigExtractor,
95 ) -> anyhow::Result<()> {
96 let mut extractors = self.config_extractors_by_type.lock();
97
98 if extractors.contains_key(&type_name) {
99 anyhow::bail!("Config extractor '{type_name}' is already registered");
100 }
101
102 extractors.insert(type_name, extractor);
103 Ok(())
104 }
105
106 pub fn register_exec_factory_extractor(
112 &self,
113 name: String,
114 extractor: ExecutionFactoryExtractor,
115 ) -> anyhow::Result<()> {
116 let mut extractors = self.exec_factory_extractors.lock();
117
118 if extractors.contains_key(&name) {
119 anyhow::bail!("Execution factory extractor '{name}' is already registered");
120 }
121 extractors.insert(name, extractor);
122 Ok(())
123 }
124
125 pub fn register_sim_exec_factory_extractor(
131 &self,
132 name: String,
133 extractor: SimulatedExecutionFactoryExtractor,
134 ) -> anyhow::Result<()> {
135 let mut extractors = self.sim_exec_factory_extractors.lock();
136
137 if extractors.contains_key(&name) {
138 anyhow::bail!("Simulated execution factory extractor '{name}' is already registered");
139 }
140 extractors.insert(name, extractor);
141 Ok(())
142 }
143
144 pub fn extract_factory(
150 &self,
151 py: Python<'_>,
152 factory: Py<PyAny>,
153 ) -> PyResult<Box<dyn DataClientFactory>> {
154 let factory_name = factory
156 .getattr(py, "name")?
157 .call0(py)?
158 .extract::<String>(py)?;
159
160 let extractors = self.factory_extractors.lock();
161 if let Some(extractor) = extractors.get(&factory_name) {
162 extractor(py, factory)
163 } else {
164 Err(to_pynotimplemented_err(format!(
165 "No factory extractor registered for '{factory_name}'"
166 )))
167 }
168 }
169
170 pub fn extract_exec_factory(
176 &self,
177 py: Python<'_>,
178 factory: Py<PyAny>,
179 ) -> PyResult<Box<dyn ExecutionClientFactory>> {
180 let factory_name = factory
181 .getattr(py, "name")?
182 .call0(py)?
183 .extract::<String>(py)?;
184
185 let extractors = self.exec_factory_extractors.lock();
186 if let Some(extractor) = extractors.get(&factory_name) {
187 extractor(py, factory)
188 } else {
189 Err(to_pynotimplemented_err(format!(
190 "No execution factory extractor registered for '{factory_name}'"
191 )))
192 }
193 }
194
195 pub fn extract_sim_exec_factory(
201 &self,
202 py: Python<'_>,
203 factory: Py<PyAny>,
204 ) -> PyResult<Box<dyn SimulatedExecutionClientFactory>> {
205 let factory_name = factory
206 .getattr(py, "name")?
207 .call0(py)?
208 .extract::<String>(py)?;
209
210 let extractors = self.sim_exec_factory_extractors.lock();
211
212 if let Some(extractor) = extractors.get(&factory_name) {
213 extractor(py, factory)
214 } else {
215 Err(to_pynotimplemented_err(format!(
216 "No simulated execution factory extractor registered for '{factory_name}'"
217 )))
218 }
219 }
220
221 pub fn extract_config(
227 &self,
228 py: Python<'_>,
229 config: Py<PyAny>,
230 ) -> PyResult<Box<dyn ClientConfig>> {
231 let config_type_name = config
233 .getattr(py, "__class__")?
234 .getattr(py, "__name__")?
235 .extract::<String>(py)?;
236
237 let extractors = self.config_extractors_by_type.lock();
238 if let Some(extractor) = extractors.get(&config_type_name) {
239 extractor(py, config)
240 } else {
241 Err(to_pynotimplemented_err(format!(
242 "No config extractor registered for '{config_type_name}'"
243 )))
244 }
245 }
246}
247
248impl Default for FactoryRegistry {
249 fn default() -> Self {
250 Self::new()
251 }
252}
253
254static GLOBAL_PYO3_REGISTRY: std::sync::LazyLock<FactoryRegistry> =
256 std::sync::LazyLock::new(FactoryRegistry::new);
257
258#[must_use]
260pub fn get_global_pyo3_registry() -> &'static FactoryRegistry {
261 &GLOBAL_PYO3_REGISTRY
262}