From f701bccbdf5e77ca6d2435521a36036bc4d44584 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 11:25:33 -0400 Subject: [PATCH] Add PyTime component accessors --- src/ffi3/datetime.rs | 45 +++++++++++++++++++++ src/objects/datetime.rs | 38 ++++++++++++++++- tests/rustapi_module/src/lib.rs | 32 +++++++++++---- tests/rustapi_module/tests/test_datetime.py | 25 +++++++++++- 4 files changed, 129 insertions(+), 11 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 18dad303..f09ea3da 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -108,6 +108,7 @@ pub struct PyDateTime_CAPI { // Type struct wrappers const _PyDateTime_DATE_DATASIZE : usize = 4; +const _PyDateTime_TIME_DATASIZE : usize = 6; const _PyDateTime_DATETIME_DATASIZE: usize = 10; #[repr(C)] @@ -119,6 +120,18 @@ pub struct PyDateTime_Date { pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_Time { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub hastzinfo: c_char, + pub data: [c_uchar; _PyDateTime_TIME_DATASIZE], + #[cfg(Py_3_6)] + pub fold: c_uchar, + pub tzinfo: *mut PyObject +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyDateTime_DateTime { @@ -324,6 +337,38 @@ pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject { _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime) } +// Accessor functions for Time +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int { + _PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0) +} + +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0) +} + +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0) +} + +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_MICROSECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_Time), 0) +} + +#[cfg(Py_3_6)] +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar { + _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time) +} + +#[inline(always)] +pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject { + _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_Time) +} + // Accessor functions for PyDateTime_Delta macro_rules! _access_delta_field { diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 55fdf9b8..d30b482f 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -6,6 +6,8 @@ use ffi::{PyDateTime_DateType, PyDate_Check}; use ffi::{PyDateTime_Date_GET_YEAR, PyDateTime_Date_GET_MONTH, PyDateTime_Date_GET_DAY}; use ffi::{PyDateTime_DateTimeType, PyDateTime_Check}; use ffi::{PyDateTime_DateTime_GET_YEAR, PyDateTime_DateTime_GET_MONTH, PyDateTime_DateTime_GET_DAY}; +use ffi::{PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MINUTE, + PyDateTime_TIME_GET_SECOND, PyDateTime_TIME_GET_MICROSECOND}; use ffi::{PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MINUTE, PyDateTime_DATE_GET_SECOND, PyDateTime_DATE_GET_MICROSECOND}; use ffi::{PyDateTime_DeltaType, PyDelta_Check}; @@ -14,7 +16,7 @@ use ffi::{PyDateTime_TimeType, PyTime_Check}; use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; #[cfg(Py_3_6)] -use ffi::{PyDateTime_DATE_GET_FOLD}; +use ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD}; use python::{Python, ToPyPointer}; use instance::Py; @@ -224,6 +226,40 @@ impl PyTime { } +impl PyTimeComponentAccess for PyTime { + fn get_hour(&self) -> u32 { + unsafe { + PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u32 + } + } + + fn get_minute(&self) -> u32 { + unsafe { + PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u32 + } + } + + fn get_second(&self) -> u32 { + unsafe { + PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u32 + } + } + + fn get_microsecond(&self) -> u32 { + unsafe { + PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 + } + } + + #[cfg(Py_3_6)] + fn get_fold(&self) -> u8 { + unsafe { + PyDateTime_TIME_GET_FOLD(self.as_ptr()) as u8 + } + } +} + + // datetime.tzinfo bindings pub struct PyTzInfo(PyObject); pyobject_native_type!(PyTzInfo, PyDateTime_TZInfoType, PyTZInfo_Check); diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index c43a1fba..5ccff192 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -46,6 +46,28 @@ fn make_time(py: Python, hour: u32, minute: u32, second: u32, PyTime::new(py, hour, minute, second, microsecond, &tzi) } +#[cfg(Py_3_6)] +#[pyfunction] +fn time_with_fold(py: Python, hour: u32, minute: u32, second: u32, + microsecond: u32, tzinfo: Option<&PyTzInfo>, + fold: bool) -> PyResult> { + let tzi = to_pyobject!(py, tzinfo); + PyTime::new_with_fold(py, hour, minute, second, microsecond, &tzi, fold) +} + +#[pyfunction] +fn get_time_tuple(py: Python, dt: &PyTime) -> Py { + PyTuple::new(py, &[dt.get_hour(), dt.get_minute(), dt.get_second(), + dt.get_microsecond()]) +} + +#[cfg(Py_3_6)] +#[pyfunction] +fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py { + PyTuple::new(py, &[dt.get_hour(), dt.get_minute(), dt.get_second(), + dt.get_microsecond(), dt.get_fold() as u32]) +} + #[pyfunction] fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResult> { PyDelta::new(py, days, seconds, microseconds, true) @@ -97,14 +119,6 @@ fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResu } -#[cfg(Py_3_6)] -#[pyfunction] -fn time_with_fold(py: Python, hour: u32, minute: u32, second: u32, - microsecond: u32, tzinfo: Option<&PyTzInfo>, - fold: bool) -> PyResult> { - let tzi = to_pyobject!(py, tzinfo); - PyTime::new_with_fold(py, hour, minute, second, microsecond, &tzi, fold) -} @@ -114,6 +128,7 @@ fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(get_date_tuple))?; m.add_function(wrap_function!(date_from_timestamp))?; m.add_function(wrap_function!(make_time))?; + m.add_function(wrap_function!(get_time_tuple))?; m.add_function(wrap_function!(make_delta))?; m.add_function(wrap_function!(get_delta_tuple))?; m.add_function(wrap_function!(make_datetime))?; @@ -124,6 +139,7 @@ fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { #[cfg(Py_3_6)] { m.add_function(wrap_function!(time_with_fold)); + m.add_function(wrap_function!(get_time_tuple_fold)); m.add_function(wrap_function!(get_datetime_tuple_fold)); } diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 252b356d..285ecb1f 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -62,6 +62,27 @@ def test_time(args, kwargs): assert act.tzinfo is exp.tzinfo +@given(t=st.times()) +def test_time(t): + act = rdt.get_time_tuple(t) + exp = (t.hour, t.minute, t.second, t.microsecond) + + assert act == exp + + +@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") +@given(t=st.times()) +def test_time_fold(t): + t_nofold = t.replace(fold=0) + t_fold = t.replace(fold=1) + + for t in (t_nofold, t_fold): + act = rdt.get_time_tuple_fold(t) + exp = (t.hour, t.minute, t.second, t.microsecond, t.fold) + + assert act == exp + + @pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") @pytest.mark.parametrize('fold', [False, True]) def test_time_fold(fold): @@ -130,8 +151,8 @@ def test_datetime_tuple(dt): @pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") @given(dt=st.datetimes()) def test_datetime_tuple_fold(dt): - dt_fold = dt.replace(fold=0) - dt_nofold = dt.replace(fold=1) + dt_fold = dt.replace(fold=1) + dt_nofold = dt.replace(fold=0) for dt in (dt_fold, dt_nofold): act = rdt.get_datetime_tuple_fold(dt)