Switch Py{Date}{Time} constructor parameters to i32

While the valid ranges for the constructor parameters is the same when
expressed as either u32 or i32, since the Python API uses i32 in their
public interface, we won't have to make any changes to the signatures if
the Python behavior changes (e.g. supporting negative years) without
their API changing.
This commit is contained in:
Paul Ganssle 2018-08-21 13:43:32 -04:00
parent 113de1bcd3
commit 5d5689f95b
No known key found for this signature in database
GPG Key ID: CD54FCE3D964BEFB
3 changed files with 112 additions and 96 deletions

View File

@ -14,7 +14,7 @@ use pyo3::{ObjectProtocol, ToPyObject};
use pyo3::{Py, PyResult, Python};
#[pyfunction]
fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult<Py<PyDate>> {
fn make_date(py: Python, year: i32, month: i32, day: i32) -> PyResult<Py<PyDate>> {
PyDate::new(py, year, month, day)
}
@ -33,10 +33,10 @@ fn date_from_timestamp(py: Python, ts: i64) -> PyResult<Py<PyDate>> {
#[pyfunction]
fn make_time(
py: Python,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<Py<PyTime>> {
PyTime::new(
@ -53,10 +53,10 @@ fn make_time(
#[pyfunction]
fn time_with_fold(
py: Python,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyTzInfo>,
fold: bool,
) -> PyResult<Py<PyTime>> {
@ -94,7 +94,7 @@ fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py<PyTuple> {
dt.get_minute(),
dt.get_second(),
dt.get_microsecond(),
dt.get_fold() as u32,
dt.get_fold() as i32,
],
)
}
@ -119,13 +119,13 @@ fn get_delta_tuple(py: Python, delta: &PyDelta) -> Py<PyTuple> {
#[pyfunction]
fn make_datetime(
py: Python,
year: u32,
month: u32,
day: u32,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
year: i32,
month: i32,
day: i32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyTzInfo>,
) -> PyResult<Py<PyDateTime>> {
PyDateTime::new(
@ -170,7 +170,7 @@ fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py<PyTuple> {
dt.get_minute(),
dt.get_second(),
dt.get_microsecond(),
dt.get_fold() as u32,
dt.get_fold() as i32,
],
)
}

View File

@ -28,9 +28,9 @@ use python::{Python, ToPyPointer};
// Traits
pub trait PyDateAccess {
fn get_year(&self) -> u32;
fn get_month(&self) -> u32;
fn get_day(&self) -> u32;
fn get_year(&self) -> i32;
fn get_month(&self) -> i32;
fn get_day(&self) -> i32;
}
pub trait PyDeltaAccess {
@ -40,10 +40,10 @@ pub trait PyDeltaAccess {
}
pub trait PyTimeAccess {
fn get_hour(&self) -> u32;
fn get_minute(&self) -> u32;
fn get_second(&self) -> u32;
fn get_microsecond(&self) -> u32;
fn get_hour(&self) -> i32;
fn get_minute(&self) -> i32;
fn get_second(&self) -> i32;
fn get_microsecond(&self) -> i32;
#[cfg(Py_3_6)]
fn get_fold(&self) -> u8;
}
@ -53,7 +53,7 @@ pub struct PyDate(PyObject);
pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check);
impl PyDate {
pub fn new(py: Python, year: u32, month: u32, day: u32) -> PyResult<Py<PyDate>> {
pub fn new(py: Python, year: i32, month: i32, day: i32) -> PyResult<Py<PyDate>> {
unsafe {
let ptr = (PyDateTimeAPI.Date_FromDate)(
year as c_int,
@ -74,16 +74,16 @@ impl PyDate {
}
impl PyDateAccess for PyDate {
fn get_year(&self) -> u32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 }
fn get_year(&self) -> i32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 }
}
fn get_month(&self) -> u32 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u32 }
fn get_month(&self) -> i32 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as i32 }
}
fn get_day(&self) -> u32 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u32 }
fn get_day(&self) -> i32 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as i32 }
}
}
@ -94,13 +94,13 @@ pyobject_native_type!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check);
impl PyDateTime {
pub fn new(
py: Python,
year: u32,
month: u32,
day: u32,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
year: i32,
month: i32,
day: i32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyObject>,
) -> PyResult<Py<PyDateTime>> {
unsafe {
@ -139,34 +139,34 @@ impl PyDateTime {
}
impl PyDateAccess for PyDateTime {
fn get_year(&self) -> u32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 }
fn get_year(&self) -> i32 {
unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 }
}
fn get_month(&self) -> u32 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u32 }
fn get_month(&self) -> i32 {
unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as i32 }
}
fn get_day(&self) -> u32 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u32 }
fn get_day(&self) -> i32 {
unsafe { PyDateTime_GET_DAY(self.as_ptr()) as i32 }
}
}
impl PyTimeAccess for PyDateTime {
fn get_hour(&self) -> u32 {
unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32 }
fn get_hour(&self) -> i32 {
unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as i32 }
}
fn get_minute(&self) -> u32 {
unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u32 }
fn get_minute(&self) -> i32 {
unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as i32 }
}
fn get_second(&self) -> u32 {
unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u32 }
fn get_second(&self) -> i32 {
unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as i32 }
}
fn get_microsecond(&self) -> u32 {
unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 }
fn get_microsecond(&self) -> i32 {
unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as i32 }
}
#[cfg(Py_3_6)]
@ -182,10 +182,10 @@ pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check);
impl PyTime {
pub fn new(
py: Python,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyObject>,
) -> PyResult<Py<PyTime>> {
unsafe {
@ -207,10 +207,10 @@ impl PyTime {
#[cfg(Py_3_6)]
pub fn new_with_fold(
py: Python,
hour: u32,
minute: u32,
second: u32,
microsecond: u32,
hour: i32,
minute: i32,
second: i32,
microsecond: i32,
tzinfo: Option<&PyObject>,
fold: bool,
) -> PyResult<Py<PyTime>> {
@ -233,20 +233,20 @@ impl PyTime {
}
impl PyTimeAccess for PyTime {
fn get_hour(&self) -> u32 {
unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u32 }
fn get_hour(&self) -> i32 {
unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as i32 }
}
fn get_minute(&self) -> u32 {
unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u32 }
fn get_minute(&self) -> i32 {
unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as i32 }
}
fn get_second(&self) -> u32 {
unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u32 }
fn get_second(&self) -> i32 {
unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as i32 }
}
fn get_microsecond(&self) -> u32 {
unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 }
fn get_microsecond(&self) -> i32 {
unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as i32 }
}
#[cfg(Py_3_6)]

View File

@ -2,8 +2,9 @@
extern crate pyo3;
use pyo3::prelude::*;
use std::iter;
use pyo3::prelude::*;
use pyo3::ffi::*;
fn _get_subclasses<'p>(
@ -123,19 +124,41 @@ fn test_datetime_utc() {
assert_eq!(offset, 0f32);
}
static INVALID_DATES : &'static [(i32, i32, i32)] = &[
(-1, 1, 1),
(0, 1, 1),
(10000, 1, 1),
(2<<30, 1, 1),
(2018, 2<<30, 1),
(2018, 0, 1),
(2018, -1, 1),
(2018, 13, 1),
(2018, 1, 0),
(2017, 2, 29),
(2018, 1, -1),
(2018, 1, 32),
];
static INVALID_TIMES: &'static [(i32, i32, i32, i32)] = &[
(-1, 0, 0, 0),
(25, 0, 0, 0),
(2<<30, 0, 0, 0),
(0, -1, 0, 0),
(0, 60, 0, 0),
(0, 2<<30, 0, 0),
(0, 0, -1, 0),
(0, 0, 61, 0),
(0, 0, 2<<30, 0),
];
#[cfg(Py_3_6)]
#[test]
fn test_pydate_out_of_bounds() {
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil();
let py = gil.python();
let vals = [
(2147484672u32, 1, 1),
(2018, 2147484672u32, 1),
(2018, 1, 2147484672u32),
];
for val in vals.into_iter() {
for val in INVALID_DATES.into_iter() {
let (year, month, day) = val;
let dt = PyDate::new(py, *year, *month, *day);
let msg = format!("Should have raised an error: {:#?}", val);
@ -152,14 +175,7 @@ fn test_pytime_out_of_bounds() {
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil();
let py = gil.python();
let vals = [
(2147484672u32, 0, 0, 0),
(0, 2147484672u32, 0, 0),
(0, 0, 2147484672u32, 0),
(0, 0, 0, 2147484672u32),
];
for val in vals.into_iter() {
for val in INVALID_TIMES.into_iter() {
let (hour, minute, second, microsecond) = val;
let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None);
let msg = format!("Should have raised an error: {:#?}", val);
@ -176,18 +192,18 @@ fn test_pydatetime_out_of_bounds() {
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil();
let py = gil.python();
let vals = [
(2147484672u32, 1, 1, 0, 0, 0, 0),
(2018, 2147484672u32, 1, 0, 0, 0, 0),
(2018, 1, 2147484672u32, 0, 0, 0, 0),
(2018, 1, 1, 2147484672u32, 0, 0, 0),
(2018, 1, 1, 0, 2147484672u32, 0, 0),
(2018, 1, 1, 0, 0, 2147484672u32, 0),
(2018, 1, 1, 0, 0, 0, 2147484672u32),
];
let valid_time = (0, 0, 0, 0);
let valid_date = (2018, 1, 1);
for val in vals.into_iter() {
let (year, month, day, hour, minute, second, microsecond) = val;
let invalid_dates = INVALID_DATES.into_iter().zip(iter::repeat(&valid_time));
let invalid_times = iter::repeat(&valid_date).zip(INVALID_TIMES.into_iter());
let vals = invalid_dates.chain(invalid_times);
for val in vals {
let (date, time) = val;
let (year, month, day) = date;
let (hour, minute, second, microsecond) = time;
let dt = PyDateTime::new(
py,
*year,