Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add _bound constructors for datetime types #3778

Merged
merged 4 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 48 additions & 36 deletions pytests/src/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Bound<'_, PyDate>> {
PyDate::new_bound(py, year, month, day)
}

#[pyfunction]
Expand All @@ -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<Bound<'_, PyDate>> {
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<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyTime>> {
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<'py, PyTzInfo>>,
fold: bool,
) -> PyResult<&'p PyTime> {
PyTime::new_with_fold(py, hour, minute, second, microsecond, tzinfo, fold)
) -> PyResult<Bound<'py, PyTime>> {
PyTime::new_bound_with_fold(py, hour, minute, second, microsecond, tzinfo, fold)
}

#[pyfunction]
Expand Down Expand Up @@ -75,14 +75,19 @@ 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<Bound<'_, PyDelta>> {
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>(delta: &Bound<'py, PyDelta>) -> Bound<'py, PyTuple> {
PyTuple::new_bound(
py,
delta.py(),
[
delta.get_days(),
delta.get_seconds(),
Expand All @@ -93,18 +98,18 @@ 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,
hour: u8,
minute: u8,
second: u8,
microsecond: u32,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<&'p PyDateTime> {
PyDateTime::new(
tzinfo: Option<&Bound<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
PyDateTime::new_bound(
py,
year,
month,
Expand All @@ -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<'py, PyDateTime>) -> Bound<'py, PyTuple> {
PyTuple::new_bound(
py,
[
Expand All @@ -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<'py, PyDateTime>,
) -> Bound<'py, PyTuple> {
PyTuple::new_bound(
py,
[
Expand All @@ -151,21 +159,21 @@ 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<'py, PyTzInfo>>,
) -> PyResult<Bound<'py, PyDateTime>> {
PyDateTime::from_timestamp_bound(py, ts, tz)
}

#[pyfunction]
fn get_datetime_tzinfo(dt: &PyDateTime) -> Option<Bound<'_, PyTzInfo>> {
fn get_datetime_tzinfo<'py>(dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyTzInfo>> {
dt.get_tzinfo_bound()
}

#[pyfunction]
fn get_time_tzinfo(dt: &PyTime) -> Option<Bound<'_, PyTzInfo>> {
fn get_time_tzinfo<'py>(dt: &Bound<'py, PyTime>) -> Option<Bound<'py, PyTzInfo>> {
dt.get_tzinfo_bound()
}

Expand All @@ -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<'py, PyDateTime>,
) -> PyResult<Bound<'py, PyDelta>> {
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<'py>(&self, _dt: &Bound<'py, PyDateTime>) -> Option<Bound<'py, PyDelta>> {
None
}
}
Expand Down
47 changes: 23 additions & 24 deletions src/conversions/chrono.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,12 @@ 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};
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,
Expand All @@ -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(),
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -264,7 +263,7 @@ impl<Tz: TimeZone> ToPyObject for DateTime<Tz> {
// 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))
}
}
Expand Down Expand Up @@ -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()
}
Expand Down Expand Up @@ -366,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()
}
}

Expand All @@ -378,7 +377,7 @@ impl IntoPy<PyObject> for Utc {

impl FromPyObject<'_> for Utc {
fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult<Utc> {
let py_utc = timezone_utc(ob.py());
let py_utc = timezone_utc_bound(ob.py());
if ob.eq(py_utc)? {
Ok(Utc)
} else {
Expand Down Expand Up @@ -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 {
Expand All @@ -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,
Expand Down Expand Up @@ -558,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)]
Expand Down Expand Up @@ -913,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",
Expand Down
17 changes: 14 additions & 3 deletions src/conversions/std/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -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"),
Expand Down Expand Up @@ -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)]
Expand Down
Loading
Loading