nautilus_network/python/
mod.rs1#![allow(unexpected_cfgs)]
23#![expect(
24 clippy::missing_errors_doc,
25 reason = "errors documented on underlying Rust methods"
26)]
27#![allow(
28 clippy::implicit_hasher,
29 reason = "PyO3 bindings receive concrete HashMap from Python and cannot be generic over hasher"
30)]
31#![allow(
32 clippy::trivially_copy_pass_by_ref,
33 reason = "PyO3 methods require &self for Python binding even when Rust impl does not need it"
34)]
35
36pub mod http;
37pub mod socket;
38pub mod websocket;
39
40use std::num::NonZeroU32;
41
42use nautilus_core::python::to_pyexception;
43use pyo3::prelude::*;
44
45use crate::{
46 python::{
47 http::{HttpClientBuildError, HttpError, HttpInvalidProxyError, HttpTimeoutError},
48 websocket::WebSocketClientError,
49 },
50 ratelimiter::quota::Quota,
51};
52
53#[pymethods]
54#[pyo3_stub_gen::derive::gen_stub_pymethods]
55impl Quota {
56 #[staticmethod]
62 pub fn rate_per_second(max_burst: u32) -> PyResult<Self> {
63 let max_burst = NonZeroU32::new(max_burst)
64 .ok_or_else(|| to_pyexception("Max burst capacity should be a non-zero integer"))?;
65 Self::per_second(max_burst).ok_or_else(|| {
66 to_pyexception(
67 "Max burst too large: replenish interval rounds to zero (max 1_000_000_000)",
68 )
69 })
70 }
71
72 #[staticmethod]
78 pub fn rate_per_minute(max_burst: u32) -> PyResult<Self> {
79 match NonZeroU32::new(max_burst) {
80 Some(max_burst) => Ok(Self::per_minute(max_burst)),
81 None => Err(to_pyexception(
82 "Max burst capacity should be a non-zero integer",
83 )),
84 }
85 }
86
87 #[staticmethod]
93 pub fn rate_per_hour(max_burst: u32) -> PyResult<Self> {
94 match NonZeroU32::new(max_burst) {
95 Some(max_burst) => Ok(Self::per_hour(max_burst)),
96 None => Err(to_pyexception(
97 "Max burst capacity should be a non-zero integer",
98 )),
99 }
100 }
101}
102
103#[pymodule]
109pub fn network(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
110 m.add_class::<crate::http::HttpClient>()?;
111 m.add_class::<crate::http::HttpMethod>()?;
112 m.add_class::<crate::http::HttpResponse>()?;
113 m.add_class::<crate::ratelimiter::quota::Quota>()?;
114 m.add_class::<crate::websocket::WebSocketClient>()?;
115 m.add_class::<crate::websocket::WebSocketConfig>()?;
116 m.add_class::<crate::socket::SocketClient>()?;
117 m.add_class::<crate::socket::SocketConfig>()?;
118
119 m.add(
120 "WebSocketClientError",
121 m.py().get_type::<WebSocketClientError>(),
122 )?;
123 m.add("HttpError", m.py().get_type::<HttpError>())?;
124 m.add("HttpTimeoutError", m.py().get_type::<HttpTimeoutError>())?;
125 m.add(
126 "HttpInvalidProxyError",
127 m.py().get_type::<HttpInvalidProxyError>(),
128 )?;
129 m.add(
130 "HttpClientBuildError",
131 m.py().get_type::<HttpClientBuildError>(),
132 )?;
133
134 m.add_function(wrap_pyfunction!(http::http_get, m)?)?;
135 m.add_function(wrap_pyfunction!(http::http_post, m)?)?;
136 m.add_function(wrap_pyfunction!(http::http_patch, m)?)?;
137 m.add_function(wrap_pyfunction!(http::http_delete, m)?)?;
138 m.add_function(wrap_pyfunction!(http::http_download, m)?)?;
139
140 Ok(())
141}