From 6832bf88f2c567394c1ff96c043af3271ec02d8f Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 27 Nov 2023 11:49:58 +0000 Subject: [PATCH] implement datetime traits for Py2 --- newsfragments/3679.changed.md | 1 + pytests/src/datetime.rs | 4 +- src/conversions/chrono.rs | 2 + src/types/datetime.rs | 130 +++++++++++++++++++++++++++++++--- 4 files changed, 126 insertions(+), 11 deletions(-) create mode 100644 newsfragments/3679.changed.md diff --git a/newsfragments/3679.changed.md b/newsfragments/3679.changed.md new file mode 100644 index 00000000..2e8e28a9 --- /dev/null +++ b/newsfragments/3679.changed.md @@ -0,0 +1 @@ +Add lifetime parameter to `PyTzInfoAccess` trait and change the return type of `PyTzInfoAccess::get_tzinfo` to `Option>`. For the deprecated gil-ref API, the trait is now implemented for `&'py PyTime` and `&'py PyDateTime` instead of `PyTime` and `PyDate`. diff --git a/pytests/src/datetime.rs b/pytests/src/datetime.rs index 88d732e7..7f0492da 100644 --- a/pytests/src/datetime.rs +++ b/pytests/src/datetime.rs @@ -160,12 +160,12 @@ fn datetime_from_timestamp<'p>( } #[pyfunction] -fn get_datetime_tzinfo(dt: &PyDateTime) -> Option<&PyTzInfo> { +fn get_datetime_tzinfo(dt: &PyDateTime) -> Option> { dt.get_tzinfo() } #[pyfunction] -fn get_time_tzinfo(dt: &PyTime) -> Option<&PyTzInfo> { +fn get_time_tzinfo(dt: &PyTime) -> Option> { dt.get_tzinfo() } diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index e01793c1..0ad5b445 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -45,6 +45,8 @@ use crate::exceptions::{PyTypeError, PyUserWarning, PyValueError}; #[cfg(Py_LIMITED_API)] use crate::sync::GILOnceCell; #[cfg(not(Py_LIMITED_API))] +use crate::types::any::PyAnyMethods; +#[cfg(not(Py_LIMITED_API))] use crate::types::datetime::timezone_from_offset; #[cfg(not(Py_LIMITED_API))] use crate::types::{ diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 1f9db92a..94802fe1 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -19,9 +19,11 @@ use crate::ffi::{ PyDateTime_TIME_GET_FOLD, PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, PyDateTime_TIME_GET_SECOND, }; +use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::PyNativeType; +use crate::types::any::PyAnyMethods; use crate::types::PyTuple; -use crate::{AsPyPointer, IntoPy, Py, PyAny, Python}; +use crate::{Bound, IntoPy, Py, PyAny, Python}; use std::os::raw::c_int; #[cfg(feature = "chrono")] use std::ptr; @@ -160,13 +162,13 @@ pub trait PyTimeAccess { } /// Trait for accessing the components of a struct containing a tzinfo. -pub trait PyTzInfoAccess { +pub trait PyTzInfoAccess<'py> { /// Returns the tzinfo (which may be None). /// /// Implementations should conform to the upstream documentation: /// /// - fn get_tzinfo(&self) -> Option<&PyTzInfo>; + fn get_tzinfo(&self) -> Option>; } /// Bindings around `datetime.date` @@ -211,6 +213,20 @@ impl PyDate { } impl PyDateAccess for PyDate { + fn get_year(&self) -> i32 { + Bound::borrowed_from_gil_ref(&self).get_year() + } + + fn get_month(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_month() + } + + fn get_day(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_day() + } +} + +impl PyDateAccess for Bound<'_, PyDate> { fn get_year(&self) -> i32 { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) } } @@ -325,6 +341,20 @@ impl PyDateTime { } impl PyDateAccess for PyDateTime { + fn get_year(&self) -> i32 { + Bound::borrowed_from_gil_ref(&self).get_year() + } + + fn get_month(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_month() + } + + fn get_day(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_day() + } +} + +impl PyDateAccess for Bound<'_, PyDateTime> { fn get_year(&self) -> i32 { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) } } @@ -339,6 +369,28 @@ impl PyDateAccess for PyDateTime { } impl PyTimeAccess for PyDateTime { + fn get_hour(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_hour() + } + + fn get_minute(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_minute() + } + + fn get_second(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_second() + } + + fn get_microsecond(&self) -> u32 { + Bound::borrowed_from_gil_ref(&self).get_microsecond() + } + + fn get_fold(&self) -> bool { + Bound::borrowed_from_gil_ref(&self).get_fold() + } +} + +impl PyTimeAccess for Bound<'_, PyDateTime> { fn get_hour(&self) -> u8 { unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u8 } } @@ -360,12 +412,24 @@ impl PyTimeAccess for PyDateTime { } } -impl PyTzInfoAccess for PyDateTime { - fn get_tzinfo(&self) -> Option<&PyTzInfo> { +impl<'py> PyTzInfoAccess<'py> for &'py PyDateTime { + fn get_tzinfo(&self) -> Option> { + Bound::borrowed_from_gil_ref(self).get_tzinfo() + } +} + +impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyDateTime> { + fn get_tzinfo(&self) -> Option> { let ptr = self.as_ptr() as *mut ffi::PyDateTime_DateTime; unsafe { if (*ptr).hastzinfo != 0 { - Some(self.py().from_borrowed_ptr((*ptr).tzinfo)) + Some( + (*ptr) + .tzinfo + .assume_borrowed(self.py()) + .to_owned() + .downcast_into_unchecked(), + ) } else { None } @@ -435,6 +499,28 @@ impl PyTime { } impl PyTimeAccess for PyTime { + fn get_hour(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_hour() + } + + fn get_minute(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_minute() + } + + fn get_second(&self) -> u8 { + Bound::borrowed_from_gil_ref(&self).get_second() + } + + fn get_microsecond(&self) -> u32 { + Bound::borrowed_from_gil_ref(&self).get_microsecond() + } + + fn get_fold(&self) -> bool { + Bound::borrowed_from_gil_ref(&self).get_fold() + } +} + +impl PyTimeAccess for Bound<'_, PyTime> { fn get_hour(&self) -> u8 { unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u8 } } @@ -456,12 +542,24 @@ impl PyTimeAccess for PyTime { } } -impl PyTzInfoAccess for PyTime { - fn get_tzinfo(&self) -> Option<&PyTzInfo> { +impl<'py> PyTzInfoAccess<'py> for &'py PyTime { + fn get_tzinfo(&self) -> Option> { + Bound::borrowed_from_gil_ref(self).get_tzinfo() + } +} + +impl<'py> PyTzInfoAccess<'py> for Bound<'py, PyTime> { + fn get_tzinfo(&self) -> Option> { let ptr = self.as_ptr() as *mut ffi::PyDateTime_Time; unsafe { if (*ptr).hastzinfo != 0 { - Some(self.py().from_borrowed_ptr((*ptr).tzinfo)) + Some( + (*ptr) + .tzinfo + .assume_borrowed(self.py()) + .to_owned() + .downcast_into_unchecked(), + ) } else { None } @@ -536,6 +634,20 @@ impl PyDelta { } impl PyDeltaAccess for PyDelta { + fn get_days(&self) -> i32 { + Bound::borrowed_from_gil_ref(&self).get_days() + } + + fn get_seconds(&self) -> i32 { + Bound::borrowed_from_gil_ref(&self).get_seconds() + } + + fn get_microseconds(&self) -> i32 { + Bound::borrowed_from_gil_ref(&self).get_microseconds() + } +} + +impl PyDeltaAccess for Bound<'_, PyDelta> { fn get_days(&self) -> i32 { unsafe { PyDateTime_DELTA_GET_DAYS(self.as_ptr()) } }