diff --git a/Cargo.lock b/Cargo.lock index c2f5e84e..327292c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1778,6 +1778,7 @@ dependencies = [ "http", "pyo3", "pyo3-build-config", + "ryo3-core", "ryo3-macros", ] diff --git a/crates/ryo3-bytes/src/bytes_ext.rs b/crates/ryo3-bytes/src/bytes_ext.rs index 3226ed08..cb8697e1 100644 --- a/crates/ryo3-bytes/src/bytes_ext.rs +++ b/crates/ryo3-bytes/src/bytes_ext.rs @@ -2,12 +2,17 @@ use crate::bytes::PyBytes; use pyo3::prelude::*; -use pyo3::types::{PyString, PyType}; +use pyo3::types::{PyString, PyTuple, PyType}; use pyo3::IntoPyObjectExt; use std::hash::Hash; #[pymethods] impl PyBytes { + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + let pybytes = pyo3::types::PyBytes::new(py, self.as_ref()).into_bound_py_any(py)?; + PyTuple::new(py, vec![pybytes]) + } + /// Hash bytes fn __hash__(&self) -> u64 { // STD-HASHER VERSION diff --git a/crates/ryo3-bytes/src/lib.rs b/crates/ryo3-bytes/src/lib.rs index 5e1ccbad..12fb810b 100644 --- a/crates/ryo3-bytes/src/lib.rs +++ b/crates/ryo3-bytes/src/lib.rs @@ -27,7 +27,7 @@ pub fn pymod_add(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_class::()?; // rename bytes module to `ry` - m.getattr("Bytes")?.setattr("__module__", "ryo3")?; + m.getattr("Bytes")?.setattr("__module__", "ry.ryo3")?; Ok(()) } diff --git a/crates/ryo3-fspath/src/fspath.rs b/crates/ryo3-fspath/src/fspath.rs index 5f122431..d180cdd6 100644 --- a/crates/ryo3-fspath/src/fspath.rs +++ b/crates/ryo3-fspath/src/fspath.rs @@ -19,10 +19,9 @@ const MAIN_SEPARATOR: char = std::path::MAIN_SEPARATOR; type ArcPathBuf = std::sync::Arc; -#[pyclass(name = "FsPath", module = "ryo3", frozen)] +#[pyclass(name = "FsPath", module = "ry", frozen)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct PyFsPath { - // pth: PathBuf, pth: ArcPathBuf, } @@ -68,6 +67,11 @@ impl PyFsPath { } } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + let os_str = self.pth.as_os_str(); + PyTuple::new(py, vec![os_str]) + } + fn string(&self) -> String { path2str(self.path()) } diff --git a/crates/ryo3-globset/src/lib.rs b/crates/ryo3-globset/src/lib.rs index 351ccd3d..ca7492e4 100644 --- a/crates/ryo3-globset/src/lib.rs +++ b/crates/ryo3-globset/src/lib.rs @@ -294,7 +294,7 @@ fn glob( name = "globster", signature = (patterns, /, *, case_insensitive=None, literal_separator=None, backslash_escape=None) )] -fn globster_fn( +fn py_globster( patterns: StringOrStrings, case_insensitive: Option, literal_separator: Option, @@ -311,7 +311,7 @@ fn globster_fn( pub fn pymod_add(m: &Bound<'_, PyModule>) -> PyResult<()> { m.add_function(wrap_pyfunction!(glob, m)?)?; - m.add_function(wrap_pyfunction!(globster_fn, m)?)?; + m.add_function(wrap_pyfunction!(py_globster, m)?)?; m.add_class::()?; m.add_class::()?; m.add_class::()?; diff --git a/crates/ryo3-http/Cargo.toml b/crates/ryo3-http/Cargo.toml index ce6da60f..afdcc0d7 100644 --- a/crates/ryo3-http/Cargo.toml +++ b/crates/ryo3-http/Cargo.toml @@ -14,6 +14,7 @@ categories.workspace = true [dependencies] http.workspace = true pyo3 = { workspace = true, features = ["experimental-inspect"] } +ryo3-core.workspace = true ryo3-macros.workspace = true [build-dependencies] diff --git a/crates/ryo3-http/src/headers.rs b/crates/ryo3-http/src/headers.rs index 7a163166..f0f5fb6f 100644 --- a/crates/ryo3-http/src/headers.rs +++ b/crates/ryo3-http/src/headers.rs @@ -4,10 +4,9 @@ use crate::PyHeadersLike; use http::header::HeaderMap; use pyo3::exceptions::PyRuntimeError; use pyo3::prelude::*; -use pyo3::types::PyString; -use std::collections::HashMap; +use pyo3::types::{PyBytes, PyDict, PyList, PyString, PyTuple}; -#[pyclass(name = "Headers", module = "ry.ryo3.http")] +#[pyclass(name = "Headers", module = "ry")] #[derive(Clone, Debug)] pub struct PyHeaders(pub HeaderMap); @@ -21,25 +20,18 @@ impl From for PyHeaders { impl PyHeaders { #[new] #[pyo3(signature = (d = None))] - fn py_new(_py: Python<'_>, d: Option>) -> PyResult { - let mut headers = HeaderMap::new(); + fn py_new(_py: Python<'_>, d: Option) -> PyResult { if let Some(d) = d { - for (k, v) in d { - let header_name = - http::header::HeaderName::from_bytes(k.as_bytes()).map_err(|e| { - PyErr::new::(format!( - "header-name-error: {e} (k={k}, v={v})" - )) - })?; - let header_value = http::header::HeaderValue::from_str(&v).map_err(|e| { - PyErr::new::(format!( - "header-value-error: {e} (k={k}, v={v})" - )) - })?; - headers.insert(header_name, header_value); - } + let headers_map = HeaderMap::try_from(d)?; + Ok(Self(headers_map)) + } else { + Ok(PyHeaders(HeaderMap::new())) } - Ok(Self(headers)) + } + + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + let dict = self.asdict(py)?; + PyTuple::new(py, vec![dict]) } /// Return struct Debug-string @@ -278,16 +270,35 @@ impl PyHeaders { Ok(PyHeaders(headers)) } - // pub fn __ior__<'py>( - // mut slf: PyRefMut<'py, Self>, - // other: &PyHeaders, - // ) -> PyResult> { - // let other_headers = other.0.clone(); - // for (k, v) in other_headers { - // if let Some(k) = k { - // slf.0.insert(k, v); - // } - // } - // Ok(slf) - // } + fn asdict<'py>(&self, py: Python<'py>) -> PyResult> { + let d = PyDict::new(py); + + for key in self.0.keys() { + let key_str = key.as_str(); + let values: Vec<_> = self.0.get_all(key).iter().collect(); + + if values.len() == 1 { + let v = values[0]; + if let Ok(vstr) = v.to_str() { + d.set_item(key_str, vstr)?; + } else { + let pybytes = PyBytes::new(py, v.as_bytes()); + d.set_item(key_str, pybytes)?; + } + } else { + let py_list = PyList::empty(py); + for v in values { + if let Ok(vstr) = v.to_str() { + py_list.append(vstr)?; + } else { + let pybytes = PyBytes::new(py, v.as_bytes()); + py_list.append(pybytes)?; + } + } + d.set_item(key_str, py_list)?; + } + } + + Ok(d) + } } diff --git a/crates/ryo3-http/src/headers_like.rs b/crates/ryo3-http/src/headers_like.rs index 04d00d1e..029aaa52 100644 --- a/crates/ryo3-http/src/headers_like.rs +++ b/crates/ryo3-http/src/headers_like.rs @@ -2,23 +2,38 @@ use crate::PyHeaders; use http::HeaderMap; use pyo3::exceptions::PyValueError; use pyo3::prelude::*; +use ryo3_core::types::StringOrStrings; use std::collections::HashMap; #[derive(Debug, Clone, FromPyObject)] pub enum PyHeadersLike { Headers(PyHeaders), - Map(HashMap), + Map(HashMap), } impl PyHeadersLike { - pub fn map2headers(d: &HashMap) -> PyResult { + pub fn map2headers(d: &HashMap) -> PyResult { let mut headers = HeaderMap::new(); for (k, v) in d { - let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) - .map_err(|e| PyValueError::new_err(format!("header-name-error: {e}")))?; - let header_value = http::header::HeaderValue::from_str(v) - .map_err(|e| PyValueError::new_err(format!("header-value-error: {e}")))?; - headers.insert(header_name, header_value); + match v { + StringOrStrings::String(s) => { + let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) + .map_err(|e| PyValueError::new_err(format!("header-name-error: {e}")))?; + let header_value = http::header::HeaderValue::from_str(s) + .map_err(|e| PyValueError::new_err(format!("header-value-error: {e}")))?; + headers.insert(header_name, header_value); + } + StringOrStrings::Strings(v) => { + let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) + .map_err(|e| PyValueError::new_err(format!("header-name-error: {e}")))?; + for s in v { + let header_value = http::header::HeaderValue::from_str(s).map_err(|e| { + PyValueError::new_err(format!("header-value-error: {e}")) + })?; + headers.append(&header_name, header_value); + } + } + } } Ok(headers) } @@ -33,12 +48,32 @@ impl TryFrom for HeaderMap { let mut default_headers = HeaderMap::new(); for (k, v) in d { let k = k.to_string(); - let v = v.to_string(); - let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) - .map_err(|e| PyValueError::new_err(format!("header-name-error: {e}")))?; - let header_value = http::header::HeaderValue::from_str(&v) - .map_err(|e| PyValueError::new_err(format!("header-value-error: {e}")))?; - default_headers.insert(header_name, header_value); + match v { + StringOrStrings::String(s) => { + let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) + .map_err(|e| { + PyValueError::new_err(format!("header-name-error: {e}")) + })?; + let header_value = + http::header::HeaderValue::from_str(&s).map_err(|e| { + PyValueError::new_err(format!("header-value-error: {e}")) + })?; + default_headers.insert(header_name, header_value); + } + StringOrStrings::Strings(v) => { + let header_name = http::header::HeaderName::from_bytes(k.as_bytes()) + .map_err(|e| { + PyValueError::new_err(format!("header-name-error: {e}")) + })?; + for s in v { + let header_value = http::header::HeaderValue::from_str(&s) + .map_err(|e| { + PyValueError::new_err(format!("header-value-error: {e}")) + })?; + default_headers.append(&header_name, header_value); + } + } + } } Ok(default_headers) } diff --git a/crates/ryo3-http/src/status_code.rs b/crates/ryo3-http/src/status_code.rs index 3371d395..a75715b2 100644 --- a/crates/ryo3-http/src/status_code.rs +++ b/crates/ryo3-http/src/status_code.rs @@ -2,6 +2,7 @@ use pyo3::prelude::*; use pyo3::pyclass::CompareOp; +use pyo3::types::PyTuple; #[pyclass(name = "HttpStatus", module = "ry.ryo3.http", frozen)] #[derive(Clone, Debug)] @@ -17,6 +18,10 @@ impl PyHttpStatus { })?)) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new(py, [self.0.as_u16()]) + } + #[must_use] pub fn __str__(&self) -> String { format!("{:?}", self.0) diff --git a/crates/ryo3-jiff/src/ry_date.rs b/crates/ryo3-jiff/src/ry_date.rs index da771a70..39838c0d 100644 --- a/crates/ryo3-jiff/src/ry_date.rs +++ b/crates/ryo3-jiff/src/ry_date.rs @@ -23,7 +23,7 @@ use std::borrow::BorrowMut; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; #[derive(Debug, Clone)] -#[pyclass(name = "Date", module = "ryo3", frozen)] +#[pyclass(name = "Date", module = "ry", frozen)] pub struct RyDate(pub(crate) Date); #[pymethods] @@ -123,6 +123,17 @@ impl RyDate { ) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.year().into_pyobject(py)?, + self.month().into_pyobject(py)?, + self.day().into_pyobject(py)?, + ], + ) + } + fn sub_date(&self, other: &RyDate) -> RySpan { RySpan::from(self.0 - other.0) } diff --git a/crates/ryo3-jiff/src/ry_datetime.rs b/crates/ryo3-jiff/src/ry_datetime.rs index c4e26ab2..5b8130a9 100644 --- a/crates/ryo3-jiff/src/ry_datetime.rs +++ b/crates/ryo3-jiff/src/ry_datetime.rs @@ -13,15 +13,15 @@ use crate::{JiffEraYear, JiffRoundMode, JiffUnit, JiffWeekday, RyDate, RyDateTim use jiff::civil::{DateTime, DateTimeRound, Weekday}; use jiff::Zoned; use pyo3::basic::CompareOp; -use pyo3::intern; use pyo3::prelude::*; -use pyo3::types::{PyDateTime, PyDict, PyType}; +use pyo3::types::{PyDateTime, PyDict, PyTuple, PyType}; +use pyo3::{intern, IntoPyObjectExt}; use std::borrow::BorrowMut; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; #[derive(Debug, Clone)] -#[pyclass(name = "DateTime", module = "ryo3", frozen)] +#[pyclass(name = "DateTime", module = "ry", frozen)] pub struct RyDateTime(pub(crate) DateTime); impl From for RyDateTime { @@ -56,6 +56,21 @@ impl RyDateTime { .map_err(|e| PyErr::new::(format!("{e}"))) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.year().into_bound_py_any(py)?, + self.month().into_bound_py_any(py)?, + self.day().into_bound_py_any(py)?, + self.hour().into_bound_py_any(py)?, + self.minute().into_bound_py_any(py)?, + self.second().into_bound_py_any(py)?, + self.subsec_nanosecond().into_bound_py_any(py)?, + ], + ) + } + #[expect(non_snake_case)] #[classattr] fn MIN() -> Self { diff --git a/crates/ryo3-jiff/src/ry_iso_week_date.rs b/crates/ryo3-jiff/src/ry_iso_week_date.rs index 0f91c641..9eaf9cde 100644 --- a/crates/ryo3-jiff/src/ry_iso_week_date.rs +++ b/crates/ryo3-jiff/src/ry_iso_week_date.rs @@ -3,11 +3,11 @@ use crate::{JiffWeekday, RyDate}; use jiff::civil::ISOWeekDate; use jiff::Zoned; use pyo3::prelude::*; -use pyo3::types::PyType; +use pyo3::types::{PyTuple, PyType}; use std::hash::{DefaultHasher, Hash, Hasher}; #[derive(Debug, Clone)] -#[pyclass(name = "ISOWeekDate", module = "ryo3", frozen)] +#[pyclass(name = "ISOWeekDate", module = "ry", frozen)] pub struct RyISOWeekDate(pub(crate) ISOWeekDate); #[pymethods] @@ -19,6 +19,17 @@ impl RyISOWeekDate { .map_err(map_py_value_err) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.year().into_pyobject(py)?, + self.week().into_pyobject(py)?, + self.weekday().into_pyobject(py)?, + ], + ) + } + // ======================================================================== // CLASSATTR // ======================================================================== diff --git a/crates/ryo3-jiff/src/ry_offset.rs b/crates/ryo3-jiff/src/ry_offset.rs index 255b5666..32337944 100644 --- a/crates/ryo3-jiff/src/ry_offset.rs +++ b/crates/ryo3-jiff/src/ry_offset.rs @@ -7,12 +7,13 @@ use crate::ry_timezone::RyTimeZone; use jiff::tz::{Offset, OffsetArithmetic}; use pyo3::prelude::*; use pyo3::pyclass::CompareOp; -use pyo3::types::PyType; +use pyo3::types::{PyTuple, PyType}; +use pyo3::IntoPyObjectExt; use ryo3_std::PyDuration; use std::hash::{DefaultHasher, Hash, Hasher}; #[derive(Debug, Clone)] -#[pyclass(name = "Offset", module = "ryo3", frozen)] +#[pyclass(name = "Offset", module = "ry", frozen)] pub struct RyOffset(pub(crate) Offset); #[pymethods] @@ -33,6 +34,16 @@ impl RyOffset { } } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + py.None().into_bound_py_any(py)?, + self.0.seconds().into_bound_py_any(py)?, + ], + ) + } + #[expect(non_snake_case)] #[classattr] fn MIN() -> Self { diff --git a/crates/ryo3-jiff/src/ry_signed_duration.rs b/crates/ryo3-jiff/src/ry_signed_duration.rs index 12af7837..efd0022f 100644 --- a/crates/ryo3-jiff/src/ry_signed_duration.rs +++ b/crates/ryo3-jiff/src/ry_signed_duration.rs @@ -3,15 +3,17 @@ use crate::pydatetime_conversions::signed_duration_from_pyobject; use crate::ry_span::RySpan; use crate::JiffSignedDuration; use jiff::{SignedDuration, Span}; +use pyo3::prelude::*; + use pyo3::basic::CompareOp; -use pyo3::types::{PyAnyMethods, PyDelta, PyType}; -use pyo3::{pyclass, pymethods, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python}; +use pyo3::types::{PyAnyMethods, PyDelta, PyTuple, PyType}; +use pyo3::IntoPyObjectExt; use ryo3_std::PyDuration; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; #[derive(Debug, Clone)] -#[pyclass(name = "SignedDuration", module = "ryo3", frozen)] +#[pyclass(name = "SignedDuration", module = "ry", frozen)] pub struct RySignedDuration(pub(crate) SignedDuration); #[pymethods] @@ -22,6 +24,16 @@ impl RySignedDuration { Self(SignedDuration::new(secs, nanos)) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.0.as_secs().into_bound_py_any(py)?, + self.0.subsec_nanos().into_bound_py_any(py)?, + ], + ) + } + #[expect(non_snake_case)] #[classattr] fn MIN() -> Self { diff --git a/crates/ryo3-jiff/src/ry_span.rs b/crates/ryo3-jiff/src/ry_span.rs index 2317e4c6..4c71d6a6 100644 --- a/crates/ryo3-jiff/src/ry_span.rs +++ b/crates/ryo3-jiff/src/ry_span.rs @@ -5,16 +5,17 @@ use crate::span_relative_to::RySpanRelativeTo; use crate::{timespan, JiffRoundMode, JiffSpan, JiffUnit, RyDate, RyDateTime, RyZoned}; use jiff::{Span, SpanArithmetic, SpanRelativeTo, SpanRound}; use pyo3::prelude::PyAnyMethods; -use pyo3::types::{PyDelta, PyDict, PyDictMethods, PyType}; +use pyo3::types::{PyDelta, PyDict, PyDictMethods, PyTuple, PyType}; use pyo3::{ - intern, pyclass, pymethods, Bound, FromPyObject, IntoPyObject, PyAny, PyErr, PyResult, Python, + intern, pyclass, pymethods, Bound, FromPyObject, IntoPyObject, IntoPyObjectExt, PyAny, PyErr, + PyResult, Python, }; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; #[derive(Debug, Clone)] -#[pyclass(name = "TimeSpan", module = "ryo3", frozen)] +#[pyclass(name = "TimeSpan", module = "ry", frozen)] pub struct RySpan(pub(crate) Span); #[pymethods] @@ -53,6 +54,11 @@ impl RySpan { fn __str__(&self) -> String { self.0.to_string() } + fn __getnewargs_ex__<'py>(&self, py: Python<'py>) -> PyResult> { + let args = PyTuple::empty(py).into_bound_py_any(py)?; + let kwargs = self.asdict(py)?.into_bound_py_any(py)?; + PyTuple::new(py, vec![args, kwargs]) + } #[pyo3(signature = (human=false))] fn string(&self, human: bool) -> String { diff --git a/crates/ryo3-jiff/src/ry_time.rs b/crates/ryo3-jiff/src/ry_time.rs index 5e32abc0..69d1dd4a 100644 --- a/crates/ryo3-jiff/src/ry_time.rs +++ b/crates/ryo3-jiff/src/ry_time.rs @@ -15,7 +15,7 @@ use std::borrow::BorrowMut; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; -#[pyclass(name = "Time", module = "ryo3", frozen)] +#[pyclass(name = "Time", module = "ry", frozen)] #[derive(Debug, Clone)] pub struct RyTime(pub(crate) jiff::civil::Time); @@ -39,6 +39,17 @@ impl RyTime { .map_err(|e| PyErr::new::(format!("{e}"))) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.hour().into_pyobject(py)?, + self.minute().into_pyobject(py)?, + self.second().into_pyobject(py)?, + self.subsec_nanosecond().into_pyobject(py)?, + ], + ) + } // ======================================================================== // CLASS ATTRS // ======================================================================== diff --git a/crates/ryo3-jiff/src/ry_timestamp.rs b/crates/ryo3-jiff/src/ry_timestamp.rs index 56cee24b..b08f8730 100644 --- a/crates/ryo3-jiff/src/ry_timestamp.rs +++ b/crates/ryo3-jiff/src/ry_timestamp.rs @@ -11,13 +11,13 @@ use crate::{JiffRoundMode, JiffUnit, RyOffset}; use jiff::{Timestamp, TimestampRound, Zoned}; use pyo3::basic::CompareOp; use pyo3::prelude::*; -use pyo3::types::PyType; +use pyo3::types::{PyTuple, PyType}; use std::borrow::BorrowMut; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; #[derive(Debug, Clone)] -#[pyclass(name = "Timestamp", module = "ryo3", frozen)] +#[pyclass(name = "Timestamp", module = "ry", frozen)] pub struct RyTimestamp(pub(crate) Timestamp); #[pymethods] @@ -32,6 +32,15 @@ impl RyTimestamp { .map_err(|e| PyErr::new::(format!("{e}"))) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.as_second().into_pyobject(py)?, + self.subsec_nanosecond().into_pyobject(py)?, + ], + ) + } #[expect(non_snake_case)] #[classattr] fn MIN() -> Self { diff --git a/crates/ryo3-jiff/src/ry_timezone.rs b/crates/ryo3-jiff/src/ry_timezone.rs index 0a20e327..ed44b54a 100644 --- a/crates/ryo3-jiff/src/ry_timezone.rs +++ b/crates/ryo3-jiff/src/ry_timezone.rs @@ -8,13 +8,14 @@ use crate::JiffTimeZone; use jiff::tz::{Offset, TimeZone}; use jiff::Timestamp; use pyo3::prelude::*; -use pyo3::types::{PyType, PyTzInfo}; +use pyo3::types::{PyTuple, PyType, PyTzInfo}; +use pyo3::IntoPyObjectExt; use ryo3_macros::err_py_not_impl; use std::fmt::Debug; use std::hash::{DefaultHasher, Hash, Hasher}; #[derive(Debug, Clone)] -#[pyclass(name = "TimeZone", module = "ryo3", frozen)] +#[pyclass(name = "TimeZone", module = "ry", frozen)] pub struct RyTimeZone(pub(crate) TimeZone); impl From for RyTimeZone { @@ -38,6 +39,13 @@ impl RyTimeZone { .map_err(|e| PyErr::new::(format!("{e}"))) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![self.iana_name().unwrap_or("").into_bound_py_any(py)?], + ) + } + #[classmethod] fn utc(_cls: &Bound<'_, PyType>) -> Self { Self::from(TimeZone::fixed(Offset::UTC)) diff --git a/crates/ryo3-jiff/src/ry_zoned.rs b/crates/ryo3-jiff/src/ry_zoned.rs index 19ecd646..16816051 100644 --- a/crates/ryo3-jiff/src/ry_zoned.rs +++ b/crates/ryo3-jiff/src/ry_zoned.rs @@ -16,13 +16,14 @@ use jiff::civil::Weekday; use jiff::{Zoned, ZonedDifference, ZonedRound}; use pyo3::prelude::*; use pyo3::pyclass::CompareOp; -use pyo3::types::{PyDate, PyDateTime, PyType}; +use pyo3::types::{PyDate, PyDateTime, PyTuple, PyType}; +use pyo3::IntoPyObjectExt; use std::fmt::Display; use std::hash::{DefaultHasher, Hash, Hasher}; use std::str::FromStr; #[derive(Debug, Clone)] -#[pyclass(name = "ZonedDateTime", module = "ryo3", frozen)] +#[pyclass(name = "ZonedDateTime", module = "ry", frozen)] pub struct RyZoned(pub(crate) Zoned); #[pymethods] @@ -34,6 +35,15 @@ impl RyZoned { let tz = time_zone.0; Ok(RyZoned::from(Zoned::new(ts, tz))) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new( + py, + vec![ + self.timestamp().into_bound_py_any(py)?, + self.timezone().into_bound_py_any(py)?, + ], + ) + } #[classmethod] #[pyo3(signature = (tz=None))] diff --git a/crates/ryo3-size/src/py_size.rs b/crates/ryo3-size/src/py_size.rs index df768b2e..94f9e779 100644 --- a/crates/ryo3-size/src/py_size.rs +++ b/crates/ryo3-size/src/py_size.rs @@ -2,11 +2,11 @@ use crate::types::{Base, Style}; use pyo3::exceptions::{PyTypeError, PyValueError}; use pyo3::prelude::*; use pyo3::pyclass::CompareOp; -use pyo3::types::PyType; +use pyo3::types::{PyTuple, PyType}; use std::ops::{Mul, Neg, Not}; #[derive(Debug, Clone)] -#[pyclass(name = "Size", module = "ryo3", frozen)] +#[pyclass(name = "Size", module = "ry", frozen)] pub struct PySize(size::Size); #[pymethods] @@ -16,6 +16,10 @@ impl PySize { PySize(size::Size::from_bytes(size)) } + fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult> { + PyTuple::new(py, vec![self.0.bytes()]) + } + fn __int__(&self) -> i64 { self.0.bytes() } diff --git a/crates/ryo3-size/src/size_formatter.rs b/crates/ryo3-size/src/size_formatter.rs index 9745a897..1757f5d5 100644 --- a/crates/ryo3-size/src/size_formatter.rs +++ b/crates/ryo3-size/src/size_formatter.rs @@ -1,5 +1,7 @@ use crate::types::{Base, Style}; use pyo3::prelude::*; +use pyo3::types::PyTuple; +use pyo3::{intern, IntoPyObjectExt}; #[pyclass(name = "SizeFormatter", module = "ry")] pub struct PySizeFormatter { @@ -13,10 +15,8 @@ impl PySizeFormatter { #[new] #[pyo3(signature = (base = None, style = None))] fn py_new(base: Option, style: Option