From ad80c94d76ae568321118b6a61d4b5acecfc4fff Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Mon, 27 Nov 2023 13:14:26 +0000 Subject: [PATCH 1/3] add `_bound` constructors for datetime types --- pytests/src/datetime.rs | 78 +++++----- src/conversions/chrono.rs | 33 ++--- src/conversions/std/time.rs | 17 ++- src/ffi/tests.rs | 16 +- src/types/datetime.rs | 282 +++++++++++++++++++++++++++++++----- src/types/mod.rs | 4 +- tests/test_datetime.rs | 12 +- 7 files changed, 334 insertions(+), 108 deletions(-) diff --git a/pytests/src/datetime.rs b/pytests/src/datetime.rs index 1407da3fe76..afa45cf704b 100644 --- a/pytests/src/datetime.rs +++ b/pytests/src/datetime.rs @@ -7,8 +7,8 @@ use pyo3::types::{ }; #[pyfunction] -fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate> { - PyDate::new(py, year, month, day) +fn make_date(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult> { + PyDate::new_bound(py, year, month, day) } #[pyfunction] @@ -17,34 +17,34 @@ fn get_date_tuple<'p>(py: Python<'p>, d: &PyDate) -> Bound<'p, PyTuple> { } #[pyfunction] -fn date_from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> { - PyDate::from_timestamp(py, timestamp) +fn date_from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult> { + PyDate::from_timestamp_bound(py, timestamp) } #[pyfunction] -fn make_time<'p>( - py: Python<'p>, +fn make_time<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, -) -> PyResult<&'p PyTime> { - PyTime::new(py, hour, minute, second, microsecond, tzinfo) + tzinfo: Option<&Bound<'_, PyTzInfo>>, +) -> PyResult> { + PyTime::new_bound(py, hour, minute, second, microsecond, tzinfo) } #[pyfunction] #[pyo3(signature = (hour, minute, second, microsecond, tzinfo, fold))] -fn time_with_fold<'p>( - py: Python<'p>, +fn time_with_fold<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, + tzinfo: Option<&Bound<'_, PyTzInfo>>, fold: bool, -) -> PyResult<&'p PyTime> { - PyTime::new_with_fold(py, hour, minute, second, microsecond, tzinfo, fold) +) -> PyResult> { + PyTime::new_bound_with_fold(py, hour, minute, second, microsecond, tzinfo, fold) } #[pyfunction] @@ -75,12 +75,17 @@ fn get_time_tuple_fold<'p>(py: Python<'p>, dt: &PyTime) -> Bound<'p, PyTuple> { } #[pyfunction] -fn make_delta(py: Python<'_>, days: i32, seconds: i32, microseconds: i32) -> PyResult<&PyDelta> { - PyDelta::new(py, days, seconds, microseconds, true) +fn make_delta( + py: Python<'_>, + days: i32, + seconds: i32, + microseconds: i32, +) -> PyResult> { + PyDelta::new_bound(py, days, seconds, microseconds, true) } #[pyfunction] -fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> Bound<'p, PyTuple> { +fn get_delta_tuple<'py>(py: Python<'py>, delta: &Bound<'_, PyDelta>) -> Bound<'py, PyTuple> { PyTuple::new_bound( py, [ @@ -93,8 +98,8 @@ fn get_delta_tuple<'p>(py: Python<'p>, delta: &PyDelta) -> Bound<'p, PyTuple> { #[allow(clippy::too_many_arguments)] #[pyfunction] -fn make_datetime<'p>( - py: Python<'p>, +fn make_datetime<'py>( + py: Python<'py>, year: i32, month: u8, day: u8, @@ -102,9 +107,9 @@ fn make_datetime<'p>( minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, -) -> PyResult<&'p PyDateTime> { - PyDateTime::new( + tzinfo: Option<&Bound<'_, PyTzInfo>>, +) -> PyResult> { + PyDateTime::new_bound( py, year, month, @@ -118,7 +123,7 @@ fn make_datetime<'p>( } #[pyfunction] -fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> { +fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'_, PyDateTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( py, [ @@ -134,7 +139,10 @@ fn get_datetime_tuple<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> } #[pyfunction] -fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyTuple> { +fn get_datetime_tuple_fold<'py>( + py: Python<'py>, + dt: &Bound<'_, PyDateTime>, +) -> Bound<'py, PyTuple> { PyTuple::new_bound( py, [ @@ -151,12 +159,12 @@ fn get_datetime_tuple_fold<'p>(py: Python<'p>, dt: &PyDateTime) -> Bound<'p, PyT } #[pyfunction] -fn datetime_from_timestamp<'p>( - py: Python<'p>, +fn datetime_from_timestamp<'py>( + py: Python<'py>, ts: f64, - tz: Option<&PyTzInfo>, -) -> PyResult<&'p PyDateTime> { - PyDateTime::from_timestamp(py, ts, tz) + tz: Option<&Bound<'_, PyTzInfo>>, +) -> PyResult> { + PyDateTime::from_timestamp_bound(py, ts, tz) } #[pyfunction] @@ -179,15 +187,19 @@ impl TzClass { TzClass {} } - fn utcoffset<'p>(&self, py: Python<'p>, _dt: &PyDateTime) -> PyResult<&'p PyDelta> { - PyDelta::new(py, 0, 3600, 0, true) + fn utcoffset<'py>( + &self, + py: Python<'py>, + _dt: &Bound<'_, PyDateTime>, + ) -> PyResult> { + PyDelta::new_bound(py, 0, 3600, 0, true) } - fn tzname(&self, _py: Python<'_>, _dt: &PyDateTime) -> String { + fn tzname(&self, _dt: &Bound<'_, PyDateTime>) -> String { String::from("+01:00") } - fn dst(&self, _py: Python<'_>, _dt: &PyDateTime) -> Option<&PyDelta> { + fn dst(&self, _dt: &Bound<'_, PyDateTime>) -> Option<&PyDelta> { None } } diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 6e529918ab2..632f5c72155 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -52,9 +52,7 @@ use crate::types::{ }; #[cfg(Py_LIMITED_API)] use crate::{intern, DowncastError}; -use crate::{ - Bound, FromPyObject, IntoPy, PyAny, PyErr, PyNativeType, PyObject, PyResult, Python, ToPyObject, -}; +use crate::{Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject, PyResult, Python, ToPyObject}; use chrono::offset::{FixedOffset, Utc}; use chrono::{ DateTime, Datelike, Duration, NaiveDate, NaiveDateTime, NaiveTime, Offset, TimeZone, Timelike, @@ -81,7 +79,7 @@ impl ToPyObject for Duration { // We pass true as the `normalize` parameter since we'd need to do several checks here to // avoid that, and it shouldn't have a big performance impact. // The seconds and microseconds cast should never overflow since it's at most the number of seconds per day - PyDelta::new( + PyDelta::new_bound( py, days.try_into().unwrap_or(i32::MAX), secs.try_into().unwrap(), @@ -144,7 +142,7 @@ impl ToPyObject for NaiveDate { let DateArgs { year, month, day } = self.into(); #[cfg(not(Py_LIMITED_API))] { - PyDate::new(py, year, month, day) + PyDate::new_bound(py, year, month, day) .expect("failed to construct date") .into() } @@ -189,15 +187,16 @@ impl ToPyObject for NaiveTime { truncated_leap_second, } = self.into(); #[cfg(not(Py_LIMITED_API))] - let time = PyTime::new(py, hour, min, sec, micro, None).expect("Failed to construct time"); + let time = + PyTime::new_bound(py, hour, min, sec, micro, None).expect("Failed to construct time"); #[cfg(Py_LIMITED_API)] let time = DatetimeTypes::get(py) .time - .as_ref(py) + .bind(py) .call1((hour, min, sec, micro)) .expect("failed to construct datetime.time"); if truncated_leap_second { - warn_truncated_leap_second(time); + warn_truncated_leap_second(&time); } time.into() } @@ -264,7 +263,7 @@ impl ToPyObject for DateTime { // FIXME: convert to better timezone representation here than just convert to fixed offset // See https://github.com/PyO3/pyo3/issues/3266 let tz = self.offset().fix().to_object(py); - let tz = tz.downcast(py).unwrap(); + let tz = tz.bind(py).downcast().unwrap(); naive_datetime_to_py_datetime(py, &self.naive_local(), Some(tz)) } } @@ -310,9 +309,9 @@ impl ToPyObject for FixedOffset { #[cfg(not(Py_LIMITED_API))] { - let td = PyDelta::new(py, 0, seconds_offset, 0, true) + let td = PyDelta::new_bound(py, 0, seconds_offset, 0, true) .expect("failed to construct timedelta"); - timezone_from_offset(py, td) + timezone_from_offset(&td) .expect("Failed to construct PyTimezone") .into() } @@ -430,8 +429,8 @@ impl From<&NaiveTime> for TimeArgs { fn naive_datetime_to_py_datetime( py: Python<'_>, naive_datetime: &NaiveDateTime, - #[cfg(not(Py_LIMITED_API))] tzinfo: Option<&PyTzInfo>, - #[cfg(Py_LIMITED_API)] tzinfo: Option<&PyAny>, + #[cfg(not(Py_LIMITED_API))] tzinfo: Option<&Bound<'_, PyTzInfo>>, + #[cfg(Py_LIMITED_API)] tzinfo: Option<&Bound<'_, PyAny>>, ) -> PyObject { let DateArgs { year, month, day } = (&naive_datetime.date()).into(); let TimeArgs { @@ -442,21 +441,21 @@ fn naive_datetime_to_py_datetime( truncated_leap_second, } = (&naive_datetime.time()).into(); #[cfg(not(Py_LIMITED_API))] - let datetime = PyDateTime::new(py, year, month, day, hour, min, sec, micro, tzinfo) + let datetime = PyDateTime::new_bound(py, year, month, day, hour, min, sec, micro, tzinfo) .expect("failed to construct datetime"); #[cfg(Py_LIMITED_API)] let datetime = DatetimeTypes::get(py) .datetime - .as_ref(py) + .bind(py) .call1((year, month, day, hour, min, sec, micro, tzinfo)) .expect("failed to construct datetime.datetime"); if truncated_leap_second { - warn_truncated_leap_second(datetime); + warn_truncated_leap_second(&datetime); } datetime.into() } -fn warn_truncated_leap_second(obj: &PyAny) { +fn warn_truncated_leap_second(obj: &Bound<'_, PyAny>) { let py = obj.py(); if let Err(e) = PyErr::warn( py, diff --git a/src/conversions/std/time.rs b/src/conversions/std/time.rs index bdf938c0bd8..85a77566995 100755 --- a/src/conversions/std/time.rs +++ b/src/conversions/std/time.rs @@ -4,7 +4,7 @@ use crate::types::any::PyAnyMethods; #[cfg(Py_LIMITED_API)] use crate::types::PyType; #[cfg(not(Py_LIMITED_API))] -use crate::types::{timezone_utc, PyDateTime, PyDelta, PyDeltaAccess}; +use crate::types::{timezone_utc_bound, PyDateTime, PyDelta, PyDeltaAccess}; #[cfg(Py_LIMITED_API)] use crate::Py; use crate::{ @@ -59,7 +59,7 @@ impl ToPyObject for Duration { #[cfg(not(Py_LIMITED_API))] { - PyDelta::new( + PyDelta::new_bound( py, days.try_into() .expect("Too large Rust duration for timedelta"), @@ -130,7 +130,18 @@ fn unix_epoch_py(py: Python<'_>) -> &PyObject { #[cfg(not(Py_LIMITED_API))] { Ok::<_, PyErr>( - PyDateTime::new(py, 1970, 1, 1, 0, 0, 0, 0, Some(timezone_utc(py)))?.into(), + PyDateTime::new_bound( + py, + 1970, + 1, + 1, + 0, + 0, + 0, + 0, + Some(&timezone_utc_bound(py)), + )? + .into(), ) } #[cfg(Py_LIMITED_API)] diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index 0e0d3688e11..be9f1c343fc 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -79,7 +79,7 @@ fn test_timezone_from_offset() { use crate::types::PyDelta; Python::with_gil(|py| { - let delta = PyDelta::new(py, 0, 100, 0, false).unwrap(); + let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap(); let tz: &PyAny = unsafe { py.from_borrowed_ptr(PyTimeZone_FromOffset(delta.as_ptr())) }; crate::py_run!( py, @@ -97,7 +97,7 @@ fn test_timezone_from_offset_and_name() { use crate::types::PyDelta; Python::with_gil(|py| { - let delta = PyDelta::new(py, 0, 100, 0, false).unwrap(); + let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap(); let tzname = PyString::new_bound(py, "testtz"); let tz: &PyAny = unsafe { py.from_borrowed_ptr(PyTimeZone_FromOffsetAndName( @@ -252,36 +252,36 @@ fn ucs4() { #[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons #[cfg(not(PyPy))] fn test_get_tzinfo() { - use crate::types::timezone_utc; + use crate::types::timezone_utc_bound; crate::Python::with_gil(|py| { use crate::types::{PyDateTime, PyTime}; use crate::PyAny; - let utc = timezone_utc(py); + let utc = &timezone_utc_bound(py); - let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap(); + let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap(); assert!( unsafe { py.from_borrowed_ptr::(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) } .is(utc) ); - let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap(); + let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap(); assert!( unsafe { py.from_borrowed_ptr::(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) } .is_none() ); - let t = PyTime::new(py, 0, 0, 0, 0, Some(utc)).unwrap(); + let t = PyTime::new_bound(py, 0, 0, 0, 0, Some(utc)).unwrap(); assert!( unsafe { py.from_borrowed_ptr::(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) } .is(utc) ); - let t = PyTime::new(py, 0, 0, 0, 0, None).unwrap(); + let t = PyTime::new_bound(py, 0, 0, 0, 0, None).unwrap(); assert!( unsafe { py.from_borrowed_ptr::(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) } diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 354414b8b4c..6950abe2211 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -21,6 +21,7 @@ use crate::ffi::{ }; use crate::ffi_ptr_ext::FfiPtrExt; use crate::instance::PyNativeType; +use crate::py_result_ext::PyResultExt; use crate::types::any::PyAnyMethods; use crate::types::PyTuple; use crate::{Bound, IntoPy, Py, PyAny, Python}; @@ -195,8 +196,20 @@ pyobject_native_type!( ); impl PyDate { - /// Creates a new `datetime.date`. + /// Deprecated form of [`PyDate::new_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDate::new` will be replaced by `PyDate::new_bound` in a future PyO3 version" + ) + )] pub fn new(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult<&PyDate> { + Self::new_bound(py, year, month, day).map(Bound::into_gil_ref) + } + + /// Creates a new `datetime.date`. + pub fn new_bound(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult> { unsafe { let ptr = (ensure_datetime_api(py).Date_FromDate)( year, @@ -204,14 +217,26 @@ impl PyDate { c_int::from(day), ensure_datetime_api(py).DateType, ); - py.from_owned_ptr_or_err(ptr) + ptr.assume_owned_or_err(py).downcast_into_unchecked() } } + /// Deprecated form of [`PyDate::from_timestamp_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDate::from_timestamp` will be replaced by `PyDate::from_timestamp_bound` in a future PyO3 version" + ) + )] + pub fn from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> { + Self::from_timestamp_bound(py, timestamp).map(Bound::into_gil_ref) + } + /// Construct a `datetime.date` from a POSIX timestamp /// /// This is equivalent to `datetime.date.fromtimestamp` - pub fn from_timestamp(py: Python<'_>, timestamp: i64) -> PyResult<&PyDate> { + pub fn from_timestamp_bound(py: Python<'_>, timestamp: i64) -> PyResult> { let time_tuple = PyTuple::new_bound(py, [timestamp]); // safety ensure that the API is loaded @@ -219,7 +244,7 @@ impl PyDate { unsafe { let ptr = PyDate_FromTimestamp(time_tuple.as_ptr()); - py.from_owned_ptr_or_err(ptr) + ptr.assume_owned_or_err(py).downcast_into_unchecked() } } } @@ -264,7 +289,14 @@ pyobject_native_type!( ); impl PyDateTime { - /// Creates a new `datetime.datetime` object. + /// Deprecated form of [`PyDateTime::new_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDateTime::new` will be replaced by `PyDateTime::new_bound` in a future PyO3 version" + ) + )] #[allow(clippy::too_many_arguments)] pub fn new<'p>( py: Python<'p>, @@ -277,9 +309,36 @@ impl PyDateTime { microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult<&'p PyDateTime> { + Self::new_bound( + py, + year, + month, + day, + hour, + minute, + second, + microsecond, + tzinfo.map(PyTzInfo::as_borrowed).as_deref(), + ) + .map(Bound::into_gil_ref) + } + + /// Creates a new `datetime.datetime` object. + #[allow(clippy::too_many_arguments)] + pub fn new_bound<'p>( + py: Python<'p>, + year: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&Bound<'_, PyTzInfo>>, + ) -> PyResult> { let api = ensure_datetime_api(py); unsafe { - let ptr = (api.DateTime_FromDateAndTime)( + (api.DateTime_FromDateAndTime)( year, c_int::from(month), c_int::from(day), @@ -289,11 +348,41 @@ impl PyDateTime { microsecond as c_int, opt_to_pyobj(tzinfo), api.DateTimeType, - ); - py.from_owned_ptr_or_err(ptr) + ) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } + /// Deprecated form of [`PyDateTime::new_bound_with_fold`]. + #[allow(clippy::too_many_arguments)] + pub fn new_with_fold<'p>( + py: Python<'p>, + year: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, + fold: bool, + ) -> PyResult<&'p PyDateTime> { + Self::new_bound_with_fold( + py, + year, + month, + day, + hour, + minute, + second, + microsecond, + tzinfo.map(PyTzInfo::as_borrowed).as_deref(), + fold, + ) + .map(Bound::into_gil_ref) + } + /// Alternate constructor that takes a `fold` parameter. A `true` value for this parameter /// signifies this this datetime is the later of two moments with the same representation, /// during a repeated interval. @@ -302,7 +391,7 @@ impl PyDateTime { /// represented time is ambiguous. /// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail. #[allow(clippy::too_many_arguments)] - pub fn new_with_fold<'p>( + pub fn new_bound_with_fold<'p>( py: Python<'p>, year: i32, month: u8, @@ -311,12 +400,12 @@ impl PyDateTime { minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, + tzinfo: Option<&Bound<'_, PyTzInfo>>, fold: bool, - ) -> PyResult<&'p PyDateTime> { + ) -> PyResult> { let api = ensure_datetime_api(py); unsafe { - let ptr = (api.DateTime_FromDateAndTimeAndFold)( + (api.DateTime_FromDateAndTimeAndFold)( year, c_int::from(month), c_int::from(day), @@ -327,27 +416,46 @@ impl PyDateTime { opt_to_pyobj(tzinfo), c_int::from(fold), api.DateTimeType, - ); - py.from_owned_ptr_or_err(ptr) + ) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } - /// Construct a `datetime` object from a POSIX timestamp - /// - /// This is equivalent to `datetime.datetime.fromtimestamp` + /// Deprecated form of [`PyDateTime::from_timestamp_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDateTime::from_timestamp` will be replaced by `PyDateTime::from_timestamp_bound` in a future PyO3 version" + ) + )] pub fn from_timestamp<'p>( py: Python<'p>, timestamp: f64, tzinfo: Option<&PyTzInfo>, ) -> PyResult<&'p PyDateTime> { - let args: Py = (timestamp, tzinfo).into_py(py); + Self::from_timestamp_bound(py, timestamp, tzinfo.map(PyTzInfo::as_borrowed).as_deref()) + .map(Bound::into_gil_ref) + } + + /// Construct a `datetime` object from a POSIX timestamp + /// + /// This is equivalent to `datetime.datetime.fromtimestamp` + pub fn from_timestamp_bound<'p>( + py: Python<'p>, + timestamp: f64, + tzinfo: Option<&Bound<'_, PyTzInfo>>, + ) -> PyResult> { + let args = IntoPy::>::into_py((timestamp, tzinfo), py).into_bound(py); // safety ensure API is loaded let _api = ensure_datetime_api(py); unsafe { - let ptr = PyDateTime_FromTimestamp(args.as_ptr()); - py.from_owned_ptr_or_err(ptr) + PyDateTime_FromTimestamp(args.as_ptr()) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } } @@ -461,7 +569,14 @@ pyobject_native_type!( ); impl PyTime { - /// Creates a new `datetime.time` object. + /// Deprecated form of [`PyTime::new_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyTime::new` will be replaced by `PyTime::new_bound` in a future PyO3 version" + ) + )] pub fn new<'p>( py: Python<'p>, hour: u8, @@ -470,21 +585,49 @@ impl PyTime { microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult<&'p PyTime> { + Self::new_bound( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(PyTzInfo::as_borrowed).as_deref(), + ) + .map(Bound::into_gil_ref) + } + + /// Creates a new `datetime.time` object. + pub fn new_bound<'p>( + py: Python<'p>, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&Bound<'_, PyTzInfo>>, + ) -> PyResult> { let api = ensure_datetime_api(py); unsafe { - let ptr = (api.Time_FromTime)( + (api.Time_FromTime)( c_int::from(hour), c_int::from(minute), c_int::from(second), microsecond as c_int, opt_to_pyobj(tzinfo), api.TimeType, - ); - py.from_owned_ptr_or_err(ptr) + ) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } - /// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`]. + /// Deprecated form of [`PyTime::new_bound_with_fold`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyTime::new_with_fold` will be replaced by `PyTime::new_bound_with_fold` in a future PyO3 version" + ) + )] pub fn new_with_fold<'p>( py: Python<'p>, hour: u8, @@ -494,9 +637,31 @@ impl PyTime { tzinfo: Option<&PyTzInfo>, fold: bool, ) -> PyResult<&'p PyTime> { + Self::new_bound_with_fold( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(PyTzInfo::as_borrowed).as_deref(), + fold, + ) + .map(Bound::into_gil_ref) + } + + /// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`]. + pub fn new_bound_with_fold<'p>( + py: Python<'p>, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&Bound<'_, PyTzInfo>>, + fold: bool, + ) -> PyResult> { let api = ensure_datetime_api(py); unsafe { - let ptr = (api.Time_FromTimeAndFold)( + (api.Time_FromTimeAndFold)( c_int::from(hour), c_int::from(minute), c_int::from(second), @@ -504,8 +669,9 @@ impl PyTime { opt_to_pyobj(tzinfo), fold as c_int, api.TimeType, - ); - py.from_owned_ptr_or_err(ptr) + ) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } } @@ -599,15 +765,33 @@ pub fn timezone_utc(py: Python<'_>) -> &PyTzInfo { unsafe { &*(ensure_datetime_api(py).TimeZone_UTC as *const PyTzInfo) } } +/// Equivalent to `datetime.timezone.utc` +pub fn timezone_utc_bound(py: Python<'_>) -> Bound<'_, PyTzInfo> { + // TODO: this _could_ have a borrowed form `timezone_utc_borrowed`, but that seems + // like an edge case optimization and we'd prefer in PyO3 0.21 to use `Bound` as + // much as possible + unsafe { + ensure_datetime_api(py) + .TimeZone_UTC + .assume_borrowed(py) + .to_owned() + .downcast_into_unchecked() + } +} + /// Equivalent to `datetime.timezone` constructor /// /// Only used internally #[cfg(feature = "chrono")] -pub fn timezone_from_offset<'a>(py: Python<'a>, offset: &PyDelta) -> PyResult<&'a PyTzInfo> { +pub(crate) fn timezone_from_offset<'py>( + offset: &Bound<'py, PyDelta>, +) -> PyResult> { + let py = offset.py(); let api = ensure_datetime_api(py); unsafe { - let ptr = (api.TimeZone_FromTimeZone)(offset.as_ptr(), ptr::null_mut()); - py.from_owned_ptr_or_err(ptr) + (api.TimeZone_FromTimeZone)(offset.as_ptr(), ptr::null_mut()) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } @@ -623,7 +807,14 @@ pyobject_native_type!( ); impl PyDelta { - /// Creates a new `timedelta`. + /// Deprecated form of [`PyDelta::new_bound`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDelta::new` will be replaced by `PyDelta::new_bound` in a future PyO3 version" + ) + )] pub fn new( py: Python<'_>, days: i32, @@ -631,16 +822,28 @@ impl PyDelta { microseconds: i32, normalize: bool, ) -> PyResult<&PyDelta> { + Self::new_bound(py, days, seconds, microseconds, normalize).map(Bound::into_gil_ref) + } + + /// Creates a new `timedelta`. + pub fn new_bound( + py: Python<'_>, + days: i32, + seconds: i32, + microseconds: i32, + normalize: bool, + ) -> PyResult> { let api = ensure_datetime_api(py); unsafe { - let ptr = (api.Delta_FromDelta)( + (api.Delta_FromDelta)( days as c_int, seconds as c_int, microseconds as c_int, normalize as c_int, api.DeltaType, - ); - py.from_owned_ptr_or_err(ptr) + ) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } } @@ -675,7 +878,7 @@ impl PyDeltaAccess for Bound<'_, PyDelta> { // Utility function which returns a borrowed reference to either // the underlying tzinfo or None. -fn opt_to_pyobj(opt: Option<&PyTzInfo>) -> *mut ffi::PyObject { +fn opt_to_pyobj(opt: Option<&Bound<'_, PyTzInfo>>) -> *mut ffi::PyObject { match opt { Some(tzi) => tzi.as_ptr(), None => unsafe { ffi::Py_None() }, @@ -683,6 +886,7 @@ fn opt_to_pyobj(opt: Option<&PyTzInfo>) -> *mut ffi::PyObject { } #[cfg(test)] +#[cfg_attr(not(feature = "gil-refs"), allow(deprecated))] mod tests { use super::*; #[cfg(feature = "macros")] @@ -766,7 +970,7 @@ mod tests { fn test_timezone_from_offset() { Python::with_gil(|py| { assert!( - timezone_from_offset(py, PyDelta::new(py, 0, -3600, 0, true).unwrap()) + timezone_from_offset(&PyDelta::new_bound(py, 0, -3600, 0, true).unwrap()) .unwrap() .call_method1("utcoffset", ((),)) .unwrap() @@ -777,7 +981,7 @@ mod tests { ); assert!( - timezone_from_offset(py, PyDelta::new(py, 0, 3600, 0, true).unwrap()) + timezone_from_offset(&PyDelta::new_bound(py, 0, 3600, 0, true).unwrap()) .unwrap() .call_method1("utcoffset", ((),)) .unwrap() @@ -787,7 +991,7 @@ mod tests { .unwrap() ); - timezone_from_offset(py, PyDelta::new(py, 1, 0, 0, true).unwrap()).unwrap_err(); + timezone_from_offset(&PyDelta::new_bound(py, 1, 0, 0, true).unwrap()).unwrap_err(); }) } } diff --git a/src/types/mod.rs b/src/types/mod.rs index f802348f466..7658e54a627 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -10,8 +10,8 @@ pub use self::code::PyCode; pub use self::complex::PyComplex; #[cfg(not(Py_LIMITED_API))] pub use self::datetime::{ - timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, - PyTzInfo, PyTzInfoAccess, + timezone_utc, timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, + PyTime, PyTimeAccess, PyTzInfo, PyTzInfoAccess, }; pub use self::dict::{IntoPyDict, PyDict}; #[cfg(not(PyPy))] diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 32163abe1e0..0f953e41c1e 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -1,7 +1,7 @@ #![cfg(not(Py_LIMITED_API))] use pyo3::prelude::*; -use pyo3::types::{timezone_utc, IntoPyDict, PyDate, PyDateTime, PyTime}; +use pyo3::types::{timezone_utc_bound, IntoPyDict, PyDate, PyDateTime, PyTime}; use pyo3_ffi::PyDateTime_IMPORT; fn _get_subclasses<'p>( @@ -118,9 +118,9 @@ fn test_datetime_utc() { use pyo3::types::PyDateTime; Python::with_gil(|py| { - let utc = timezone_utc(py); + let utc = timezone_utc_bound(py); - let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(utc)).unwrap(); + let dt = PyDateTime::new_bound(py, 2018, 1, 1, 0, 0, 0, 0, Some(&utc)).unwrap(); let locals = [("dt", dt)].into_py_dict(py); @@ -155,7 +155,7 @@ fn test_pydate_out_of_bounds() { Python::with_gil(|py| { for val in INVALID_DATES { let (year, month, day) = val; - let dt = PyDate::new(py, *year, *month, *day); + let dt = PyDate::new_bound(py, *year, *month, *day); dt.unwrap_err(); } }); @@ -168,7 +168,7 @@ fn test_pytime_out_of_bounds() { Python::with_gil(|py| { for val in INVALID_TIMES { let (hour, minute, second, microsecond) = val; - let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None); + let dt = PyTime::new_bound(py, *hour, *minute, *second, *microsecond, None); dt.unwrap_err(); } }); @@ -192,7 +192,7 @@ fn test_pydatetime_out_of_bounds() { let (date, time) = val; let (year, month, day) = date; let (hour, minute, second, microsecond) = time; - let dt = PyDateTime::new( + let dt = PyDateTime::new_bound( py, *year, *month, From 10cb09045418678b189fecce264e6277fbaee758 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 11 Feb 2024 22:32:12 +0000 Subject: [PATCH 2/3] review: Icxolu feedback --- pytests/src/datetime.rs | 24 ++++----- src/ffi/tests.rs | 13 ++--- src/types/datetime.rs | 108 +++++++++++++++++++++++----------------- 3 files changed, 79 insertions(+), 66 deletions(-) diff --git a/pytests/src/datetime.rs b/pytests/src/datetime.rs index afa45cf704b..9d8f32a93c9 100644 --- a/pytests/src/datetime.rs +++ b/pytests/src/datetime.rs @@ -28,7 +28,7 @@ fn make_time<'py>( minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, + tzinfo: Option<&Bound<'py, PyTzInfo>>, ) -> PyResult> { PyTime::new_bound(py, hour, minute, second, microsecond, tzinfo) } @@ -41,7 +41,7 @@ fn time_with_fold<'py>( minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, + tzinfo: Option<&Bound<'py, PyTzInfo>>, fold: bool, ) -> PyResult> { PyTime::new_bound_with_fold(py, hour, minute, second, microsecond, tzinfo, fold) @@ -85,9 +85,9 @@ fn make_delta( } #[pyfunction] -fn get_delta_tuple<'py>(py: Python<'py>, delta: &Bound<'_, PyDelta>) -> Bound<'py, PyTuple> { +fn get_delta_tuple<'py>(delta: &Bound<'py, PyDelta>) -> Bound<'py, PyTuple> { PyTuple::new_bound( - py, + delta.py(), [ delta.get_days(), delta.get_seconds(), @@ -107,7 +107,7 @@ fn make_datetime<'py>( minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, + tzinfo: Option<&Bound<'py, PyTzInfo>>, ) -> PyResult> { PyDateTime::new_bound( py, @@ -123,7 +123,7 @@ fn make_datetime<'py>( } #[pyfunction] -fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'_, PyDateTime>) -> Bound<'py, PyTuple> { +fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'py, PyDateTime>) -> Bound<'py, PyTuple> { PyTuple::new_bound( py, [ @@ -141,7 +141,7 @@ fn get_datetime_tuple<'py>(py: Python<'py>, dt: &Bound<'_, PyDateTime>) -> Bound #[pyfunction] fn get_datetime_tuple_fold<'py>( py: Python<'py>, - dt: &Bound<'_, PyDateTime>, + dt: &Bound<'py, PyDateTime>, ) -> Bound<'py, PyTuple> { PyTuple::new_bound( py, @@ -162,18 +162,18 @@ fn get_datetime_tuple_fold<'py>( fn datetime_from_timestamp<'py>( py: Python<'py>, ts: f64, - tz: Option<&Bound<'_, PyTzInfo>>, + tz: Option<&Bound<'py, PyTzInfo>>, ) -> PyResult> { PyDateTime::from_timestamp_bound(py, ts, tz) } #[pyfunction] -fn get_datetime_tzinfo(dt: &PyDateTime) -> Option> { +fn get_datetime_tzinfo<'py>(dt: &Bound<'py, PyDateTime>) -> Option> { dt.get_tzinfo_bound() } #[pyfunction] -fn get_time_tzinfo(dt: &PyTime) -> Option> { +fn get_time_tzinfo<'py>(dt: &Bound<'py, PyTime>) -> Option> { dt.get_tzinfo_bound() } @@ -190,7 +190,7 @@ impl TzClass { fn utcoffset<'py>( &self, py: Python<'py>, - _dt: &Bound<'_, PyDateTime>, + _dt: &Bound<'py, PyDateTime>, ) -> PyResult> { PyDelta::new_bound(py, 0, 3600, 0, true) } @@ -199,7 +199,7 @@ impl TzClass { String::from("+01:00") } - fn dst(&self, _dt: &Bound<'_, PyDateTime>) -> Option<&PyDelta> { + fn dst<'py>(&self, _dt: &Bound<'py, PyDateTime>) -> Option> { None } } diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index f225e4f3647..610edb1c92c 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -77,11 +77,11 @@ fn test_utc_timezone() { #[cfg(feature = "macros")] #[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons fn test_timezone_from_offset() { - use crate::types::PyDelta; + use crate::{ffi_ptr_ext::FfiPtrExt, types::PyDelta}; Python::with_gil(|py| { let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap(); - let tz: &PyAny = unsafe { py.from_borrowed_ptr(PyTimeZone_FromOffset(delta.as_ptr())) }; + let tz = unsafe { PyTimeZone_FromOffset(delta.as_ptr()).assume_owned(py) }; crate::py_run!( py, tz, @@ -95,16 +95,13 @@ fn test_timezone_from_offset() { #[cfg(feature = "macros")] #[cfg_attr(target_arch = "wasm32", ignore)] // DateTime import fails on wasm for mysterious reasons fn test_timezone_from_offset_and_name() { - use crate::types::PyDelta; + use crate::{ffi_ptr_ext::FfiPtrExt, types::PyDelta}; Python::with_gil(|py| { let delta = PyDelta::new_bound(py, 0, 100, 0, false).unwrap(); let tzname = PyString::new_bound(py, "testtz"); - let tz: &PyAny = unsafe { - py.from_borrowed_ptr(PyTimeZone_FromOffsetAndName( - delta.as_ptr(), - tzname.as_ptr(), - )) + let tz = unsafe { + PyTimeZone_FromOffsetAndName(delta.as_ptr(), tzname.as_ptr()).assume_owned(py) }; crate::py_run!( py, diff --git a/src/types/datetime.rs b/src/types/datetime.rs index 9c1c007a6c9..d46a2b77c33 100644 --- a/src/types/datetime.rs +++ b/src/types/datetime.rs @@ -218,8 +218,9 @@ impl PyDate { pub fn new_bound(py: Python<'_>, year: i32, month: u8, day: u8) -> PyResult> { let api = ensure_datetime_api(py)?; unsafe { - let ptr = (api.Date_FromDate)(year, c_int::from(month), c_int::from(day), api.DateType); - ptr.assume_owned_or_err(py).downcast_into_unchecked() + (api.Date_FromDate)(year, c_int::from(month), c_int::from(day), api.DateType) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } @@ -245,8 +246,9 @@ impl PyDate { let _api = ensure_datetime_api(py)?; unsafe { - let ptr = PyDate_FromTimestamp(time_tuple.as_ptr()); - ptr.assume_owned_or_err(py).downcast_into_unchecked() + PyDate_FromTimestamp(time_tuple.as_ptr()) + .assume_owned_or_err(py) + .downcast_into_unchecked() } } } @@ -300,8 +302,8 @@ impl PyDateTime { ) )] #[allow(clippy::too_many_arguments)] - pub fn new<'p>( - py: Python<'p>, + pub fn new<'py>( + py: Python<'py>, year: i32, month: u8, day: u8, @@ -309,8 +311,8 @@ impl PyDateTime { minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, - ) -> PyResult<&'p PyDateTime> { + tzinfo: Option<&'py PyTzInfo>, + ) -> PyResult<&'py PyDateTime> { Self::new_bound( py, year, @@ -327,8 +329,8 @@ impl PyDateTime { /// Creates a new `datetime.datetime` object. #[allow(clippy::too_many_arguments)] - pub fn new_bound<'p>( - py: Python<'p>, + pub fn new_bound<'py>( + py: Python<'py>, year: i32, month: u8, day: u8, @@ -336,8 +338,8 @@ impl PyDateTime { minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, - ) -> PyResult> { + tzinfo: Option<&Bound<'py, PyTzInfo>>, + ) -> PyResult> { let api = ensure_datetime_api(py)?; unsafe { (api.DateTime_FromDateAndTime)( @@ -357,9 +359,16 @@ impl PyDateTime { } /// Deprecated form of [`PyDateTime::new_bound_with_fold`]. + #[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`PyDateTime::new_with_fold` will be replaced by `PyDateTime::new_bound_with_fold` in a future PyO3 version" + ) + )] #[allow(clippy::too_many_arguments)] - pub fn new_with_fold<'p>( - py: Python<'p>, + pub fn new_with_fold<'py>( + py: Python<'py>, year: i32, month: u8, day: u8, @@ -367,9 +376,9 @@ impl PyDateTime { minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, + tzinfo: Option<&'py PyTzInfo>, fold: bool, - ) -> PyResult<&'p PyDateTime> { + ) -> PyResult<&'py PyDateTime> { Self::new_bound_with_fold( py, year, @@ -393,8 +402,8 @@ impl PyDateTime { /// represented time is ambiguous. /// See [PEP 495](https://www.python.org/dev/peps/pep-0495/) for more detail. #[allow(clippy::too_many_arguments)] - pub fn new_bound_with_fold<'p>( - py: Python<'p>, + pub fn new_bound_with_fold<'py>( + py: Python<'py>, year: i32, month: u8, day: u8, @@ -402,9 +411,9 @@ impl PyDateTime { minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, + tzinfo: Option<&Bound<'py, PyTzInfo>>, fold: bool, - ) -> PyResult> { + ) -> PyResult> { let api = ensure_datetime_api(py)?; unsafe { (api.DateTime_FromDateAndTimeAndFold)( @@ -432,11 +441,11 @@ impl PyDateTime { note = "`PyDateTime::from_timestamp` will be replaced by `PyDateTime::from_timestamp_bound` in a future PyO3 version" ) )] - pub fn from_timestamp<'p>( - py: Python<'p>, + pub fn from_timestamp<'py>( + py: Python<'py>, timestamp: f64, - tzinfo: Option<&PyTzInfo>, - ) -> PyResult<&'p PyDateTime> { + tzinfo: Option<&'py PyTzInfo>, + ) -> PyResult<&'py PyDateTime> { Self::from_timestamp_bound(py, timestamp, tzinfo.map(PyTzInfo::as_borrowed).as_deref()) .map(Bound::into_gil_ref) } @@ -444,11 +453,11 @@ impl PyDateTime { /// Construct a `datetime` object from a POSIX timestamp /// /// This is equivalent to `datetime.datetime.fromtimestamp` - pub fn from_timestamp_bound<'p>( - py: Python<'p>, + pub fn from_timestamp_bound<'py>( + py: Python<'py>, timestamp: f64, - tzinfo: Option<&Bound<'_, PyTzInfo>>, - ) -> PyResult> { + tzinfo: Option<&Bound<'py, PyTzInfo>>, + ) -> PyResult> { let args = IntoPy::>::into_py((timestamp, tzinfo), py).into_bound(py); // safety ensure API is loaded @@ -579,14 +588,14 @@ impl PyTime { note = "`PyTime::new` will be replaced by `PyTime::new_bound` in a future PyO3 version" ) )] - pub fn new<'p>( - py: Python<'p>, + pub fn new<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, - ) -> PyResult<&'p PyTime> { + tzinfo: Option<&'py PyTzInfo>, + ) -> PyResult<&'py PyTime> { Self::new_bound( py, hour, @@ -599,14 +608,14 @@ impl PyTime { } /// Creates a new `datetime.time` object. - pub fn new_bound<'p>( - py: Python<'p>, + pub fn new_bound<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, - ) -> PyResult> { + tzinfo: Option<&Bound<'py, PyTzInfo>>, + ) -> PyResult> { let api = ensure_datetime_api(py)?; unsafe { (api.Time_FromTime)( @@ -630,15 +639,15 @@ impl PyTime { note = "`PyTime::new_with_fold` will be replaced by `PyTime::new_bound_with_fold` in a future PyO3 version" ) )] - pub fn new_with_fold<'p>( - py: Python<'p>, + pub fn new_with_fold<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&PyTzInfo>, + tzinfo: Option<&'py PyTzInfo>, fold: bool, - ) -> PyResult<&'p PyTime> { + ) -> PyResult<&'py PyTime> { Self::new_bound_with_fold( py, hour, @@ -652,15 +661,15 @@ impl PyTime { } /// Alternate constructor that takes a `fold` argument. See [`PyDateTime::new_with_fold`]. - pub fn new_bound_with_fold<'p>( - py: Python<'p>, + pub fn new_bound_with_fold<'py>( + py: Python<'py>, hour: u8, minute: u8, second: u8, microsecond: u32, - tzinfo: Option<&Bound<'_, PyTzInfo>>, + tzinfo: Option<&Bound<'py, PyTzInfo>>, fold: bool, - ) -> PyResult> { + ) -> PyResult> { let api = ensure_datetime_api(py)?; unsafe { (api.Time_FromTimeAndFold)( @@ -762,9 +771,16 @@ pyobject_native_type!( #checkfunction=PyTZInfo_Check ); -/// Equivalent to `datetime.timezone.utc` +/// Deprecated form of [`timezone_utc_bound`]. +#[cfg_attr( + not(feature = "gil-refs"), + deprecated( + since = "0.21.0", + note = "`timezone_utc` will be replaced by `timezone_utc_bound` in a future PyO3 version" + ) +)] pub fn timezone_utc(py: Python<'_>) -> &PyTzInfo { - unsafe { &*(expect_datetime_api(py).TimeZone_UTC as *const PyTzInfo) } + timezone_utc_bound(py).into_gil_ref() } /// Equivalent to `datetime.timezone.utc` From 43217dd46cabf689bd89523e4b10a7c8d1ca1d77 Mon Sep 17 00:00:00 2001 From: David Hewitt Date: Sun, 11 Feb 2024 23:06:57 +0000 Subject: [PATCH 3/3] update uses of deprecated timezone_utc --- src/conversions/chrono.rs | 14 +++++++------- src/types/mod.rs | 7 +++++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/conversions/chrono.rs b/src/conversions/chrono.rs index 8808123e656..80a83b23915 100644 --- a/src/conversions/chrono.rs +++ b/src/conversions/chrono.rs @@ -47,8 +47,8 @@ use crate::types::any::PyAnyMethods; use crate::types::datetime::timezone_from_offset; #[cfg(not(Py_LIMITED_API))] use crate::types::{ - timezone_utc, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, - PyTzInfo, PyTzInfoAccess, + timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, + PyTimeAccess, PyTzInfo, PyTzInfoAccess, }; #[cfg(Py_LIMITED_API)] use crate::{intern, DowncastError}; @@ -365,7 +365,7 @@ impl FromPyObject<'_> for FixedOffset { impl ToPyObject for Utc { fn to_object(&self, py: Python<'_>) -> PyObject { - timezone_utc(py).into() + timezone_utc_bound(py).into() } } @@ -377,7 +377,7 @@ impl IntoPy for Utc { impl FromPyObject<'_> for Utc { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { - let py_utc = timezone_utc(ob.py()); + let py_utc = timezone_utc_bound(ob.py()); if ob.eq(py_utc)? { Ok(Utc) } else { @@ -557,8 +557,8 @@ impl DatetimeTypes { } #[cfg(Py_LIMITED_API)] -fn timezone_utc(py: Python<'_>) -> &PyAny { - DatetimeTypes::get(py).timezone_utc.as_ref(py) +fn timezone_utc_bound(py: Python<'_>) -> Bound<'_, PyAny> { + DatetimeTypes::get(py).timezone_utc.bind(py).clone() } #[cfg(test)] @@ -912,7 +912,7 @@ mod tests { let minute = 8; let second = 9; let micro = 999_999; - let tz_utc = timezone_utc(py); + let tz_utc = timezone_utc_bound(py); let py_datetime = new_py_datetime_ob( py, "datetime", diff --git a/src/types/mod.rs b/src/types/mod.rs index 6005fa148dd..da22b30729f 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -8,10 +8,13 @@ pub use self::capsule::PyCapsule; #[cfg(not(Py_LIMITED_API))] pub use self::code::PyCode; pub use self::complex::PyComplex; +#[allow(deprecated)] +#[cfg(not(Py_LIMITED_API))] +pub use self::datetime::timezone_utc; #[cfg(not(Py_LIMITED_API))] pub use self::datetime::{ - timezone_utc, timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, - PyTime, PyTimeAccess, PyTzInfo, PyTzInfoAccess, + timezone_utc_bound, PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, + PyTimeAccess, PyTzInfo, PyTzInfoAccess, }; pub use self::dict::{IntoPyDict, PyDict}; #[cfg(not(PyPy))]