nautilus_core/python/
mod.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2025 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Python bindings from `pyo3`.
17
18pub mod casing;
19pub mod datetime;
20pub mod serialization;
21pub mod uuid;
22pub mod version;
23
24use pyo3::{
25    conversion::IntoPyObjectExt,
26    exceptions::{PyRuntimeError, PyTypeError, PyValueError},
27    prelude::*,
28    types::PyString,
29    wrap_pyfunction,
30};
31
32use crate::{
33    UUID4,
34    consts::{NAUTILUS_VERSION, USER_AGENT},
35    datetime::{
36        MILLISECONDS_IN_SECOND, NANOSECONDS_IN_MICROSECOND, NANOSECONDS_IN_MILLISECOND,
37        NANOSECONDS_IN_SECOND,
38    },
39};
40
41/// Extend `IntoPyObjectExt` helper trait to unwrap `PyObject` after conversion.
42pub trait IntoPyObjectNautilusExt<'py>: IntoPyObjectExt<'py> {
43    #[inline]
44    fn into_py_any_unwrap(self, py: Python<'py>) -> PyObject {
45        self.into_py_any(py)
46            .expect("Failed to convert type to PyObject")
47    }
48}
49
50impl<'py, T> IntoPyObjectNautilusExt<'py> for T where T: IntoPyObjectExt<'py> {}
51
52/// Gets the type name for the given Python `obj`.
53///
54/// # Errors
55///
56/// Returns a error if accessing the type name fails.
57pub fn get_pytype_name<'py>(obj: &Bound<'py, PyAny>) -> PyResult<Bound<'py, PyString>> {
58    obj.get_type().name()
59}
60
61/// Converts any type that implements `Display` to a Python `ValueError`.
62///
63/// # Errors
64///
65/// Returns a Python error with the error string.
66pub fn to_pyvalue_err(e: impl std::fmt::Display) -> PyErr {
67    PyValueError::new_err(e.to_string())
68}
69
70/// Converts any type that implements `Display` to a Python `TypeError`.
71///
72/// # Errors
73///
74/// Returns a Python error with the error string.
75pub fn to_pytype_err(e: impl std::fmt::Display) -> PyErr {
76    PyTypeError::new_err(e.to_string())
77}
78
79/// Converts any type that implements `Display` to a Python `RuntimeError`.
80///
81/// # Errors
82///
83/// Returns a Python error with the error string.
84pub fn to_pyruntime_err(e: impl std::fmt::Display) -> PyErr {
85    PyRuntimeError::new_err(e.to_string())
86}
87
88/// Loaded as nautilus_pyo3.core
89///
90/// # Errors
91///
92/// Returns a `PyErr` if registering any module components fails.
93#[pymodule]
94#[rustfmt::skip]
95pub fn core(_: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
96    m.add(stringify!(NAUTILUS_VERSION), NAUTILUS_VERSION)?;
97    m.add(stringify!(USER_AGENT), USER_AGENT)?;
98    m.add(stringify!(MILLISECONDS_IN_SECOND), MILLISECONDS_IN_SECOND)?;
99    m.add(stringify!(NANOSECONDS_IN_SECOND), NANOSECONDS_IN_SECOND)?;
100    m.add(stringify!(NANOSECONDS_IN_MILLISECOND), NANOSECONDS_IN_MILLISECOND)?;
101    m.add(stringify!(NANOSECONDS_IN_MICROSECOND), NANOSECONDS_IN_MICROSECOND)?;
102    m.add_class::<UUID4>()?;
103    m.add_function(wrap_pyfunction!(casing::py_convert_to_snake_case, m)?)?;
104    m.add_function(wrap_pyfunction!(datetime::py_secs_to_nanos, m)?)?;
105    m.add_function(wrap_pyfunction!(datetime::py_secs_to_millis, m)?)?;
106    m.add_function(wrap_pyfunction!(datetime::py_millis_to_nanos, m)?)?;
107    m.add_function(wrap_pyfunction!(datetime::py_micros_to_nanos, m)?)?;
108    m.add_function(wrap_pyfunction!(datetime::py_nanos_to_secs, m)?)?;
109    m.add_function(wrap_pyfunction!(datetime::py_nanos_to_millis, m)?)?;
110    m.add_function(wrap_pyfunction!(datetime::py_nanos_to_micros, m)?)?;
111    m.add_function(wrap_pyfunction!(datetime::py_unix_nanos_to_iso8601, m)?)?;
112    m.add_function(wrap_pyfunction!(datetime::py_last_weekday_nanos, m)?)?;
113    m.add_function(wrap_pyfunction!(datetime::py_is_within_last_24_hours, m)?)?;
114    Ok(())
115}