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

View File

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

View File

@ -2,8 +2,9 @@
extern crate pyo3; extern crate pyo3;
use pyo3::prelude::*; use std::iter;
use pyo3::prelude::*;
use pyo3::ffi::*; use pyo3::ffi::*;
fn _get_subclasses<'p>( fn _get_subclasses<'p>(
@ -123,19 +124,41 @@ fn test_datetime_utc() {
assert_eq!(offset, 0f32); 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)] #[cfg(Py_3_6)]
#[test] #[test]
fn test_pydate_out_of_bounds() { fn test_pydate_out_of_bounds() {
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented // This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let vals = [ for val in INVALID_DATES.into_iter() {
(2147484672u32, 1, 1),
(2018, 2147484672u32, 1),
(2018, 1, 2147484672u32),
];
for val in vals.into_iter() {
let (year, month, day) = val; let (year, month, day) = val;
let dt = PyDate::new(py, *year, *month, *day); let dt = PyDate::new(py, *year, *month, *day);
let msg = format!("Should have raised an error: {:#?}", val); 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 // This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let vals = [ for val in INVALID_TIMES.into_iter() {
(2147484672u32, 0, 0, 0),
(0, 2147484672u32, 0, 0),
(0, 0, 2147484672u32, 0),
(0, 0, 0, 2147484672u32),
];
for val in vals.into_iter() {
let (hour, minute, second, microsecond) = val; let (hour, minute, second, microsecond) = val;
let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None); let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None);
let msg = format!("Should have raised an error: {:#?}", val); 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 // This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil(); let gil = Python::acquire_gil();
let py = gil.python(); let py = gil.python();
let vals = [ let valid_time = (0, 0, 0, 0);
(2147484672u32, 1, 1, 0, 0, 0, 0), let valid_date = (2018, 1, 1);
(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),
];
for val in vals.into_iter() { let invalid_dates = INVALID_DATES.into_iter().zip(iter::repeat(&valid_time));
let (year, month, day, hour, minute, second, microsecond) = val; 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( let dt = PyDateTime::new(
py, py,
*year, *year,