nautilus_infrastructure/python/
mod.rs1#[cfg(feature = "redis")]
19pub mod redis;
20
21#[cfg(feature = "postgres")]
22pub mod sql;
23
24use pyo3::{prelude::*, pymodule};
25
26#[pymodule]
32pub fn infrastructure(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
33 #[cfg(not(any(feature = "redis", feature = "postgres")))]
34 let _ = m;
35
36 #[cfg(feature = "redis")]
37 m.add_class::<crate::redis::cache::RedisCacheConfig>()?;
38 #[cfg(feature = "redis")]
39 m.add_class::<crate::redis::cache::RedisCacheDatabase>()?;
40 #[cfg(feature = "redis")]
41 m.add_class::<redis::msgbus::PyRedisMessageBusBacking>()?;
42 #[cfg(feature = "redis")]
43 m.add_class::<redis::msgbus::PyRedisMessageBusFactory>()?;
44 #[cfg(feature = "redis")]
45 m.add_class::<crate::redis::msgbus::RedisMessageBusConfig>()?;
46 #[cfg(feature = "redis")]
47 redis::msgbus::register_redis_msgbus_factory()?;
48 #[cfg(feature = "redis")]
49 redis::cache::register_redis_cache_database_factory()?;
50 #[cfg(feature = "postgres")]
51 m.add_class::<crate::sql::cache::PostgresCacheConfig>()?;
52 #[cfg(feature = "postgres")]
53 m.add_class::<crate::sql::cache::PostgresCacheDatabase>()?;
54 #[cfg(feature = "postgres")]
55 m.add_class::<crate::sql::pg::PostgresConnectOptions>()?;
56 #[cfg(feature = "postgres")]
57 sql::cache::register_postgres_cache_database_factory()?;
58 Ok(())
59}
60
61#[cfg(all(test, any(feature = "redis", feature = "postgres")))]
62mod tests {
63 use nautilus_common::python::cache::get_global_cache_database_factory_registry;
64 #[cfg(feature = "redis")]
65 use nautilus_common::python::msgbus::get_global_msgbus_factory_registry;
66 use pyo3::PyRef;
67 use rstest::rstest;
68
69 use super::*;
70
71 #[cfg(feature = "redis")]
72 #[rstest]
73 fn test_infrastructure_module_extracts_redis_cache_database_factory() {
74 Python::initialize();
75 Python::attach(|py| {
76 let module = PyModule::new(py, "infrastructure").unwrap();
77
78 infrastructure(py, &module).unwrap();
79
80 let config = module
81 .getattr("RedisCacheConfig")
82 .unwrap()
83 .call1((
84 "redis.example.com",
85 6380,
86 "user",
87 "secret",
88 true,
89 7,
90 8,
91 9,
92 3,
93 10,
94 4,
95 ))
96 .unwrap();
97 {
98 let config = config
99 .extract::<PyRef<crate::redis::cache::RedisCacheConfig>>()
100 .unwrap();
101
102 assert_eq!(config.host.as_deref(), Some("redis.example.com"));
103 assert_eq!(config.port, Some(6380));
104 assert_eq!(config.username.as_deref(), Some("user"));
105 assert_eq!(config.password.as_deref(), Some("secret"));
106 assert!(config.ssl);
107 assert_eq!(config.connection_timeout, 7);
108 assert_eq!(config.response_timeout, 8);
109 assert_eq!(config.number_of_retries, 9);
110 assert_eq!(config.exponent_base, 3);
111 assert_eq!(config.max_delay, 10);
112 assert_eq!(config.factor, 4);
113 }
114 let factory = get_global_cache_database_factory_registry()
115 .extract(py, config.unbind())
116 .unwrap();
117 let debug = format!("{factory:?}");
118
119 assert!(debug.contains("redis.example.com"));
120 assert!(debug.contains("password: Some(\"***\")"));
121 assert!(!debug.contains("secret"));
122 });
123 }
124
125 #[cfg(feature = "postgres")]
126 #[rstest]
127 fn test_infrastructure_module_extracts_postgres_cache_database_factory() {
128 Python::initialize();
129 Python::attach(|py| {
130 let module = PyModule::new(py, "infrastructure").unwrap();
131
132 infrastructure(py, &module).unwrap();
133
134 let config = module
135 .getattr("PostgresCacheConfig")
136 .unwrap()
137 .call1(("postgres.example.com", 5433, "user", "secret", "nautilus"))
138 .unwrap();
139 {
140 let config = config
141 .extract::<PyRef<crate::sql::cache::PostgresCacheConfig>>()
142 .unwrap();
143
144 assert_eq!(config.host.as_deref(), Some("postgres.example.com"));
145 assert_eq!(config.port, Some(5433));
146 assert_eq!(config.username.as_deref(), Some("user"));
147 assert_eq!(config.password.as_deref(), Some("secret"));
148 assert_eq!(config.database.as_deref(), Some("nautilus"));
149 }
150 let factory = get_global_cache_database_factory_registry()
151 .extract(py, config.unbind())
152 .unwrap();
153 let debug = format!("{factory:?}");
154
155 assert!(debug.contains("postgres.example.com"));
156 assert!(debug.contains("password: Some(\"***\")"));
157 assert!(!debug.contains("secret"));
158 });
159 }
160
161 #[cfg(feature = "redis")]
162 #[rstest]
163 fn test_infrastructure_module_exports_redis_message_bus_types() {
164 Python::initialize();
165 Python::attach(|py| {
166 let module = PyModule::new(py, "infrastructure").unwrap();
167
168 infrastructure(py, &module).unwrap();
169
170 assert!(module.getattr("RedisMessageBusBacking").is_ok());
171 assert!(module.getattr("RedisMessageBusConfig").is_ok());
172 assert!(module.getattr("RedisMessageBusFactory").is_ok());
173
174 let config = module
175 .getattr("RedisMessageBusConfig")
176 .unwrap()
177 .call1((
178 "redis.example.com",
179 6380,
180 "user",
181 "secret",
182 true,
183 7,
184 8,
185 9,
186 3,
187 10,
188 4,
189 ))
190 .unwrap();
191 {
192 let config = config
193 .extract::<PyRef<crate::redis::msgbus::RedisMessageBusConfig>>()
194 .unwrap();
195
196 assert_eq!(config.host.as_deref(), Some("redis.example.com"));
197 assert_eq!(config.port, Some(6380));
198 assert_eq!(config.username.as_deref(), Some("user"));
199 assert_eq!(config.password.as_deref(), Some("secret"));
200 assert!(config.ssl);
201 assert_eq!(config.connection_timeout, 7);
202 assert_eq!(config.response_timeout, 8);
203 assert_eq!(config.number_of_retries, 9);
204 assert_eq!(config.exponent_base, 3);
205 assert_eq!(config.max_delay, 10);
206 assert_eq!(config.factor, 4);
207 }
208 let direct_factory = get_global_msgbus_factory_registry()
209 .extract(py, config.unbind())
210 .unwrap();
211 let debug = format!("{direct_factory:?}");
212
213 assert!(debug.contains("redis.example.com"));
214 assert!(debug.contains("password: Some(\"***\")"));
215 assert!(!debug.contains("secret"));
216
217 let config = module
218 .getattr("RedisMessageBusConfig")
219 .unwrap()
220 .call1(("redis.example.com", 6380, "user", "secret"))
221 .unwrap();
222 let factory = module
223 .getattr("RedisMessageBusFactory")
224 .unwrap()
225 .call1((config,))
226 .unwrap()
227 .unbind();
228 let compatibility_factory = get_global_msgbus_factory_registry()
229 .extract(py, factory)
230 .unwrap();
231 let debug = format!("{compatibility_factory:?}");
232
233 assert!(debug.contains("redis.example.com"));
234 assert!(debug.contains("password: Some(\"***\")"));
235 assert!(!debug.contains("secret"));
236
237 let second_module = PyModule::new(py, "infrastructure").unwrap();
238 infrastructure(py, &second_module).unwrap();
239 assert!(second_module.getattr("RedisMessageBusConfig").is_ok());
240 assert!(second_module.getattr("RedisMessageBusFactory").is_ok());
241 });
242 }
243}