From 2b74cdbd57b3b75b4bb1730f5c125102b7945483 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 3 Apr 2018 09:31:35 -0400 Subject: [PATCH 01/44] Add initial bindings for datetime.h --- src/ffi3/datetime.rs | 117 +++++++++++++++++++++++++++++++++++++++++++ src/ffi3/mod.rs | 2 + 2 files changed, 119 insertions(+) create mode 100644 src/ffi3/datetime.rs diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs new file mode 100644 index 00000000..831aa924 --- /dev/null +++ b/src/ffi3/datetime.rs @@ -0,0 +1,117 @@ +use std::os::raw::c_int; +use std::ffi::CString; +use std::option::Option; +use ffi3::object::*; +use ffi3::pycapsule::PyCapsule_Import; + +#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { + pub static mut PyDateTime_Date: PyTypeObject; + pub static mut PyDateTime_Time: PyTypeObject; + pub static mut PyDateTime_DateTime: PyTypeObject; + + pub static mut PyDateTime_Delta: PyTypeObject; + pub static mut PyDateTime_TZInfo: PyTypeObject; +} + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_CAPI { + pub DateType: *mut PyTypeObject, + pub DateTimeType: *mut PyTypeObject, + pub TimeType: *mut PyTypeObject, + pub DeltaType: *mut PyTypeObject, + pub TZInfoType: *mut PyTypeObject, + /* pub TimeZone_UTC: *mut PyObject, */ + pub Date_FromDate: Option< + unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub DateTime_FromDateAndTime: Option< + unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub Time_FromTime: Option< + unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub Delta_FromDelta: Option< + unsafe extern "C" fn( + days: c_int, + seconds: c_int, + microseconds: c_int, + normalize: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + /* pub TimeZone_FromTimeZone: Option< */ + /* unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, */ + /* >, */ + pub DateTime_FromTimestamp: Option< + unsafe extern "C" fn(cls: *mut PyObject, args: *mut PyObject, kwargs: *mut PyObject) + -> *mut PyObject, + >, + pub Date_FromTimestamp: Option< + unsafe extern "C" fn(cls: *mut PyObject, args: *mut PyObject) -> *mut PyObject, + >, + pub DateTime_FromDateAndTimeAndFold: Option< + unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + fold: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub Time_FromTimeAndFold: Option< unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + fold: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, +} + +unsafe impl Sync for PyDateTime_CAPI {} + +#[inline(always)] +pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { + // PyDateTime_CAPSULE_NAME is a macro in C + let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); + + let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 0); + *(capsule as *const PyDateTime_CAPI) +} + + +#[inline(always)] +pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, &mut PyDateTime_Date) as c_int +} diff --git a/src/ffi3/mod.rs b/src/ffi3/mod.rs index 8c6b68dd..c68d6cfc 100644 --- a/src/ffi3/mod.rs +++ b/src/ffi3/mod.rs @@ -17,6 +17,7 @@ pub use self::boolobject::*; pub use self::bytearrayobject::*; pub use self::bytesobject::*; pub use self::complexobject::*; +pub use self::datetime::*; pub use self::descrobject::*; pub use self::dictobject::*; pub use self::enumobject::*; @@ -118,6 +119,7 @@ mod codecs; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3 mod pyerrors; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod pystate; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 +mod datetime; #[cfg(Py_LIMITED_API)] mod pyarena {} From 496b8795251fdd6953f1c0b5cd1da12131a2205c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 3 Apr 2018 09:32:04 -0400 Subject: [PATCH 02/44] Add objects/datetime.rs --- Cargo.toml | 1 + src/lib.rs | 2 ++ src/objects/datetime.rs | 27 +++++++++++++++++++++++++++ src/objects/mod.rs | 2 ++ 4 files changed, 32 insertions(+) create mode 100644 src/objects/datetime.rs diff --git a/Cargo.toml b/Cargo.toml index c6cd5141..f2167f7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,6 +24,7 @@ spin = "0.4.9" num-traits = "0.2.5" pyo3cls = { path = "pyo3cls", version = "=0.4.1" } mashup = "0.1.5" +lazy_static = "1.0" [dev-dependencies] docmatic = "0.1.2" diff --git a/src/lib.rs b/src/lib.rs index 01074536..842ab0e8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,6 +129,8 @@ extern crate spin; #[doc(hidden)] pub extern crate mashup; +#[macro_use] extern crate lazy_static; + #[cfg(not(Py_3))] mod ffi2; diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs new file mode 100644 index 00000000..ec0e166c --- /dev/null +++ b/src/objects/datetime.rs @@ -0,0 +1,27 @@ +use object::PyObject; +use std::os::raw::c_int; +use ffi::{PyDateTime_CAPI, PyDateTime_IMPORT}; +use python::{Python, ToPyPointer}; +use instance::Py; + +pub struct PyDate(PyObject); +pyobject_convert!(PyDate); +pyobject_nativetype!(PyDate, PyDateTime_Date, PyDate_Check); + +lazy_static! { + static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +} + +impl PyDate { + pub fn new(py: Python, year: u32, month: u32, day: u32) -> Py { + let y = year as c_int; + let m = month as c_int; + let d = day as c_int; + + unsafe { + let ptr = PyDateTimeAPI.Date_FromDate.unwrap()(y, m, d, PyDateTimeAPI.DateType); + Py::from_owned_ptr_or_panic(ptr) + } + } +} + diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 2a4a1a13..62b098d6 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,6 +5,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; +pub use self::datetime::{PyDate}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; @@ -185,6 +186,7 @@ pyobject_native_type_convert!(PyObjectRef, ffi::PyBaseObject_Type, ffi::PyObject mod boolobject; mod bytearray; +mod datetime; mod dict; pub mod exc; mod floatob; From 52a64a9240b31331221e9898dc22ea30a32c21e5 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 3 Apr 2018 09:33:05 -0400 Subject: [PATCH 03/44] Add rustapi_module test --- tests/rustapi_module/Cargo.toml | 15 ++++++ .../rustapi_module/rustapi_module/__init__.py | 0 tests/rustapi_module/setup.py | 53 +++++++++++++++++++ tests/rustapi_module/src/lib.rs | 16 ++++++ tests/rustapi_module/tests/test_datetime.py | 14 +++++ 5 files changed, 98 insertions(+) create mode 100644 tests/rustapi_module/Cargo.toml create mode 100644 tests/rustapi_module/rustapi_module/__init__.py create mode 100644 tests/rustapi_module/setup.py create mode 100644 tests/rustapi_module/src/lib.rs create mode 100644 tests/rustapi_module/tests/test_datetime.py diff --git a/tests/rustapi_module/Cargo.toml b/tests/rustapi_module/Cargo.toml new file mode 100644 index 00000000..e3901ee3 --- /dev/null +++ b/tests/rustapi_module/Cargo.toml @@ -0,0 +1,15 @@ +[package] +authors = ["PyO3 Authors"] +name = "rustapi-module" +version = "0.1.0" +description = "A Python wrapper for the Rust API for purposes of testing" + +[dependencies] + +[dependencies.pyo3] +path = "../../" +# features = ["extension-module"] + +[lib] +name = "rustapi_module" +crate-type = ["cdylib"] diff --git a/tests/rustapi_module/rustapi_module/__init__.py b/tests/rustapi_module/rustapi_module/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/rustapi_module/setup.py b/tests/rustapi_module/setup.py new file mode 100644 index 00000000..e0a8230a --- /dev/null +++ b/tests/rustapi_module/setup.py @@ -0,0 +1,53 @@ +import sys + +from setuptools import setup +from setuptools.command.test import test as TestCommand + +try: + from setuptools_rust import RustExtension +except ImportError: + import subprocess + errno = subprocess.call([sys.executable, '-m', 'pip', 'install', 'setuptools-rust']) + if errno: + print("Please install setuptools-rust package") + raise SystemExit(errno) + else: + from setuptools_rust import RustExtension + + +class PyTest(TestCommand): + user_options = [] + + def run(self): + self.run_command("test_rust") + + import subprocess + errno = subprocess.call(['pytest', 'tests']) + raise SystemExit(errno) + + +setup_requires = ['setuptools-rust>=0.6.1'] +install_requires = [] +tests_require = install_requires + ['pytest', 'pytest-benchmark'] + +setup( + name='rustapi-module', + version='0.1.0', + classifiers=[ + 'License :: OSI Approved :: MIT License', + 'Development Status :: 3 - Alpha', + 'Intended Audience :: Developers', + 'Programming Language :: Python', + 'Programming Language :: Rust', + 'Operating System :: POSIX', + 'Operating System :: MacOS :: MacOS X', + ], + packages=['rustapi_module'], + rust_extensions=[RustExtension('rustapi_module.datetime', 'Cargo.toml')], + install_requires=install_requires, + tests_require=tests_require, + setup_requires=setup_requires, + include_package_data=True, + zip_safe=False, + cmdclass=dict(test=PyTest) +) diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs new file mode 100644 index 00000000..4781f13a --- /dev/null +++ b/tests/rustapi_module/src/lib.rs @@ -0,0 +1,16 @@ +#![feature(proc_macro, specialization)] + +extern crate pyo3; + +use pyo3::{py, Py, Python, PyModule, PyResult}; +use pyo3::prelude::PyDate; + +#[py::modinit(datetime)] +fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { + #[pyfn(m, "make_date")] + fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { + Ok(PyDate::new(py, year, month, day)) + } + + Ok(()) +} diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py new file mode 100644 index 00000000..313beb38 --- /dev/null +++ b/tests/rustapi_module/tests/test_datetime.py @@ -0,0 +1,14 @@ +import rustapi_module.datetime as rdt + +import datetime as pdt + +import pytest + + +def test_date(): + assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) + + +def test_date_fails(): + with pytest.raises(ValueError): + rdt.make_date(2017, 2, 30) From d7b90c1b3a8cd16c6f38fe07ec1b36ad4b850fbb Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Fri, 6 Apr 2018 13:39:45 -0400 Subject: [PATCH 04/44] Add datetime.datetime and switch to PyResult> --- src/ffi3/datetime.rs | 27 ++++++++--- src/objects/datetime.rs | 50 +++++++++++++++++---- src/objects/mod.rs | 2 +- tests/rustapi_module/src/lib.rs | 18 ++++++-- tests/rustapi_module/tests/test_datetime.py | 23 +++++++++- 5 files changed, 101 insertions(+), 19 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 831aa924..6d85c8c1 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -5,12 +5,12 @@ use ffi3::object::*; use ffi3::pycapsule::PyCapsule_Import; #[cfg_attr(windows, link(name="pythonXY"))] extern "C" { - pub static mut PyDateTime_Date: PyTypeObject; - pub static mut PyDateTime_Time: PyTypeObject; - pub static mut PyDateTime_DateTime: PyTypeObject; + pub static mut PyDateTime_DateType: PyTypeObject; + pub static mut PyDateTime_TimeType: PyTypeObject; + pub static mut PyDateTime_DateTimeType: PyTypeObject; - pub static mut PyDateTime_Delta: PyTypeObject; - pub static mut PyDateTime_TZInfo: PyTypeObject; + pub static mut PyDateTime_DeltaType: PyTypeObject; + pub static mut PyDateTime_TZInfoType: PyTypeObject; } @@ -101,6 +101,11 @@ pub struct PyDateTime_CAPI { unsafe impl Sync for PyDateTime_CAPI {} +lazy_static! { + pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +} + + #[inline(always)] pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C @@ -113,5 +118,15 @@ pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { #[inline(always)] pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, &mut PyDateTime_Date) as c_int + PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int } diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index ec0e166c..724c796c 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -1,27 +1,61 @@ +use err::PyResult; use object::PyObject; use std::os::raw::c_int; -use ffi::{PyDateTime_CAPI, PyDateTime_IMPORT}; +use ffi::{PyDateTimeAPI}; use python::{Python, ToPyPointer}; use instance::Py; + + +// datetime.date bindings pub struct PyDate(PyObject); pyobject_convert!(PyDate); -pyobject_nativetype!(PyDate, PyDateTime_Date, PyDate_Check); - -lazy_static! { - static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; -} +pyobject_nativetype!(PyDate, PyDateTime_DateType, PyDate_Check); impl PyDate { - pub fn new(py: Python, year: u32, month: u32, day: u32) -> Py { + pub fn new(py: Python, year: u32, month: u32, day: u32) -> PyResult> { let y = year as c_int; let m = month as c_int; let d = day as c_int; unsafe { let ptr = PyDateTimeAPI.Date_FromDate.unwrap()(y, m, d, PyDateTimeAPI.DateType); - Py::from_owned_ptr_or_panic(ptr) + Py::from_owned_ptr_or_err(py, ptr) } } } + +// datetime.datetime bindings +pub struct PyDateTime(PyObject); +pyobject_convert!(PyDateTime); +pyobject_nativetype!(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, + tzinfo: &PyObject) -> PyResult> { + let y = year as c_int; + let mo = month as c_int; + let d = day as c_int; + let h = hour as c_int; + let mi = minute as c_int; + let s = second as c_int; + let u = microsecond as c_int; + + unsafe { + let ptr = PyDateTimeAPI.DateTime_FromDateAndTime.unwrap()( + y, mo, d, h, mi, s, u, tzinfo.as_ptr(), + PyDateTimeAPI.DateTimeType + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + +// datetime.tzinfo bindings +pub struct PyTzInfo(PyObject); +pyobject_convert!(PyTzInfo); +pyobject_nativetype!(PyTzInfo, PyDateTime_TZInfoType, PyTZInfo_Check); + diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 62b098d6..36c6ff1d 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,7 +5,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::{PyDate}; +pub use self::datetime::{PyDate,PyDateTime,PyTzInfo}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 4781f13a..862ab598 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -1,15 +1,27 @@ #![feature(proc_macro, specialization)] extern crate pyo3; - use pyo3::{py, Py, Python, PyModule, PyResult}; -use pyo3::prelude::PyDate; +use pyo3::{ToPyObject}; +use pyo3::prelude::{PyObject}; +use pyo3::prelude::{PyDate, PyDateTime, PyTzInfo}; #[py::modinit(datetime)] fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "make_date")] fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { - Ok(PyDate::new(py, year, month, day)) + PyDate::new(py, year, month, day) + } + + #[pyfn(m, "make_datetime")] + fn make_datetime(py: Python, year: u32, month: u32, day: u32, + hour: u32, minute: u32, second: u32, microsecond: u32, + tzinfo: Option<&PyTzInfo>) -> PyResult> { + let tzi : PyObject = match tzinfo { + Some(t) => t.to_object(py), + None => py.None(), + }; + PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) } Ok(()) diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 313beb38..cc01331c 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -9,6 +9,27 @@ def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) -def test_date_fails(): +def test_invalid_date_fails(): with pytest.raises(ValueError): rdt.make_date(2017, 2, 30) + + +@pytest.mark.parametrize('args, kwargs', [ + ((2017, 9, 1, 12, 45, 30, 0), {}), + ((2017, 9, 1, 12, 45, 30, 0), {'tzinfo': pdt.timezone.utc}), +]) +def test_datetime(args, kwargs): + act = rdt.make_datetime(*args, **kwargs) + exp = pdt.datetime(*args, **kwargs) + + assert act == exp + + +def test_invalid_datetime_fails(): + with pytest.raises(ValueError): + rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) + + +def test_datetime_typeerror(): + with pytest.raises(TypeError): + rdt.make_datetime('2011', 1, 1, 0, 0, 0, 0) From f45362943de0d1b40490af71c8742e261858d9ee Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 10:28:20 -0400 Subject: [PATCH 05/44] Add datetime.time --- src/ffi3/datetime.rs | 5 ++ src/objects/datetime.rs | 23 +++++++++ src/objects/mod.rs | 2 +- tests/rustapi_module/src/lib.rs | 13 ++++- tests/rustapi_module/tests/test_datetime.py | 56 ++++++++++++++++++++- 5 files changed, 96 insertions(+), 3 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 6d85c8c1..2d4e2150 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -130,3 +130,8 @@ pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int } + +#[inline(always)] +pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int +} diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 724c796c..c740914b 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -54,6 +54,29 @@ impl PyDateTime { } } +// datetime.time +pub struct PyTime(PyObject); +pyobject_convert!(PyTime); +pyobject_nativetype!(PyTime, PyDateTime_TimeType, PyTime_Check); + +impl PyTime { + pub fn new(py: Python, hour: u32, minute: u32, second: u32, + microsecond: u32, tzinfo: &PyObject) -> PyResult> { + let h = hour as c_int; + let m = minute as c_int; + let s = second as c_int; + let u = microsecond as c_int; + let tzi = tzinfo.as_ptr(); + + unsafe { + let ptr = PyDateTimeAPI.Time_FromTime.unwrap()( + h, m, s, u, tzi, PyDateTimeAPI.TimeType + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + // datetime.tzinfo bindings pub struct PyTzInfo(PyObject); pyobject_convert!(PyTzInfo); diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 36c6ff1d..8520880f 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,7 +5,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::{PyDate,PyDateTime,PyTzInfo}; +pub use self::datetime::{PyDate,PyTime,PyDateTime,PyTzInfo}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 862ab598..2a8994cf 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -4,7 +4,7 @@ extern crate pyo3; use pyo3::{py, Py, Python, PyModule, PyResult}; use pyo3::{ToPyObject}; use pyo3::prelude::{PyObject}; -use pyo3::prelude::{PyDate, PyDateTime, PyTzInfo}; +use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyTzInfo}; #[py::modinit(datetime)] fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { @@ -13,6 +13,17 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { PyDate::new(py, year, month, day) } + #[pyfn(m, "make_time")] + fn make_time(py: Python, hour: u32, minute: u32, second: u32, + microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { + let tzi: PyObject = match tzinfo { + Some(t) => t.to_object(py), + None => py.None(), + }; + + PyTime::new(py, hour, minute, second, microsecond, &tzi) + } + #[pyfn(m, "make_datetime")] fn make_datetime(py: Python, year: u32, month: u32, day: u32, hour: u32, minute: u32, second: u32, microsecond: u32, diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index cc01331c..5b922679 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,6 +4,8 @@ import datetime as pdt import pytest +UTC = pdt.timezone.utc + def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) @@ -14,15 +16,67 @@ def test_invalid_date_fails(): rdt.make_date(2017, 2, 30) +@pytest.mark.parametrize('args, kwargs', [ + ((0, 0, 0, 0, None), {}), + ((1, 12, 14, 124731), {}), + ((1, 12, 14, 124731), {'tzinfo': UTC}), +]) +def test_time(args, kwargs): + act = rdt.make_time(*args, **kwargs) + exp = pdt.time(*args, **kwargs) + + assert act == exp + assert act.tzinfo is exp.tzinfo + + +@pytest.mark.xfail +@pytest.mark.parametrize('args', [ + (-1, 0, 0, 0), + (0, -1, 0, 0), + (0, 0, -1, 0), + (0, 0, 0, -1), +]) +def test_invalid_time_fails_xfail(args): + with pytest.raises(ValueError): + rdt.make_time(*args) + + +@pytest.mark.parametrize('args', [ + (24, 0, 0, 0), + (25, 0, 0, 0), + (0, 60, 0, 0), + (0, 61, 0, 0), + (0, 0, 60, 0), + (0, 0, 61, 0), + (0, 0, 0, 1000000) +]) +def test_invalid_time_fails(args): + with pytest.raises(ValueError): + rdt.make_time(*args) + + +@pytest.mark.parametrize('args', [ + ('0', 0, 0, 0), + (0, '0', 0, 0), + (0, 0, '0', 0), + (0, 0, 0, '0'), + (0, 0, 0, 0, 'UTC') +]) +def test_time_typeerror(args): + with pytest.raises(TypeError): + rdt.make_time(*args) + + @pytest.mark.parametrize('args, kwargs', [ ((2017, 9, 1, 12, 45, 30, 0), {}), - ((2017, 9, 1, 12, 45, 30, 0), {'tzinfo': pdt.timezone.utc}), + ((2017, 9, 1, 12, 45, 30, 0), {'tzinfo': UTC}), ]) def test_datetime(args, kwargs): act = rdt.make_datetime(*args, **kwargs) exp = pdt.datetime(*args, **kwargs) assert act == exp + assert act.tzinfo is exp.tzinfo def test_invalid_datetime_fails(): From 8d781cb03fc5877343a4debdf9f999bb3183d681 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 12:46:12 -0400 Subject: [PATCH 06/44] Add datetime.timedelta --- src/ffi3/datetime.rs | 5 +++ src/objects/datetime.rs | 23 ++++++++++++ src/objects/mod.rs | 2 +- tests/rustapi_module/src/lib.rs | 10 ++++- tests/rustapi_module/tests/test_datetime.py | 41 +++++++++++++++++++++ 5 files changed, 79 insertions(+), 2 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 2d4e2150..fc7c6a4d 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -135,3 +135,8 @@ pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int } + +#[inline(always)] +pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int +} diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index c740914b..594659d4 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -82,3 +82,26 @@ pub struct PyTzInfo(PyObject); pyobject_convert!(PyTzInfo); pyobject_nativetype!(PyTzInfo, PyDateTime_TZInfoType, PyTZInfo_Check); + +// datetime.timedelta bindings +pub struct PyDelta(PyObject); +pyobject_convert!(PyDelta); +pyobject_nativetype!(PyDelta, PyDateTime_DeltaType, PyDelta_Check); + +impl PyDelta { + pub fn new(py: Python, days: i32, seconds: i32, microseconds: i32, + normalize: bool) -> PyResult> { + let d = days as c_int; + let s = seconds as c_int; + let u = microseconds as c_int; + let n = normalize as c_int; + + unsafe { + let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()( + d, s, u, n, PyDateTimeAPI.DeltaType + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 8520880f..65539649 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,7 +5,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::{PyDate,PyTime,PyDateTime,PyTzInfo}; +pub use self::datetime::{PyDate,PyTime,PyDateTime,PyDelta,PyTzInfo}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 2a8994cf..13ace978 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -4,10 +4,13 @@ extern crate pyo3; use pyo3::{py, Py, Python, PyModule, PyResult}; use pyo3::{ToPyObject}; use pyo3::prelude::{PyObject}; -use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyTzInfo}; +use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; + #[py::modinit(datetime)] fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { + + #[pyfn(m, "make_date")] fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { PyDate::new(py, year, month, day) @@ -24,6 +27,11 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { PyTime::new(py, hour, minute, second, microsecond, &tzi) } + #[pyfn(m, "make_delta")] + fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResult> { + PyDelta::new(py, days, seconds, microseconds, true) + } + #[pyfn(m, "make_datetime")] fn make_datetime(py: Python, year: u32, month: u32, day: u32, hour: u32, minute: u32, second: u32, microsecond: u32, diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 5b922679..5f9968c8 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,7 +4,14 @@ import datetime as pdt import pytest +# Constants UTC = pdt.timezone.utc +MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) +MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1) +MAX_SECONDS = int(pdt.timedelta.max.total_seconds()) +MIN_SECONDS = int(pdt.timedelta.min.total_seconds()) +MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) +MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) def test_date(): @@ -87,3 +94,37 @@ def test_invalid_datetime_fails(): def test_datetime_typeerror(): with pytest.raises(TypeError): rdt.make_datetime('2011', 1, 1, 0, 0, 0, 0) + + +@pytest.mark.parametrize('args', [ + (0, 0, 0), + (1, 0, 0), + (-1, 0, 0), + (0, 1, 0), + (0, -1, 0), + (1, -1, 0), + (-1, 1, 0), + (0, 0, 123456), + (0, 0, -123456), +]) +def test_delta(args): + act = pdt.timedelta(*args) + exp = rdt.make_delta(*args) + + assert act == exp + + +@pytest.mark.parametrize('args,err_type', [ + ((MAX_DAYS + 1, 0, 0), OverflowError), + ((MIN_DAYS - 1, 0, 0), OverflowError), + ((0, MAX_SECONDS + 1, 0), OverflowError), + ((0, MIN_SECONDS - 1, 0), OverflowError), + ((0, 0, MAX_MICROSECONDS + 1), OverflowError), + ((0, 0, MIN_MICROSECONDS - 1), OverflowError), + (('0', 0, 0), TypeError), + ((0, '0', 0), TypeError), + ((0, 0, '0'), TypeError), +]) +def test_delta_err(args, err_type): + with pytest.raises(err_type): + rdt.make_delta(*args) From ee85940579fbe4a696d5434c22c4516f3de131ac Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 13:20:19 -0400 Subject: [PATCH 07/44] Add new requirements-dev.txt file --- tests/rustapi_module/requirements-dev.txt | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/rustapi_module/requirements-dev.txt diff --git a/tests/rustapi_module/requirements-dev.txt b/tests/rustapi_module/requirements-dev.txt new file mode 100644 index 00000000..270740dd --- /dev/null +++ b/tests/rustapi_module/requirements-dev.txt @@ -0,0 +1,3 @@ +hypothesis>=3.55 +pytest>=3.5.0 +setuptools-rust>=0.9.1 From e8d6a4bcc025a33c5aa9645405fec71352e76300 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 13:21:13 -0400 Subject: [PATCH 08/44] Version qualify PyDateTimeAPI --- src/ffi3/datetime.rs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index fc7c6a4d..106be398 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -22,7 +22,9 @@ pub struct PyDateTime_CAPI { pub TimeType: *mut PyTypeObject, pub DeltaType: *mut PyTypeObject, pub TZInfoType: *mut PyTypeObject, - /* pub TimeZone_UTC: *mut PyObject, */ + #[cfg(Py_3_7)] + pub TimeZone_UTC: *mut PyObject, + pub Date_FromDate: Option< unsafe extern "C" fn( year: c_int, @@ -63,16 +65,18 @@ pub struct PyDateTime_CAPI { cls: *mut PyTypeObject, ) -> *mut PyObject, >, - /* pub TimeZone_FromTimeZone: Option< */ - /* unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, */ - /* >, */ + #[cfg(Py_3_7)] + pub TimeZone_FromTimeZone: Option< + unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, + >, pub DateTime_FromTimestamp: Option< - unsafe extern "C" fn(cls: *mut PyObject, args: *mut PyObject, kwargs: *mut PyObject) + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject, >, pub Date_FromTimestamp: Option< - unsafe extern "C" fn(cls: *mut PyObject, args: *mut PyObject) -> *mut PyObject, + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, >, + #[cfg(Py_3_6)] pub DateTime_FromDateAndTimeAndFold: Option< unsafe extern "C" fn( year: c_int, @@ -87,6 +91,7 @@ pub struct PyDateTime_CAPI { cls: *mut PyTypeObject, ) -> *mut PyObject, >, + #[cfg(Py_3_6)] pub Time_FromTimeAndFold: Option< unsafe extern "C" fn( hour: c_int, minute: c_int, From 7256bc85b73eec41201bc9a599925a1792d9caca Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 13:21:28 -0400 Subject: [PATCH 09/44] Add PyDate::from_timestamp --- src/objects/datetime.rs | 8 ++++++++ tests/rustapi_module/src/lib.rs | 7 +++++++ tests/rustapi_module/tests/test_datetime.py | 15 +++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 594659d4..b594dcf2 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -23,6 +23,14 @@ impl PyDate { Py::from_owned_ptr_or_err(py, ptr) } } + + pub fn from_timestamp(py: Python, args: &PyObject) -> PyResult> { + unsafe { + let ptr = PyDateTimeAPI.Date_FromTimestamp.unwrap() + (PyDateTimeAPI.DateType, args.as_ptr()); + Py::from_owned_ptr_or_err(py, ptr) + } + } } diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 13ace978..20d31aa0 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -16,6 +16,13 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { PyDate::new(py, year, month, day) } + #[pyfn(m, "date_from_timestamp")] + fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { + let timestamp = ts.to_object(py); + let args = PyTuple::new(py, &[timestamp]); + PyDate::from_timestamp(py, &args.to_object(py)) + } + #[pyfn(m, "make_time")] fn make_time(py: Python, hour: u32, minute: u32, second: u32, microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 5f9968c8..b48ce14b 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,6 +4,9 @@ import datetime as pdt import pytest +from hypothesis import given +from hypothesis.strategies import dates + # Constants UTC = pdt.timezone.utc MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) @@ -14,6 +17,12 @@ MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) +# Helper functions +def get_timestamp(dt): + return int(dt.timestamp()) + + +# Tests def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) @@ -23,6 +32,12 @@ def test_invalid_date_fails(): rdt.make_date(2017, 2, 30) +@given(d=dates()) +def test_date_from_timestamp(d): + ts = get_timestamp(pdt.datetime.combine(d, pdt.time(0))) + assert rdt.date_from_timestamp(ts) == pdt.date.fromtimestamp(ts) + + @pytest.mark.parametrize('args, kwargs', [ ((0, 0, 0, 0, None), {}), ((1, 12, 14, 124731), {}), From c49bbe4549bc86f79cd12f6223804a71dfe3b575 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Sun, 8 Apr 2018 15:30:29 -0400 Subject: [PATCH 10/44] Add PyDateTime::from_timestamp --- src/objects/datetime.rs | 10 ++++++++++ tests/rustapi_module/src/lib.rs | 15 +++++++++++++++ tests/rustapi_module/tests/test_datetime.py | 20 +++++++++++++++++--- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index b594dcf2..0e8821f8 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -60,6 +60,16 @@ impl PyDateTime { Py::from_owned_ptr_or_err(py, ptr) } } + + pub fn from_timestamp(py: Python, args: &PyObject, kwargs: &PyObject) -> + PyResult> { + + unsafe { + let ptr = PyDateTimeAPI.DateTime_FromTimestamp.unwrap() + (PyDateTimeAPI.DateTimeType, args.as_ptr(), kwargs.as_ptr()); + Py::from_owned_ptr_or_err(py, ptr) + } + } } // datetime.time diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 20d31aa0..ca51bab1 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -4,6 +4,7 @@ extern crate pyo3; use pyo3::{py, Py, Python, PyModule, PyResult}; use pyo3::{ToPyObject}; use pyo3::prelude::{PyObject}; +use pyo3::prelude::{PyTuple, PyDict}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; @@ -50,5 +51,19 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) } + #[pyfn(m, "datetime_from_timestamp")] + fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult> { + let timestamp : PyObject = ts.to_object(py); + let tzi : PyObject = match tz { + Some(t) => t.to_object(py), + None => py.None() + }; + + let args = PyTuple::new(py, &[timestamp, tzi]); + let kwargs = PyDict::new(py); + + PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py)) + } + Ok(()) } diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index b48ce14b..5774ef41 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -5,7 +5,7 @@ import datetime as pdt import pytest from hypothesis import given -from hypothesis.strategies import dates +from hypothesis.strategies import dates, datetimes # Constants UTC = pdt.timezone.utc @@ -19,7 +19,7 @@ MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) # Helper functions def get_timestamp(dt): - return int(dt.timestamp()) + return dt.timestamp() # Tests @@ -35,7 +35,7 @@ def test_invalid_date_fails(): @given(d=dates()) def test_date_from_timestamp(d): ts = get_timestamp(pdt.datetime.combine(d, pdt.time(0))) - assert rdt.date_from_timestamp(ts) == pdt.date.fromtimestamp(ts) + assert rdt.date_from_timestamp(int(ts)) == pdt.date.fromtimestamp(ts) @pytest.mark.parametrize('args, kwargs', [ @@ -111,6 +111,20 @@ def test_datetime_typeerror(): rdt.make_datetime('2011', 1, 1, 0, 0, 0, 0) +@given(dt=datetimes()) +def test_datetime_from_timestamp(dt): + ts = get_timestamp(dt) + assert rdt.datetime_from_timestamp(ts) == pdt.datetime.fromtimestamp(ts) + + +def test_datetime_from_timestamp_tzinfo(): + d1 = rdt.datetime_from_timestamp(0, tz=UTC) + d2 = rdt.datetime_from_timestamp(0, tz=UTC) + + assert d1 == d2 + assert d1.tzinfo is d2.tzinfo + + @pytest.mark.parametrize('args', [ (0, 0, 0), (1, 0, 0), From f5e0785654892563696d87850531511403e67aab Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 9 Apr 2018 08:00:36 -0400 Subject: [PATCH 11/44] Add time_with_fold --- src/objects/datetime.rs | 19 ++++++++++++++++ tests/rustapi_module/src/lib.rs | 25 ++++++++++++++++----- tests/rustapi_module/tests/test_datetime.py | 9 ++++++++ 3 files changed, 47 insertions(+), 6 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 0e8821f8..e3147b80 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -93,6 +93,25 @@ impl PyTime { Py::from_owned_ptr_or_err(py, ptr) } } + + #[cfg(Py_3_6)] + pub fn new_with_fold(py: Python, hour: u32, minute: u32, second: u32, + microsecond: u32, tzinfo: &PyObject, + fold: bool) -> PyResult> { + let h = hour as c_int; + let m = minute as c_int; + let s = second as c_int; + let u = microsecond as c_int; + + let f = fold as c_int; + unsafe { + let ptr = PyDateTimeAPI.Time_FromTimeAndFold.unwrap() + (h, m, s, u, tzinfo.as_ptr(), f, PyDateTimeAPI.TimeType); + Py::from_owned_ptr_or_err(py, ptr) + } + + } + } // datetime.tzinfo bindings diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index ca51bab1..5d5f4dae 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -8,10 +8,17 @@ use pyo3::prelude::{PyTuple, PyDict}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +macro_rules! to_pyobject { + ($py:expr, $o:ident) => (match $o { + Some(t) => t.to_object($py), + None => $py.None() + }) +} + + #[py::modinit(datetime)] fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { - #[pyfn(m, "make_date")] fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { PyDate::new(py, year, month, day) @@ -27,11 +34,7 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { #[pyfn(m, "make_time")] fn make_time(py: Python, hour: u32, minute: u32, second: u32, microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { - let tzi: PyObject = match tzinfo { - Some(t) => t.to_object(py), - None => py.None(), - }; - + let tzi: PyObject = to_pyobject!(py, tzinfo); PyTime::new(py, hour, minute, second, microsecond, &tzi) } @@ -65,5 +68,15 @@ fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py)) } + + #[cfg(Py_3_6)] + #[pyfn(m, "time_with_fold")] + 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) + } + Ok(()) } diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 5774ef41..3bb7b4fd 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -16,6 +16,8 @@ MIN_SECONDS = int(pdt.timedelta.min.total_seconds()) MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) +HAS_FOLD = getattr(pdt.datetime, 'fold', False) + # Helper functions def get_timestamp(dt): @@ -51,6 +53,13 @@ def test_time(args, kwargs): assert act.tzinfo is exp.tzinfo +@pytest.mark.skipif(not HAS_FOLD, reason="Feature not available before 3.6") +@pytest.mark.parametrize('fold', [False, True]) +def test_time_fold(fold): + t = rdt.time_with_fold(0, 0, 0, 0, None, fold) + assert t.fold == fold + + @pytest.mark.xfail @pytest.mark.parametrize('args', [ (-1, 0, 0, 0), From f47697e2b53ea5fd6fe39300d51a2b87c3db6443 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 7 Aug 2018 11:37:43 -0400 Subject: [PATCH 12/44] Refactor pyobject_* macros See 8c260200157564f6f07d0a064ce2f658733fb7f7 for general change. --- src/objects/datetime.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index e3147b80..ee30e486 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -2,6 +2,11 @@ use err::PyResult; use object::PyObject; use std::os::raw::c_int; use ffi::{PyDateTimeAPI}; +use ffi::{PyDateTime_DateType, PyDate_Check}; +use ffi::{PyDateTime_DateTimeType, PyDateTime_Check}; +use ffi::{PyDateTime_DeltaType, PyDelta_Check}; +use ffi::{PyDateTime_TimeType, PyTime_Check}; +use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; use python::{Python, ToPyPointer}; use instance::Py; @@ -9,8 +14,7 @@ use instance::Py; // datetime.date bindings pub struct PyDate(PyObject); -pyobject_convert!(PyDate); -pyobject_nativetype!(PyDate, PyDateTime_DateType, PyDate_Check); +pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); impl PyDate { pub fn new(py: Python, year: u32, month: u32, day: u32) -> PyResult> { @@ -36,8 +40,7 @@ impl PyDate { // datetime.datetime bindings pub struct PyDateTime(PyObject); -pyobject_convert!(PyDateTime); -pyobject_nativetype!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check); +pyobject_native_type!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check); impl PyDateTime { @@ -74,8 +77,7 @@ impl PyDateTime { // datetime.time pub struct PyTime(PyObject); -pyobject_convert!(PyTime); -pyobject_nativetype!(PyTime, PyDateTime_TimeType, PyTime_Check); +pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check); impl PyTime { pub fn new(py: Python, hour: u32, minute: u32, second: u32, @@ -116,14 +118,12 @@ impl PyTime { // datetime.tzinfo bindings pub struct PyTzInfo(PyObject); -pyobject_convert!(PyTzInfo); -pyobject_nativetype!(PyTzInfo, PyDateTime_TZInfoType, PyTZInfo_Check); +pyobject_native_type!(PyTzInfo, PyDateTime_TZInfoType, PyTZInfo_Check); // datetime.timedelta bindings pub struct PyDelta(PyObject); -pyobject_convert!(PyDelta); -pyobject_nativetype!(PyDelta, PyDateTime_DeltaType, PyDelta_Check); +pyobject_native_type!(PyDelta, PyDateTime_DeltaType, PyDelta_Check); impl PyDelta { pub fn new(py: Python, days: i32, seconds: i32, microseconds: i32, From d67d2c4265689dd664192e4055a3eeaa94e03f3d Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 7 Aug 2018 11:40:27 -0400 Subject: [PATCH 13/44] Update rustapi_module to new macro methods --- Cargo.toml | 1 + tests/rustapi_module/src/lib.rs | 126 ++++++++++++++++++-------------- 2 files changed, 73 insertions(+), 54 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2167f7c..e4cc38ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,4 +57,5 @@ members = [ "pyo3cls", "pyo3-derive-backend", "examples/*", + "tests/rustapi_module", ] diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 5d5f4dae..9d9e9b32 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -1,11 +1,15 @@ -#![feature(proc_macro, specialization)] +#![feature(use_extern_macros, specialization)] +#[macro_use] extern crate pyo3; -use pyo3::{py, Py, Python, PyModule, PyResult}; -use pyo3::{ToPyObject}; + +use pyo3::{Py, Python, PyResult}; +use pyo3::{ObjectProtocol, ToPyObject}; +use pyo3::prelude::{pyfunction, pymodinit}; use pyo3::prelude::{PyObject}; -use pyo3::prelude::{PyTuple, PyDict}; +use pyo3::prelude::{PyModule}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +use pyo3::prelude::{PyTuple, PyDict}; macro_rules! to_pyobject { @@ -16,66 +20,80 @@ macro_rules! to_pyobject { } -#[py::modinit(datetime)] -fn init_mod(py: Python, m: &PyModule) -> PyResult<()> { +#[pyfunction] +fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { + PyDate::new(py, year, month, day) +} - #[pyfn(m, "make_date")] - fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { - PyDate::new(py, year, month, day) - } +#[pyfunction] +fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { + let timestamp = ts.to_object(py); + let args = PyTuple::new(py, &[timestamp]); + PyDate::from_timestamp(py, &args.to_object(py)) +} - #[pyfn(m, "date_from_timestamp")] - fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { - let timestamp = ts.to_object(py); - let args = PyTuple::new(py, &[timestamp]); - PyDate::from_timestamp(py, &args.to_object(py)) - } +#[pyfunction] +fn make_time(py: Python, hour: u32, minute: u32, second: u32, + microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { + let tzi: PyObject = to_pyobject!(py, tzinfo); + PyTime::new(py, hour, minute, second, microsecond, &tzi) +} - #[pyfn(m, "make_time")] - fn make_time(py: Python, hour: u32, minute: u32, second: u32, - microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { - let tzi: PyObject = to_pyobject!(py, tzinfo); - PyTime::new(py, hour, minute, second, microsecond, &tzi) - } +#[pyfunction] +fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResult> { + PyDelta::new(py, days, seconds, microseconds, true) +} - #[pyfn(m, "make_delta")] - fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResult> { - PyDelta::new(py, days, seconds, microseconds, true) - } +#[pyfunction] +fn make_datetime(py: Python, year: u32, month: u32, day: u32, + hour: u32, minute: u32, second: u32, microsecond: u32, + tzinfo: Option<&PyTzInfo>) -> PyResult> { + let tzi : PyObject = match tzinfo { + Some(t) => t.to_object(py), + None => py.None(), + }; + PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) +} - #[pyfn(m, "make_datetime")] - fn make_datetime(py: Python, year: u32, month: u32, day: u32, - hour: u32, minute: u32, second: u32, microsecond: u32, - tzinfo: Option<&PyTzInfo>) -> PyResult> { - let tzi : PyObject = match tzinfo { - Some(t) => t.to_object(py), - None => py.None(), - }; - PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) - } +#[pyfunction] +fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult> { + let timestamp : PyObject = ts.to_object(py); + let tzi : PyObject = match tz { + Some(t) => t.to_object(py), + None => py.None() + }; - #[pyfn(m, "datetime_from_timestamp")] - fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult> { - let timestamp : PyObject = ts.to_object(py); - let tzi : PyObject = match tz { - Some(t) => t.to_object(py), - None => py.None() - }; + let args = PyTuple::new(py, &[timestamp, tzi]); + let kwargs = PyDict::new(py); - let args = PyTuple::new(py, &[timestamp, tzi]); - let kwargs = PyDict::new(py); - - PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py)) - } + PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py)) +} +#[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) +} + + + +#[pymodinit] +fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { + m.add_function(wrap_function!(make_date))?; + m.add_function(wrap_function!(date_from_timestamp))?; + m.add_function(wrap_function!(make_time))?; + m.add_function(wrap_function!(make_delta))?; + m.add_function(wrap_function!(make_datetime))?; + m.add_function(wrap_function!(datetime_from_timestamp))?; + + // Python 3.6+ functions #[cfg(Py_3_6)] - #[pyfn(m, "time_with_fold")] - 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) + { + m.add_function(wrap_function!(time_with_fold)); } Ok(()) From cca8eb43d69817dced14587f6f1631a8d3888908 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 7 Aug 2018 11:55:57 -0400 Subject: [PATCH 14/44] Add gitignore to rustapi_module --- tests/rustapi_module/.gitignore | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/rustapi_module/.gitignore diff --git a/tests/rustapi_module/.gitignore b/tests/rustapi_module/.gitignore new file mode 100644 index 00000000..7a6ac170 --- /dev/null +++ b/tests/rustapi_module/.gitignore @@ -0,0 +1,6 @@ +.pytest_cache +.mypy_cache +.hypothesis +.tox + +*.py[co] From 149a13cbfa8e308e6e210430ff95178c6b99be24 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 8 Aug 2018 09:31:41 -0400 Subject: [PATCH 15/44] Fix how the test rustapi_module is built --- tests/rustapi_module/pyproject.toml | 2 ++ tests/rustapi_module/requirements-dev.txt | 2 +- tests/rustapi_module/setup.py | 29 +++++++++++++---------- tests/rustapi_module/tox.ini | 14 +++++++++++ 4 files changed, 34 insertions(+), 13 deletions(-) create mode 100644 tests/rustapi_module/pyproject.toml create mode 100644 tests/rustapi_module/tox.ini diff --git a/tests/rustapi_module/pyproject.toml b/tests/rustapi_module/pyproject.toml new file mode 100644 index 00000000..0f58585a --- /dev/null +++ b/tests/rustapi_module/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_rust>=0.10.2"] diff --git a/tests/rustapi_module/requirements-dev.txt b/tests/rustapi_module/requirements-dev.txt index 270740dd..b1000dbb 100644 --- a/tests/rustapi_module/requirements-dev.txt +++ b/tests/rustapi_module/requirements-dev.txt @@ -1,3 +1,3 @@ hypothesis>=3.55 pytest>=3.5.0 -setuptools-rust>=0.9.1 +setuptools-rust>=0.10.2 diff --git a/tests/rustapi_module/setup.py b/tests/rustapi_module/setup.py index e0a8230a..e55d8d61 100644 --- a/tests/rustapi_module/setup.py +++ b/tests/rustapi_module/setup.py @@ -2,17 +2,7 @@ import sys from setuptools import setup from setuptools.command.test import test as TestCommand - -try: - from setuptools_rust import RustExtension -except ImportError: - import subprocess - errno = subprocess.call([sys.executable, '-m', 'pip', 'install', 'setuptools-rust']) - if errno: - print("Please install setuptools-rust package") - raise SystemExit(errno) - else: - from setuptools_rust import RustExtension +from setuptools_rust import RustExtension class PyTest(TestCommand): @@ -26,6 +16,20 @@ class PyTest(TestCommand): raise SystemExit(errno) +def get_py_version_cfgs(): + # For now each Cfg Py_3_X flag is interpreted as "at least 3.X" + version = sys.version_info[0:2] + + if version[0] == 2: + return ['--cfg=Py_2'] + + py3_min = 5 + out_cfg = [] + for minor in range(py3_min, version[1]+1): + out_cfg.append('--cfg=Py_3_%d' % minor) + + return out_cfg + setup_requires = ['setuptools-rust>=0.6.1'] install_requires = [] tests_require = install_requires + ['pytest', 'pytest-benchmark'] @@ -43,7 +47,8 @@ setup( 'Operating System :: MacOS :: MacOS X', ], packages=['rustapi_module'], - rust_extensions=[RustExtension('rustapi_module.datetime', 'Cargo.toml')], + rust_extensions=[RustExtension('rustapi_module.datetime', 'Cargo.toml', + rustc_flags=get_py_version_cfgs())], install_requires=install_requires, tests_require=tests_require, setup_requires=setup_requires, diff --git a/tests/rustapi_module/tox.ini b/tests/rustapi_module/tox.ini new file mode 100644 index 00000000..11e866b5 --- /dev/null +++ b/tests/rustapi_module/tox.ini @@ -0,0 +1,14 @@ +[tox] +envlist = py27, + py35, + py36, + py37, +minversion = 2.9.0 +skip_missing_interpreters = true + +[testenv] +description = Run the unit tests under {basepython} +deps = -rrequirements-dev.txt +usedevelop = True +commands = pip install -e . + pytest From 08e7e0f55a3015b60b7f1996e5961026ba682b6e Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 8 Aug 2018 14:41:39 -0400 Subject: [PATCH 16/44] Add PyDelta component accessors Adds the PyDateTime_DELTA_GET_{comp} accessors and bindings to them in the PyDelta object. --- src/ffi3/datetime.rs | 42 +++++++++++++++++++++ src/objects/datetime.rs | 26 +++++++++++++ src/objects/mod.rs | 1 + tests/rustapi_module/src/lib.rs | 7 ++++ tests/rustapi_module/tests/test_datetime.py | 9 +++++ 5 files changed, 85 insertions(+) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 106be398..95ee36ce 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -1,6 +1,7 @@ use std::os::raw::c_int; use std::ffi::CString; use std::option::Option; +use ffi3::pyport::Py_hash_t; use ffi3::object::*; use ffi3::pycapsule::PyCapsule_Import; @@ -104,6 +105,16 @@ pub struct PyDateTime_CAPI { >, } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_Delta { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub days: c_int, + pub seconds: c_int, + pub microseconds: c_int, +} + unsafe impl Sync for PyDateTime_CAPI {} lazy_static! { @@ -145,3 +156,34 @@ pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int } + +// +// Accessor functions +// +macro_rules! _access_field { + ($obj:expr, $type: ident, $field:tt) => { + (*($obj as *mut $type)).$field + } +} + +// Accessor functions for PyDateTime_Delta +macro_rules! _access_delta_field { + ($obj:expr, $field:tt) => { + _access_field!($obj, PyDateTime_Delta, $field) + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, days) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, seconds) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, microseconds) +} diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index ee30e486..6f340b28 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -5,12 +5,19 @@ use ffi::{PyDateTimeAPI}; use ffi::{PyDateTime_DateType, PyDate_Check}; use ffi::{PyDateTime_DateTimeType, PyDateTime_Check}; use ffi::{PyDateTime_DeltaType, PyDelta_Check}; +use ffi::{PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS}; use ffi::{PyDateTime_TimeType, PyTime_Check}; use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; use python::{Python, ToPyPointer}; use instance::Py; +pub trait PyDeltaComponentAccess { + fn get_days(&self) -> i32; + fn get_seconds(&self) -> i32; + fn get_microseconds(&self) -> i32; +} + // datetime.date bindings pub struct PyDate(PyObject); @@ -142,3 +149,22 @@ impl PyDelta { } } +impl PyDeltaComponentAccess for PyDelta { + fn get_days(&self) -> i32 { + unsafe { + PyDateTime_DELTA_GET_DAYS(self.as_ptr()) as i32 + } + } + + fn get_seconds(&self) -> i32 { + unsafe { + PyDateTime_DELTA_GET_SECONDS(self.as_ptr()) as i32 + } + } + + fn get_microseconds(&self) -> i32 { + unsafe { + PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) as i32 + } + } +} diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 65539649..18d6109e 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -6,6 +6,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; pub use self::datetime::{PyDate,PyTime,PyDateTime,PyDelta,PyTzInfo}; +pub use self::datetime::{PyDeltaComponentAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 9d9e9b32..edcaf4db 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -9,6 +9,7 @@ use pyo3::prelude::{pyfunction, pymodinit}; use pyo3::prelude::{PyObject}; use pyo3::prelude::{PyModule}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +use pyo3::prelude::{PyDeltaComponentAccess}; use pyo3::prelude::{PyTuple, PyDict}; @@ -44,6 +45,11 @@ fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResul PyDelta::new(py, days, seconds, microseconds, true) } +#[pyfunction] +fn get_delta_tuple(py: Python, delta: &PyDelta) -> Py { + PyTuple::new(py, &[delta.get_days(), delta.get_seconds(), delta.get_microseconds()]) +} + #[pyfunction] fn make_datetime(py: Python, year: u32, month: u32, day: u32, hour: u32, minute: u32, second: u32, microsecond: u32, @@ -87,6 +93,7 @@ fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(date_from_timestamp))?; m.add_function(wrap_function!(make_time))?; m.add_function(wrap_function!(make_delta))?; + m.add_function(wrap_function!(get_delta_tuple))?; m.add_function(wrap_function!(make_datetime))?; m.add_function(wrap_function!(datetime_from_timestamp))?; diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 3bb7b4fd..76fed521 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -5,6 +5,7 @@ import datetime as pdt import pytest from hypothesis import given +from hypothesis import strategies as st from hypothesis.strategies import dates, datetimes # Constants @@ -152,6 +153,14 @@ def test_delta(args): assert act == exp +@given(td=st.timedeltas()) +def test_delta_accessors(td): + act = rdt.get_delta_tuple(td) + exp = (td.days, td.seconds, td.microseconds) + + assert act == exp + + @pytest.mark.parametrize('args,err_type', [ ((MAX_DAYS + 1, 0, 0), OverflowError), ((MIN_DAYS - 1, 0, 0), OverflowError), From 0b3945227608a6bdfadb20ab039384e650fdb104 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Wed, 8 Aug 2018 20:27:24 -0400 Subject: [PATCH 17/44] Add accessors to PyDateTime_Date --- src/ffi3/datetime.rs | 62 ++++++++++++++++++++- src/objects/datetime.rs | 28 ++++++++++ src/objects/mod.rs | 3 +- tests/rustapi_module/src/lib.rs | 7 +++ tests/rustapi_module/tests/test_datetime.py | 8 +++ 5 files changed, 106 insertions(+), 2 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 95ee36ce..af82c50a 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -1,4 +1,4 @@ -use std::os::raw::c_int; +use std::os::raw::{c_int, c_char, c_uchar}; use std::ffi::CString; use std::option::Option; use ffi3::pyport::Py_hash_t; @@ -105,6 +105,19 @@ pub struct PyDateTime_CAPI { >, } +// Type struct wrappers + +const _PyDateTime_DATE_DATASIZE : usize = 4; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_Date { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub hastzinfo: c_char, + pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyDateTime_Delta { @@ -115,6 +128,8 @@ pub struct PyDateTime_Delta { pub microseconds: c_int, } + +// C API Capsule unsafe impl Sync for PyDateTime_CAPI {} lazy_static! { @@ -132,6 +147,9 @@ pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { } +// +// Type Check macros +// #[inline(always)] pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int @@ -166,6 +184,48 @@ macro_rules! _access_field { } } +// Accessor functions for PyDateTime_Date +// Note: These have nonsensical names +#[macro_export] +macro_rules! PyDateTime_GET_YEAR { + // This is a macro in the C API and it's difficult to get the same behavior + // without making it a macro in Rust as well, or playing with pointers + ($o: expr) => { + (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int { + PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date) +} + +#[macro_export] +macro_rules! PyDateTime_GET_MONTH { + ($o: expr) => { + (*$o).data[2] as c_int + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int { + PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date) +} + +#[macro_export] +macro_rules! PyDateTime_GET_DAY { + ($o: expr) => { + (*$o).data[3] as c_int + } +} + + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int { + PyDateTime_GET_DAY!(o as *mut PyDateTime_Date) +} + + // Accessor functions for PyDateTime_Delta macro_rules! _access_delta_field { ($obj:expr, $field:tt) => { diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 6f340b28..9d755e0f 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -3,6 +3,7 @@ use object::PyObject; use std::os::raw::c_int; use ffi::{PyDateTimeAPI}; 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_DeltaType, PyDelta_Check}; use ffi::{PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS}; @@ -11,6 +12,12 @@ use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; use python::{Python, ToPyPointer}; use instance::Py; +// Traits +pub trait PyDateComponentAccess { + fn get_year(&self) -> u32; + fn get_month(&self) -> u32; + fn get_day(&self) -> u32; +} pub trait PyDeltaComponentAccess { fn get_days(&self) -> i32; @@ -42,8 +49,29 @@ impl PyDate { Py::from_owned_ptr_or_err(py, ptr) } } + + } +impl PyDateComponentAccess for PyDate { + fn get_year(&self) -> u32 { + unsafe { + PyDateTime_Date_GET_YEAR(self.as_ptr()) as u32 + } + } + + fn get_month(&self) -> u32 { + unsafe { + PyDateTime_Date_GET_MONTH(self.as_ptr()) as u32 + } + } + + fn get_day(&self) -> u32 { + unsafe { + PyDateTime_Date_GET_DAY(self.as_ptr()) as u32 + } + } +} // datetime.datetime bindings pub struct PyDateTime(PyObject); diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 18d6109e..810989ac 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,7 +5,8 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::{PyDate,PyTime,PyDateTime,PyDelta,PyTzInfo}; +pub use self::datetime::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +pub use self::datetime::{PyDateComponentAccess}; pub use self::datetime::{PyDeltaComponentAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index edcaf4db..a8267660 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -9,6 +9,7 @@ use pyo3::prelude::{pyfunction, pymodinit}; use pyo3::prelude::{PyObject}; use pyo3::prelude::{PyModule}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +use pyo3::prelude::{PyDateComponentAccess}; use pyo3::prelude::{PyDeltaComponentAccess}; use pyo3::prelude::{PyTuple, PyDict}; @@ -26,6 +27,11 @@ fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult PyDate::new(py, year, month, day) } +#[pyfunction] +fn get_date_tuple(py: Python, d: &PyDate) -> Py { + PyTuple::new(py, &[d.get_year(), d.get_month(), d.get_day()]) +} + #[pyfunction] fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { let timestamp = ts.to_object(py); @@ -90,6 +96,7 @@ fn time_with_fold(py: Python, hour: u32, minute: u32, second: u32, #[pymodinit] fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(make_date))?; + 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!(make_delta))?; diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 76fed521..6678f4a8 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -30,6 +30,14 @@ def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) +@given(d=st.dates()) +def test_date_accessors(d): + act = rdt.get_date_tuple(d) + exp = (d.year, d.month, d.day) + + assert act == exp + + def test_invalid_date_fails(): with pytest.raises(ValueError): rdt.make_date(2017, 2, 30) From ecf3aaafb2f127ad00921998abd646714d5fd599 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 10:50:46 -0400 Subject: [PATCH 18/44] Add PyDateTime component accessors --- src/ffi3/datetime.rs | 101 +++++++++++++++++++- src/objects/datetime.rs | 73 ++++++++++++++ src/objects/mod.rs | 2 +- tests/rustapi_module/src/lib.rs | 19 +++- tests/rustapi_module/tests/test_datetime.py | 20 ++++ 5 files changed, 212 insertions(+), 3 deletions(-) diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index af82c50a..18dad303 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_DATETIME_DATASIZE: usize = 10; #[repr(C)] #[derive(Debug, Copy, Clone)] @@ -118,6 +119,18 @@ pub struct PyDateTime_Date { pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], } +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_DateTime { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub hastzinfo: c_char, + pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], + #[cfg(Py_3_6)] + pub fold: c_uchar, + pub tzinfo: *mut PyObject +} + #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyDateTime_Delta { @@ -200,6 +213,11 @@ pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int { PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date) } +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { + PyDateTime_GET_YEAR!(o as *mut PyDateTime_DateTime) +} + #[macro_export] macro_rules! PyDateTime_GET_MONTH { ($o: expr) => { @@ -212,6 +230,11 @@ pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int { PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date) } +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { + PyDateTime_GET_MONTH!(o as *mut PyDateTime_DateTime) +} + #[macro_export] macro_rules! PyDateTime_GET_DAY { ($o: expr) => { @@ -219,12 +242,88 @@ macro_rules! PyDateTime_GET_DAY { } } - #[inline(always)] pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int { PyDateTime_GET_DAY!(o as *mut PyDateTime_Date) } +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { + PyDateTime_GET_DAY!(o as *mut PyDateTime_DateTime) +} + +// Accessor macros for times +macro_rules! _PyDateTime_GET_HOUR { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 0] as c_int + } +} + +macro_rules! _PyDateTime_GET_MINUTE { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 1] as c_int + } +} + +macro_rules! _PyDateTime_GET_SECOND { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 2] as c_int + } +} + +macro_rules! _PyDateTime_GET_MICROSECOND { + ($o: expr, $offset:expr) => { + (((*$o).data[$offset + 3] as c_int) << 16) | + (((*$o).data[$offset + 4] as c_int) << 8) | + ((*$o).data[$offset + 5] as c_int) + } +} + +#[cfg(Py_3_6)] +macro_rules! _PyDateTime_GET_FOLD{ + ($o: expr) => { + (*$o).fold + } +} + +macro_rules! _PyDateTime_GET_TZINFO{ + ($o: expr) => { + (*$o).tzinfo + } +} + +// Accessor functions for DateTime +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int { + _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[cfg(Py_3_6)] +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar { + _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_TZINFO(o: *mut PyObject) -> *mut PyObject { + _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_DateTime) +} + // Accessor functions for PyDateTime_Delta macro_rules! _access_delta_field { diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 9d755e0f..55fdf9b8 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -5,10 +5,17 @@ use ffi::{PyDateTimeAPI}; 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_DATE_GET_HOUR, PyDateTime_DATE_GET_MINUTE, + PyDateTime_DATE_GET_SECOND, PyDateTime_DATE_GET_MICROSECOND}; use ffi::{PyDateTime_DeltaType, PyDelta_Check}; use ffi::{PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS}; use ffi::{PyDateTime_TimeType, PyTime_Check}; use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; + +#[cfg(Py_3_6)] +use ffi::{PyDateTime_DATE_GET_FOLD}; + use python::{Python, ToPyPointer}; use instance::Py; @@ -26,6 +33,16 @@ pub trait PyDeltaComponentAccess { } +pub trait PyTimeComponentAccess { + fn get_hour(&self) -> u32; + fn get_minute(&self) -> u32; + fn get_second(&self) -> u32; + fn get_microsecond(&self) -> u32; + #[cfg(Py_3_6)] + fn get_fold(&self) -> u8; +} + + // datetime.date bindings pub struct PyDate(PyObject); pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); @@ -73,6 +90,7 @@ impl PyDateComponentAccess for PyDate { } } + // datetime.datetime bindings pub struct PyDateTime(PyObject); pyobject_native_type!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check); @@ -108,8 +126,63 @@ impl PyDateTime { Py::from_owned_ptr_or_err(py, ptr) } } + } +impl PyDateComponentAccess for PyDateTime { + fn get_year(&self) -> u32 { + unsafe { + PyDateTime_DateTime_GET_YEAR(self.as_ptr()) as u32 + } + } + + fn get_month(&self) -> u32 { + unsafe { + PyDateTime_DateTime_GET_MONTH(self.as_ptr()) as u32 + } + } + + fn get_day(&self) -> u32 { + unsafe { + PyDateTime_DateTime_GET_DAY(self.as_ptr()) as u32 + } + } +} + +impl PyTimeComponentAccess for PyDateTime { + fn get_hour(&self) -> u32 { + unsafe { + PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32 + } + } + + fn get_minute(&self) -> u32 { + unsafe { + PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u32 + } + } + + fn get_second(&self) -> u32 { + unsafe { + PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u32 + } + } + + fn get_microsecond(&self) -> u32 { + unsafe { + PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 + } + } + + #[cfg(Py_3_6)] + fn get_fold(&self) -> u8 { + unsafe { + PyDateTime_DATE_GET_FOLD(self.as_ptr()) as u8 + } + } +} + + // datetime.time pub struct PyTime(PyObject); pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check); diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 810989ac..199785d1 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -6,7 +6,7 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; pub use self::datetime::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; -pub use self::datetime::{PyDateComponentAccess}; +pub use self::datetime::{PyDateComponentAccess, PyTimeComponentAccess}; pub use self::datetime::{PyDeltaComponentAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index a8267660..c43a1fba 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -9,7 +9,7 @@ use pyo3::prelude::{pyfunction, pymodinit}; use pyo3::prelude::{PyObject}; use pyo3::prelude::{PyModule}; use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; -use pyo3::prelude::{PyDateComponentAccess}; +use pyo3::prelude::{PyDateComponentAccess, PyTimeComponentAccess}; use pyo3::prelude::{PyDeltaComponentAccess}; use pyo3::prelude::{PyTuple, PyDict}; @@ -67,6 +67,21 @@ fn make_datetime(py: Python, year: u32, month: u32, day: u32, PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) } +#[pyfunction] +fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py { + PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(), + dt.get_hour(), dt.get_minute(), dt.get_second(), + dt.get_microsecond()]) +} + +#[cfg(Py_3_6)] +#[pyfunction] +fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py { + PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(), + dt.get_hour(), dt.get_minute(), dt.get_second(), + dt.get_microsecond(), dt.get_fold() as u32]) +} + #[pyfunction] fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult> { let timestamp : PyObject = ts.to_object(py); @@ -102,12 +117,14 @@ fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(make_delta))?; m.add_function(wrap_function!(get_delta_tuple))?; m.add_function(wrap_function!(make_datetime))?; + m.add_function(wrap_function!(get_datetime_tuple))?; m.add_function(wrap_function!(datetime_from_timestamp))?; // Python 3.6+ functions #[cfg(Py_3_6)] { m.add_function(wrap_function!(time_with_fold)); + m.add_function(wrap_function!(get_datetime_tuple_fold)); } Ok(()) diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 6678f4a8..252b356d 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -119,6 +119,26 @@ def test_datetime(args, kwargs): assert act.tzinfo is exp.tzinfo +@given(dt=st.datetimes()) +def test_datetime_tuple(dt): + act = rdt.get_datetime_tuple(dt) + exp = dt.timetuple()[0:6] + (dt.microsecond, ) + + assert act == exp + + +@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) + + for dt in (dt_fold, dt_nofold): + act = rdt.get_datetime_tuple_fold(dt) + exp = dt.timetuple()[0:6] + (dt.microsecond, dt.fold) + + assert act == exp + def test_invalid_datetime_fails(): with pytest.raises(ValueError): rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) From f701bccbdf5e77ca6d2435521a36036bc4d44584 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 11:25:33 -0400 Subject: [PATCH 19/44] 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) From cd6f7295a1e993b8e90cef3bb7b1ee5c2059ebdd Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 14:17:34 -0400 Subject: [PATCH 20/44] Add type checking FFI bindings --- src/ffi3/datetime.rs | 29 ++++- tests/rustapi_module/tests/test_datetime.py | 8 ++ tests/test_datetime.rs | 126 ++++++++++++++++++++ 3 files changed, 161 insertions(+), 2 deletions(-) create mode 100644 tests/test_datetime.rs diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index f09ea3da..f90e0347 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -181,14 +181,19 @@ pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int } +#[inline(always)] +pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int +} + #[inline(always)] pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int } #[inline(always)] -pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int +pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int } #[inline(always)] @@ -196,11 +201,31 @@ pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int } +#[inline(always)] +pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int +} + #[inline(always)] pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int } +#[inline(always)] +pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int +} + // // Accessor functions // diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 285ecb1f..93764f31 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -25,6 +25,13 @@ def get_timestamp(dt): return dt.timestamp() + + + + + + + # Tests def test_date(): assert rdt.make_date(2017, 9, 1) == pdt.date(2017, 9, 1) @@ -224,3 +231,4 @@ def test_delta_accessors(td): def test_delta_err(args, err_type): with pytest.raises(err_type): rdt.make_delta(*args) + diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs new file mode 100644 index 00000000..deadb49e --- /dev/null +++ b/tests/test_datetime.rs @@ -0,0 +1,126 @@ +#![feature(concat_idents)] + +extern crate pyo3; + +use pyo3::prelude::*; + +use pyo3::ffi::*; + +#[cfg(Py_3)] +fn _get_subclasses<'p>(py: &'p Python, py_type: &str, args: &str) -> + (&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef) { + macro_rules! unwrap_py { + ($e:expr) => { ($e).map_err(|e| e.print(*py)).unwrap() } + }; + + // Import the class from Python and create some subclasses + let datetime = unwrap_py!(py.import("datetime")); + + let locals = PyDict::new(*py); + locals.set_item(py_type, datetime.get(py_type).unwrap()) + .unwrap(); + + let make_subclass_py = + format!("class Subklass({}):\n pass", py_type); + + let make_sub_subclass_py = + "class SubSubklass(Subklass):\n pass"; + + unwrap_py!(py.run(&make_subclass_py, None, Some(&locals))); + unwrap_py!(py.run(&make_sub_subclass_py, None, Some(&locals))); + + // Construct an instance of the base class + let obj = unwrap_py!( + py.eval(&format!("{}({})", py_type, args), None, Some(&locals)) + ); + + // Construct an instance of the subclass + let sub_obj = unwrap_py!( + py.eval(&format!("Subklass({})", args), None, Some(&locals)) + ); + + // Construct an instance of the sub-subclass + let sub_sub_obj = unwrap_py!( + py.eval(&format!("SubSubklass({})", args), None, Some(&locals)) + ); + + (obj, sub_obj, sub_sub_obj) +} + +#[cfg(Py_3)] +macro_rules! assert_check_exact { + ($check_func:ident, $obj: expr) => { + unsafe { + assert!($check_func(($obj).as_ptr()) != 0); + assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) != 0); + } + } +} + +#[cfg(Py_3)] +macro_rules! assert_check_only { + ($check_func:ident, $obj: expr) => { + unsafe { + assert!($check_func(($obj).as_ptr()) != 0); + assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) == 0); + } + } +} + + +#[test] +#[cfg(Py_3)] +fn test_date_check() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, + "date", "2018, 1, 1" + ); + + assert_check_exact!(PyDate_Check, obj); + assert_check_only!(PyDate_Check, sub_obj); + assert_check_only!(PyDate_Check, sub_sub_obj); +} + +#[test] +#[cfg(Py_3)] +fn test_time_check() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, + "time", "12, 30, 15" + ); + + assert_check_exact!(PyTime_Check, obj); + assert_check_only!(PyTime_Check, sub_obj); + assert_check_only!(PyTime_Check, sub_sub_obj); +} + +#[test] +#[cfg(Py_3)] +fn test_datetime_check() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, + "datetime", "2018, 1, 1, 13, 30, 15" + ); + + assert_check_only!(PyDate_Check, obj); + assert_check_exact!(PyDateTime_Check, obj); + assert_check_only!(PyDateTime_Check, sub_obj); + assert_check_only!(PyDateTime_Check, sub_sub_obj); +} + +#[test] +#[cfg(Py_3)] +fn test_delta_check() { + let gil = Python::acquire_gil(); + let py = gil.python(); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, + "timedelta", "1, -3" + ); + + assert_check_exact!(PyDelta_Check, obj); + assert_check_only!(PyDelta_Check, sub_obj); + assert_check_only!(PyDelta_Check, sub_sub_obj); +} From 996c2baec800e39e2e59ee8afb733ccd58f2c8c6 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 16:24:40 -0400 Subject: [PATCH 21/44] Add preliminary Python 2 support to datetime bindings The bounds checking tests are xfail because the datetime "fast path" constructor didn't do bounds checking until bpo-29100 was resolved in CPython commit b67f0967386a9c90411, which was merged for Python 3.6. This functionality should be simple to backport to earlier versions in a future commit. BPO issue: https://bugs.python.org/issue29100 CPython commit: https://github.com/python/cpython/commit/b67f0967386a9c9041166d2bbe0a421bd81e10bc --- src/ffi2/datetime.rs | 363 ++++++++++++++++++++ src/ffi2/mod.rs | 2 + tests/rustapi_module/tests/test_datetime.py | 51 ++- tests/test_datetime.rs | 7 - 4 files changed, 405 insertions(+), 18 deletions(-) create mode 100644 src/ffi2/datetime.rs diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs new file mode 100644 index 00000000..02e16f99 --- /dev/null +++ b/src/ffi2/datetime.rs @@ -0,0 +1,363 @@ +use std::os::raw::{c_int, c_char, c_uchar}; +use std::ffi::CString; +use std::option::Option; +use ffi2::pyport::Py_hash_t; +use ffi2::object::*; +use ffi2::pycapsule::PyCapsule_Import; + +#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { + pub static mut PyDateTime_DateType: PyTypeObject; + pub static mut PyDateTime_TimeType: PyTypeObject; + pub static mut PyDateTime_DateTimeType: PyTypeObject; + + pub static mut PyDateTime_DeltaType: PyTypeObject; + pub static mut PyDateTime_TZInfoType: PyTypeObject; +} + + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct PyDateTime_CAPI { + pub DateType: *mut PyTypeObject, + pub DateTimeType: *mut PyTypeObject, + pub TimeType: *mut PyTypeObject, + pub DeltaType: *mut PyTypeObject, + pub TZInfoType: *mut PyTypeObject, + + pub Date_FromDate: Option< + unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub DateTime_FromDateAndTime: Option< + unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub Time_FromTime: Option< + unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub Delta_FromDelta: Option< + unsafe extern "C" fn( + days: c_int, + seconds: c_int, + microseconds: c_int, + normalize: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + >, + pub DateTime_FromTimestamp: Option< + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) + -> *mut PyObject, + >, + pub Date_FromTimestamp: Option< + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, + >, +} + +// Type struct wrappers + +const _PyDateTime_DATE_DATASIZE : usize = 4; +const _PyDateTime_TIME_DATASIZE : usize = 6; +const _PyDateTime_DATETIME_DATASIZE: usize = 10; + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct PyDateTime_Date { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub hastzinfo: c_char, + pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], +} + +#[repr(C)] +#[derive(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], + pub tzinfo: *mut PyObject +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct PyDateTime_DateTime { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub hastzinfo: c_char, + pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], + pub tzinfo: *mut PyObject +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct PyDateTime_Delta { + pub ob_base: PyObject, + pub hashcode: Py_hash_t, + pub days: c_int, + pub seconds: c_int, + pub microseconds: c_int, +} + + +// C API Capsule +unsafe impl Sync for PyDateTime_CAPI {} + +lazy_static! { + pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +} + + +#[inline(always)] +pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { + // PyDateTime_CAPSULE_NAME is a macro in C + let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); + + let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 0); + *(capsule as *const PyDateTime_CAPI) +} + + +// +// Type Check macros +// +#[inline(always)] +pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int +} + +#[inline(always)] +pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int +} + +#[inline(always)] +pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int +} + +// +// Accessor functions +// +macro_rules! _access_field { + ($obj:expr, $type: ident, $field:tt) => { + (*($obj as *mut $type)).$field + } +} + +// Accessor functions for PyDateTime_Date +// Note: These have nonsensical names +#[macro_export] +macro_rules! PyDateTime_GET_YEAR { + // This is a macro in the C API and it's difficult to get the same behavior + // without making it a macro in Rust as well, or playing with pointers + ($o: expr) => { + (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int { + PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { + PyDateTime_GET_YEAR!(o as *mut PyDateTime_DateTime) +} + +#[macro_export] +macro_rules! PyDateTime_GET_MONTH { + ($o: expr) => { + (*$o).data[2] as c_int + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int { + PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { + PyDateTime_GET_MONTH!(o as *mut PyDateTime_DateTime) +} + +#[macro_export] +macro_rules! PyDateTime_GET_DAY { + ($o: expr) => { + (*$o).data[3] as c_int + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int { + PyDateTime_GET_DAY!(o as *mut PyDateTime_Date) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { + PyDateTime_GET_DAY!(o as *mut PyDateTime_DateTime) +} + +// Accessor macros for times +macro_rules! _PyDateTime_GET_HOUR { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 0] as c_int + } +} + +macro_rules! _PyDateTime_GET_MINUTE { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 1] as c_int + } +} + +macro_rules! _PyDateTime_GET_SECOND { + ($o: expr, $offset:expr) => { + (*$o).data[$offset + 2] as c_int + } +} + +macro_rules! _PyDateTime_GET_MICROSECOND { + ($o: expr, $offset:expr) => { + (((*$o).data[$offset + 3] as c_int) << 16) | + (((*$o).data[$offset + 4] as c_int) << 8) | + ((*$o).data[$offset + 5] as c_int) + } +} + +macro_rules! _PyDateTime_GET_TZINFO{ + ($o: expr) => { + (*$o).tzinfo + } +} + +// Accessor functions for DateTime +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int { + _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[inline(always)] +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) +} + +#[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 { + ($obj:expr, $field:tt) => { + _access_field!($obj, PyDateTime_Delta, $field) + } +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, days) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, seconds) +} + +#[inline(always)] +pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, microseconds) +} diff --git a/src/ffi2/mod.rs b/src/ffi2/mod.rs index 29a160a7..3a30b67d 100644 --- a/src/ffi2/mod.rs +++ b/src/ffi2/mod.rs @@ -16,6 +16,7 @@ pub use self::cobject::*; pub use self::code::*; pub use self::compile::*; pub use self::complexobject::*; +pub use self::datetime::*; pub use self::descrobject::*; pub use self::dictobject::*; pub use self::enumobject::*; @@ -66,6 +67,7 @@ mod cellobject; mod classobject; mod cobject; mod complexobject; +mod datetime; mod descrobject; mod dictobject; mod enumobject; diff --git a/tests/rustapi_module/tests/test_datetime.py b/tests/rustapi_module/tests/test_datetime.py index 93764f31..ccfa6ae2 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -1,5 +1,6 @@ import rustapi_module.datetime as rdt +import sys import datetime as pdt import pytest @@ -9,11 +10,36 @@ from hypothesis import strategies as st from hypothesis.strategies import dates, datetimes # Constants -UTC = pdt.timezone.utc -MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) -MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1) +def _get_utc(): + timezone = getattr(pdt, 'timezone', None) + if timezone: + return timezone.utc + else: + class UTC(pdt.tzinfo): + def utcoffset(self, dt): + return pdt.timedelta(0) + + def dst(self, dt): + return pdt.timedelta(0) + + def tzname(self, dt): + return "UTC" + + return UTC() + +UTC = _get_utc() + MAX_SECONDS = int(pdt.timedelta.max.total_seconds()) MIN_SECONDS = int(pdt.timedelta.min.total_seconds()) + +try: + MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) + MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1) +except Exception: + # Python 2 compatibility + MAX_DAYS = MAX_SECONDS // pdt.timedelta(days=1).total_seconds() + MIN_DAYS = MIN_SECONDS // pdt.timedelta(days=1).total_seconds() + MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) @@ -21,16 +47,15 @@ HAS_FOLD = getattr(pdt.datetime, 'fold', False) # Helper functions -def get_timestamp(dt): - return dt.timestamp() - - - - - - +get_timestamp = getattr(pdt.datetime, 'timestamp', None) +if get_timestamp is None: + def get_timestamp(dt): + # Python 2 compatibility + return (dt - pdt.datetime(1970, 1, 1)).total_seconds() +xfail_date_bounds = pytest.mark.xfail(sys.version_info < (3, 6), + reason="Date bounds were not checked in the C constructor prior to version 3.6") # Tests def test_date(): @@ -45,6 +70,7 @@ def test_date_accessors(d): assert act == exp +@xfail_date_bounds def test_invalid_date_fails(): with pytest.raises(ValueError): rdt.make_date(2017, 2, 30) @@ -109,6 +135,7 @@ def test_invalid_time_fails_xfail(args): rdt.make_time(*args) +@xfail_date_bounds @pytest.mark.parametrize('args', [ (24, 0, 0, 0), (25, 0, 0, 0), @@ -167,6 +194,8 @@ def test_datetime_tuple_fold(dt): assert act == exp + +@xfail_date_bounds def test_invalid_datetime_fails(): with pytest.raises(ValueError): rdt.make_datetime(2011, 1, 42, 0, 0, 0, 0) diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index deadb49e..0fffb396 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -6,7 +6,6 @@ use pyo3::prelude::*; use pyo3::ffi::*; -#[cfg(Py_3)] fn _get_subclasses<'p>(py: &'p Python, py_type: &str, args: &str) -> (&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef) { macro_rules! unwrap_py { @@ -47,7 +46,6 @@ fn _get_subclasses<'p>(py: &'p Python, py_type: &str, args: &str) -> (obj, sub_obj, sub_sub_obj) } -#[cfg(Py_3)] macro_rules! assert_check_exact { ($check_func:ident, $obj: expr) => { unsafe { @@ -57,7 +55,6 @@ macro_rules! assert_check_exact { } } -#[cfg(Py_3)] macro_rules! assert_check_only { ($check_func:ident, $obj: expr) => { unsafe { @@ -69,7 +66,6 @@ macro_rules! assert_check_only { #[test] -#[cfg(Py_3)] fn test_date_check() { let gil = Python::acquire_gil(); let py = gil.python(); @@ -83,7 +79,6 @@ fn test_date_check() { } #[test] -#[cfg(Py_3)] fn test_time_check() { let gil = Python::acquire_gil(); let py = gil.python(); @@ -97,7 +92,6 @@ fn test_time_check() { } #[test] -#[cfg(Py_3)] fn test_datetime_check() { let gil = Python::acquire_gil(); let py = gil.python(); @@ -112,7 +106,6 @@ fn test_datetime_check() { } #[test] -#[cfg(Py_3)] fn test_delta_check() { let gil = Python::acquire_gil(); let py = gil.python(); From 7fc1dae2382f75dc073e22aabac7ed27568656cf Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 16:26:17 -0400 Subject: [PATCH 22/44] Add rustapi_module tests to CI --- .travis.yml | 2 +- ci/travis/test.sh | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 08a8ed25..008d4c88 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,7 +38,7 @@ before_install: - source ./ci/travis/setup.sh install: - - pip install setuptools-rust pytest pytest-benchmark + - pip install setuptools-rust pytest pytest-benchmark tox script: - ./ci/travis/test.sh diff --git a/ci/travis/test.sh b/ci/travis/test.sh index 095ad69b..4f69b17a 100755 --- a/ci/travis/test.sh +++ b/ci/travis/test.sh @@ -11,3 +11,7 @@ for example in examples/*; do pytest -v tests cd $TRAVIS_BUILD_DIR done + +cd tests/rustapi_module +tox -e py +cd $TRAVIS_BUILD_DIR From ee658de1fb45119bbd69385e64fec537da5d7573 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Thu, 9 Aug 2018 16:29:59 -0400 Subject: [PATCH 23/44] Run rustfmt after datetime changes Most of the datetime related changes were made before pyo3 switched to using rustfmt, so I ran rustfmt only on the final commit to make it easier to rewrite history as necessary (for fixups and whatnot). --- src/ffi2/datetime.rs | 65 ++++------ src/ffi3/datetime.rs | 77 +++++------ src/ffi3/mod.rs | 2 +- src/lib.rs | 3 +- src/objects/datetime.rs | 218 ++++++++++++++++---------------- src/objects/mod.rs | 4 +- tests/rustapi_module/src/lib.rs | 149 ++++++++++++++++------ tests/test_datetime.rs | 53 +++----- 8 files changed, 304 insertions(+), 267 deletions(-) diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs index 02e16f99..bda33ab2 100644 --- a/src/ffi2/datetime.rs +++ b/src/ffi2/datetime.rs @@ -1,11 +1,12 @@ -use std::os::raw::{c_int, c_char, c_uchar}; -use std::ffi::CString; -use std::option::Option; -use ffi2::pyport::Py_hash_t; use ffi2::object::*; use ffi2::pycapsule::PyCapsule_Import; +use ffi2::pyport::Py_hash_t; +use std::ffi::CString; +use std::option::Option; +use std::os::raw::{c_char, c_int, c_uchar}; -#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { pub static mut PyDateTime_DateType: PyTypeObject; pub static mut PyDateTime_TimeType: PyTypeObject; pub static mut PyDateTime_DateTimeType: PyTypeObject; @@ -14,7 +15,6 @@ use ffi2::pycapsule::PyCapsule_Import; pub static mut PyDateTime_TZInfoType: PyTypeObject; } - #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyDateTime_CAPI { @@ -25,12 +25,8 @@ pub struct PyDateTime_CAPI { pub TZInfoType: *mut PyTypeObject, pub Date_FromDate: Option< - unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, + unsafe extern "C" fn(year: c_int, month: c_int, day: c_int, cls: *mut PyTypeObject) + -> *mut PyObject, >, pub DateTime_FromDateAndTime: Option< unsafe extern "C" fn( @@ -68,15 +64,14 @@ pub struct PyDateTime_CAPI { unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject, >, - pub Date_FromTimestamp: Option< - unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, - >, + pub Date_FromTimestamp: + Option *mut PyObject>, } // Type struct wrappers -const _PyDateTime_DATE_DATASIZE : usize = 4; -const _PyDateTime_TIME_DATASIZE : usize = 6; +const _PyDateTime_DATE_DATASIZE: usize = 4; +const _PyDateTime_TIME_DATASIZE: usize = 6; const _PyDateTime_DATETIME_DATASIZE: usize = 10; #[repr(C)] @@ -95,7 +90,7 @@ pub struct PyDateTime_Time { pub hashcode: Py_hash_t, pub hastzinfo: c_char, pub data: [c_uchar; _PyDateTime_TIME_DATASIZE], - pub tzinfo: *mut PyObject + pub tzinfo: *mut PyObject, } #[repr(C)] @@ -105,7 +100,7 @@ pub struct PyDateTime_DateTime { pub hashcode: Py_hash_t, pub hastzinfo: c_char, pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], - pub tzinfo: *mut PyObject + pub tzinfo: *mut PyObject, } #[repr(C)] @@ -118,7 +113,6 @@ pub struct PyDateTime_Delta { pub microseconds: c_int, } - // C API Capsule unsafe impl Sync for PyDateTime_CAPI {} @@ -126,7 +120,6 @@ lazy_static! { pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; } - #[inline(always)] pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C @@ -136,7 +129,6 @@ pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { *(capsule as *const PyDateTime_CAPI) } - // // Type Check macros // @@ -196,7 +188,7 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { macro_rules! _access_field { ($obj:expr, $type: ident, $field:tt) => { (*($obj as *mut $type)).$field - } + }; } // Accessor functions for PyDateTime_Date @@ -207,7 +199,7 @@ macro_rules! PyDateTime_GET_YEAR { // without making it a macro in Rust as well, or playing with pointers ($o: expr) => { (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) - } + }; } #[inline(always)] @@ -224,7 +216,7 @@ pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { macro_rules! PyDateTime_GET_MONTH { ($o: expr) => { (*$o).data[2] as c_int - } + }; } #[inline(always)] @@ -241,7 +233,7 @@ pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { macro_rules! PyDateTime_GET_DAY { ($o: expr) => { (*$o).data[3] as c_int - } + }; } #[inline(always)] @@ -258,33 +250,33 @@ pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { macro_rules! _PyDateTime_GET_HOUR { ($o: expr, $offset:expr) => { (*$o).data[$offset + 0] as c_int - } + }; } macro_rules! _PyDateTime_GET_MINUTE { ($o: expr, $offset:expr) => { (*$o).data[$offset + 1] as c_int - } + }; } macro_rules! _PyDateTime_GET_SECOND { ($o: expr, $offset:expr) => { (*$o).data[$offset + 2] as c_int - } + }; } macro_rules! _PyDateTime_GET_MICROSECOND { ($o: expr, $offset:expr) => { - (((*$o).data[$offset + 3] as c_int) << 16) | - (((*$o).data[$offset + 4] as c_int) << 8) | - ((*$o).data[$offset + 5] as c_int) - } + (((*$o).data[$offset + 3] as c_int) << 16) + | (((*$o).data[$offset + 4] as c_int) << 8) + | ((*$o).data[$offset + 5] as c_int) + }; } -macro_rules! _PyDateTime_GET_TZINFO{ +macro_rules! _PyDateTime_GET_TZINFO { ($o: expr) => { (*$o).tzinfo - } + }; } // Accessor functions for DateTime @@ -339,12 +331,11 @@ 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 { ($obj:expr, $field:tt) => { _access_field!($obj, PyDateTime_Delta, $field) - } + }; } #[inline(always)] diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index f90e0347..0182b0d1 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -1,11 +1,12 @@ -use std::os::raw::{c_int, c_char, c_uchar}; -use std::ffi::CString; -use std::option::Option; -use ffi3::pyport::Py_hash_t; use ffi3::object::*; use ffi3::pycapsule::PyCapsule_Import; +use ffi3::pyport::Py_hash_t; +use std::ffi::CString; +use std::option::Option; +use std::os::raw::{c_char, c_int, c_uchar}; -#[cfg_attr(windows, link(name="pythonXY"))] extern "C" { +#[cfg_attr(windows, link(name = "pythonXY"))] +extern "C" { pub static mut PyDateTime_DateType: PyTypeObject; pub static mut PyDateTime_TimeType: PyTypeObject; pub static mut PyDateTime_DateTimeType: PyTypeObject; @@ -14,7 +15,6 @@ use ffi3::pycapsule::PyCapsule_Import; pub static mut PyDateTime_TZInfoType: PyTypeObject; } - #[repr(C)] #[derive(Debug, Copy, Clone)] pub struct PyDateTime_CAPI { @@ -27,12 +27,8 @@ pub struct PyDateTime_CAPI { pub TimeZone_UTC: *mut PyObject, pub Date_FromDate: Option< - unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, + unsafe extern "C" fn(year: c_int, month: c_int, day: c_int, cls: *mut PyTypeObject) + -> *mut PyObject, >, pub DateTime_FromDateAndTime: Option< unsafe extern "C" fn( @@ -67,16 +63,14 @@ pub struct PyDateTime_CAPI { ) -> *mut PyObject, >, #[cfg(Py_3_7)] - pub TimeZone_FromTimeZone: Option< - unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, - >, + pub TimeZone_FromTimeZone: + Option *mut PyObject>, pub DateTime_FromTimestamp: Option< unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject, >, - pub Date_FromTimestamp: Option< - unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, - >, + pub Date_FromTimestamp: + Option *mut PyObject>, #[cfg(Py_3_6)] pub DateTime_FromDateAndTimeAndFold: Option< unsafe extern "C" fn( @@ -93,7 +87,8 @@ pub struct PyDateTime_CAPI { ) -> *mut PyObject, >, #[cfg(Py_3_6)] - pub Time_FromTimeAndFold: Option< unsafe extern "C" fn( + pub Time_FromTimeAndFold: Option< + unsafe extern "C" fn( hour: c_int, minute: c_int, second: c_int, @@ -107,8 +102,8 @@ pub struct PyDateTime_CAPI { // Type struct wrappers -const _PyDateTime_DATE_DATASIZE : usize = 4; -const _PyDateTime_TIME_DATASIZE : usize = 6; +const _PyDateTime_DATE_DATASIZE: usize = 4; +const _PyDateTime_TIME_DATASIZE: usize = 6; const _PyDateTime_DATETIME_DATASIZE: usize = 10; #[repr(C)] @@ -129,7 +124,7 @@ pub struct PyDateTime_Time { pub data: [c_uchar; _PyDateTime_TIME_DATASIZE], #[cfg(Py_3_6)] pub fold: c_uchar, - pub tzinfo: *mut PyObject + pub tzinfo: *mut PyObject, } #[repr(C)] @@ -141,7 +136,7 @@ pub struct PyDateTime_DateTime { pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], #[cfg(Py_3_6)] pub fold: c_uchar, - pub tzinfo: *mut PyObject + pub tzinfo: *mut PyObject, } #[repr(C)] @@ -154,7 +149,6 @@ pub struct PyDateTime_Delta { pub microseconds: c_int, } - // C API Capsule unsafe impl Sync for PyDateTime_CAPI {} @@ -162,7 +156,6 @@ lazy_static! { pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; } - #[inline(always)] pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C @@ -172,7 +165,6 @@ pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { *(capsule as *const PyDateTime_CAPI) } - // // Type Check macros // @@ -232,7 +224,7 @@ pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { macro_rules! _access_field { ($obj:expr, $type: ident, $field:tt) => { (*($obj as *mut $type)).$field - } + }; } // Accessor functions for PyDateTime_Date @@ -243,7 +235,7 @@ macro_rules! PyDateTime_GET_YEAR { // without making it a macro in Rust as well, or playing with pointers ($o: expr) => { (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) - } + }; } #[inline(always)] @@ -260,7 +252,7 @@ pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { macro_rules! PyDateTime_GET_MONTH { ($o: expr) => { (*$o).data[2] as c_int - } + }; } #[inline(always)] @@ -277,7 +269,7 @@ pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { macro_rules! PyDateTime_GET_DAY { ($o: expr) => { (*$o).data[3] as c_int - } + }; } #[inline(always)] @@ -294,40 +286,40 @@ pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { macro_rules! _PyDateTime_GET_HOUR { ($o: expr, $offset:expr) => { (*$o).data[$offset + 0] as c_int - } + }; } macro_rules! _PyDateTime_GET_MINUTE { ($o: expr, $offset:expr) => { (*$o).data[$offset + 1] as c_int - } + }; } macro_rules! _PyDateTime_GET_SECOND { ($o: expr, $offset:expr) => { (*$o).data[$offset + 2] as c_int - } + }; } macro_rules! _PyDateTime_GET_MICROSECOND { ($o: expr, $offset:expr) => { - (((*$o).data[$offset + 3] as c_int) << 16) | - (((*$o).data[$offset + 4] as c_int) << 8) | - ((*$o).data[$offset + 5] as c_int) - } + (((*$o).data[$offset + 3] as c_int) << 16) + | (((*$o).data[$offset + 4] as c_int) << 8) + | ((*$o).data[$offset + 5] as c_int) + }; } #[cfg(Py_3_6)] -macro_rules! _PyDateTime_GET_FOLD{ +macro_rules! _PyDateTime_GET_FOLD { ($o: expr) => { (*$o).fold - } + }; } -macro_rules! _PyDateTime_GET_TZINFO{ +macro_rules! _PyDateTime_GET_TZINFO { ($o: expr) => { (*$o).tzinfo - } + }; } // Accessor functions for DateTime @@ -394,12 +386,11 @@ 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 { ($obj:expr, $field:tt) => { _access_field!($obj, PyDateTime_Delta, $field) - } + }; } #[inline(always)] diff --git a/src/ffi3/mod.rs b/src/ffi3/mod.rs index c68d6cfc..9b416111 100644 --- a/src/ffi3/mod.rs +++ b/src/ffi3/mod.rs @@ -118,8 +118,8 @@ mod weakrefobject; // TODO supports PEP-384 only; needs adjustment for Python 3. mod codecs; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod pyerrors; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 -mod pystate; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod datetime; +mod pystate; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 #[cfg(Py_LIMITED_API)] mod pyarena {} diff --git a/src/lib.rs b/src/lib.rs index 842ab0e8..bf9b8038 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,7 +129,8 @@ extern crate spin; #[doc(hidden)] pub extern crate mashup; -#[macro_use] extern crate lazy_static; +#[macro_use] +extern crate lazy_static; #[cfg(not(Py_3))] mod ffi2; diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index d30b482f..90bc0429 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -1,25 +1,33 @@ use err::PyResult; +use ffi::PyDateTimeAPI; +use ffi::{PyDateTime_Check, PyDateTime_DateTimeType}; +use ffi::{ + PyDateTime_DATE_GET_HOUR, PyDateTime_DATE_GET_MICROSECOND, PyDateTime_DATE_GET_MINUTE, + PyDateTime_DATE_GET_SECOND, +}; +use ffi::{ + PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_MICROSECONDS, PyDateTime_DELTA_GET_SECONDS, +}; +use ffi::{ + PyDateTime_DateTime_GET_DAY, PyDateTime_DateTime_GET_MONTH, PyDateTime_DateTime_GET_YEAR, +}; +use ffi::{PyDateTime_DateType, PyDate_Check}; +use ffi::{PyDateTime_Date_GET_DAY, PyDateTime_Date_GET_MONTH, PyDateTime_Date_GET_YEAR}; +use ffi::{PyDateTime_DeltaType, PyDelta_Check}; +use ffi::{ + PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, + PyDateTime_TIME_GET_SECOND, +}; +use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; +use ffi::{PyDateTime_TimeType, PyTime_Check}; use object::PyObject; use std::os::raw::c_int; -use ffi::{PyDateTimeAPI}; -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}; -use ffi::{PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_SECONDS, PyDateTime_DELTA_GET_MICROSECONDS}; -use ffi::{PyDateTime_TimeType, PyTime_Check}; -use ffi::{PyDateTime_TZInfoType, PyTZInfo_Check}; #[cfg(Py_3_6)] use ffi::{PyDateTime_DATE_GET_FOLD, PyDateTime_TIME_GET_FOLD}; -use python::{Python, ToPyPointer}; use instance::Py; +use python::{Python, ToPyPointer}; // Traits pub trait PyDateComponentAccess { @@ -34,7 +42,6 @@ pub trait PyDeltaComponentAccess { fn get_microseconds(&self) -> i32; } - pub trait PyTimeComponentAccess { fn get_hour(&self) -> u32; fn get_minute(&self) -> u32; @@ -44,7 +51,6 @@ pub trait PyTimeComponentAccess { fn get_fold(&self) -> u8; } - // datetime.date bindings pub struct PyDate(PyObject); pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); @@ -63,45 +69,43 @@ impl PyDate { pub fn from_timestamp(py: Python, args: &PyObject) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.Date_FromTimestamp.unwrap() - (PyDateTimeAPI.DateType, args.as_ptr()); + let ptr = + PyDateTimeAPI.Date_FromTimestamp.unwrap()(PyDateTimeAPI.DateType, args.as_ptr()); Py::from_owned_ptr_or_err(py, ptr) } } - - } impl PyDateComponentAccess for PyDate { fn get_year(&self) -> u32 { - unsafe { - PyDateTime_Date_GET_YEAR(self.as_ptr()) as u32 - } + unsafe { PyDateTime_Date_GET_YEAR(self.as_ptr()) as u32 } } fn get_month(&self) -> u32 { - unsafe { - PyDateTime_Date_GET_MONTH(self.as_ptr()) as u32 - } + unsafe { PyDateTime_Date_GET_MONTH(self.as_ptr()) as u32 } } fn get_day(&self) -> u32 { - unsafe { - PyDateTime_Date_GET_DAY(self.as_ptr()) as u32 - } + unsafe { PyDateTime_Date_GET_DAY(self.as_ptr()) as u32 } } } - // datetime.datetime bindings pub struct PyDateTime(PyObject); 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, - tzinfo: &PyObject) -> PyResult> { + pub fn new( + py: Python, + year: u32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + microsecond: u32, + tzinfo: &PyObject, + ) -> PyResult> { let y = year as c_int; let mo = month as c_int; let d = day as c_int; @@ -112,86 +116,86 @@ impl PyDateTime { unsafe { let ptr = PyDateTimeAPI.DateTime_FromDateAndTime.unwrap()( - y, mo, d, h, mi, s, u, tzinfo.as_ptr(), - PyDateTimeAPI.DateTimeType + y, + mo, + d, + h, + mi, + s, + u, + tzinfo.as_ptr(), + PyDateTimeAPI.DateTimeType, ); Py::from_owned_ptr_or_err(py, ptr) } } - pub fn from_timestamp(py: Python, args: &PyObject, kwargs: &PyObject) -> - PyResult> { - + pub fn from_timestamp( + py: Python, + args: &PyObject, + kwargs: &PyObject, + ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.DateTime_FromTimestamp.unwrap() - (PyDateTimeAPI.DateTimeType, args.as_ptr(), kwargs.as_ptr()); + let ptr = PyDateTimeAPI.DateTime_FromTimestamp.unwrap()( + PyDateTimeAPI.DateTimeType, + args.as_ptr(), + kwargs.as_ptr(), + ); Py::from_owned_ptr_or_err(py, ptr) } } - } impl PyDateComponentAccess for PyDateTime { fn get_year(&self) -> u32 { - unsafe { - PyDateTime_DateTime_GET_YEAR(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DateTime_GET_YEAR(self.as_ptr()) as u32 } } fn get_month(&self) -> u32 { - unsafe { - PyDateTime_DateTime_GET_MONTH(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DateTime_GET_MONTH(self.as_ptr()) as u32 } } fn get_day(&self) -> u32 { - unsafe { - PyDateTime_DateTime_GET_DAY(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DateTime_GET_DAY(self.as_ptr()) as u32 } } } impl PyTimeComponentAccess for PyDateTime { fn get_hour(&self) -> u32 { - unsafe { - PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32 } } fn get_minute(&self) -> u32 { - unsafe { - PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u32 } } fn get_second(&self) -> u32 { - unsafe { - PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u32 } } fn get_microsecond(&self) -> u32 { - unsafe { - PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 - } + unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 } } #[cfg(Py_3_6)] fn get_fold(&self) -> u8 { - unsafe { - PyDateTime_DATE_GET_FOLD(self.as_ptr()) as u8 - } + unsafe { PyDateTime_DATE_GET_FOLD(self.as_ptr()) as u8 } } } - // datetime.time pub struct PyTime(PyObject); pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check); impl PyTime { - pub fn new(py: Python, hour: u32, minute: u32, second: u32, - microsecond: u32, tzinfo: &PyObject) -> PyResult> { + pub fn new( + py: Python, + hour: u32, + minute: u32, + second: u32, + microsecond: u32, + tzinfo: &PyObject, + ) -> PyResult> { let h = hour as c_int; let m = minute as c_int; let s = second as c_int; @@ -199,17 +203,21 @@ impl PyTime { let tzi = tzinfo.as_ptr(); unsafe { - let ptr = PyDateTimeAPI.Time_FromTime.unwrap()( - h, m, s, u, tzi, PyDateTimeAPI.TimeType - ); + let ptr = PyDateTimeAPI.Time_FromTime.unwrap()(h, m, s, u, tzi, PyDateTimeAPI.TimeType); Py::from_owned_ptr_or_err(py, ptr) } } #[cfg(Py_3_6)] - pub fn new_with_fold(py: Python, hour: u32, minute: u32, second: u32, - microsecond: u32, tzinfo: &PyObject, - fold: bool) -> PyResult> { + pub fn new_with_fold( + py: Python, + hour: u32, + minute: u32, + second: u32, + microsecond: u32, + tzinfo: &PyObject, + fold: bool, + ) -> PyResult> { let h = hour as c_int; let m = minute as c_int; let s = second as c_int; @@ -217,70 +225,66 @@ impl PyTime { let f = fold as c_int; unsafe { - let ptr = PyDateTimeAPI.Time_FromTimeAndFold.unwrap() - (h, m, s, u, tzinfo.as_ptr(), f, PyDateTimeAPI.TimeType); + let ptr = PyDateTimeAPI.Time_FromTimeAndFold.unwrap()( + h, + m, + s, + u, + tzinfo.as_ptr(), + f, + PyDateTimeAPI.TimeType, + ); Py::from_owned_ptr_or_err(py, ptr) } - } - } impl PyTimeComponentAccess for PyTime { fn get_hour(&self) -> u32 { - unsafe { - PyDateTime_TIME_GET_HOUR(self.as_ptr()) as 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 - } + 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 - } + 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 - } + 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 - } + 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); - // datetime.timedelta bindings pub struct PyDelta(PyObject); pyobject_native_type!(PyDelta, PyDateTime_DeltaType, PyDelta_Check); impl PyDelta { - pub fn new(py: Python, days: i32, seconds: i32, microseconds: i32, - normalize: bool) -> PyResult> { + pub fn new( + py: Python, + days: i32, + seconds: i32, + microseconds: i32, + normalize: bool, + ) -> PyResult> { let d = days as c_int; let s = seconds as c_int; let u = microseconds as c_int; let n = normalize as c_int; unsafe { - let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()( - d, s, u, n, PyDateTimeAPI.DeltaType - ); + let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()(d, s, u, n, PyDateTimeAPI.DeltaType); Py::from_owned_ptr_or_err(py, ptr) } } @@ -288,20 +292,14 @@ impl PyDelta { impl PyDeltaComponentAccess for PyDelta { fn get_days(&self) -> i32 { - unsafe { - PyDateTime_DELTA_GET_DAYS(self.as_ptr()) as i32 - } + unsafe { PyDateTime_DELTA_GET_DAYS(self.as_ptr()) as i32 } } fn get_seconds(&self) -> i32 { - unsafe { - PyDateTime_DELTA_GET_SECONDS(self.as_ptr()) as i32 - } + unsafe { PyDateTime_DELTA_GET_SECONDS(self.as_ptr()) as i32 } } fn get_microseconds(&self) -> i32 { - unsafe { - PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) as i32 - } + unsafe { PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) as i32 } } } diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 199785d1..29105349 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,9 +5,9 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +pub use self::datetime::PyDeltaComponentAccess; +pub use self::datetime::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo}; pub use self::datetime::{PyDateComponentAccess, PyTimeComponentAccess}; -pub use self::datetime::{PyDeltaComponentAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 5ccff192..44becbd7 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -3,25 +3,25 @@ #[macro_use] extern crate pyo3; -use pyo3::{Py, Python, PyResult}; -use pyo3::{ObjectProtocol, ToPyObject}; +use pyo3::prelude::PyDeltaComponentAccess; +use pyo3::prelude::PyModule; +use pyo3::prelude::PyObject; use pyo3::prelude::{pyfunction, pymodinit}; -use pyo3::prelude::{PyObject}; -use pyo3::prelude::{PyModule}; -use pyo3::prelude::{PyDate, PyTime, PyDateTime, PyDelta, PyTzInfo}; +use pyo3::prelude::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo}; use pyo3::prelude::{PyDateComponentAccess, PyTimeComponentAccess}; -use pyo3::prelude::{PyDeltaComponentAccess}; -use pyo3::prelude::{PyTuple, PyDict}; - +use pyo3::prelude::{PyDict, PyTuple}; +use pyo3::{ObjectProtocol, ToPyObject}; +use pyo3::{Py, PyResult, Python}; macro_rules! to_pyobject { - ($py:expr, $o:ident) => (match $o { - Some(t) => t.to_object($py), - None => $py.None() - }) + ($py:expr, $o:ident) => { + match $o { + Some(t) => t.to_object($py), + None => $py.None(), + } + }; } - #[pyfunction] fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { PyDate::new(py, year, month, day) @@ -40,32 +40,59 @@ fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { } #[pyfunction] -fn make_time(py: Python, hour: u32, minute: u32, second: u32, - microsecond: u32, tzinfo: Option<&PyTzInfo>) -> PyResult> { +fn make_time( + py: Python, + hour: u32, + minute: u32, + second: u32, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, +) -> PyResult> { let tzi: PyObject = to_pyobject!(py, tzinfo); 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> { +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()]) + 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]) + PyTuple::new( + py, + &[ + dt.get_hour(), + dt.get_minute(), + dt.get_second(), + dt.get_microsecond(), + dt.get_fold() as u32, + ], + ) } #[pyfunction] @@ -75,41 +102,85 @@ fn make_delta(py: Python, days: i32, seconds: i32, microseconds: i32) -> PyResul #[pyfunction] fn get_delta_tuple(py: Python, delta: &PyDelta) -> Py { - PyTuple::new(py, &[delta.get_days(), delta.get_seconds(), delta.get_microseconds()]) + PyTuple::new( + py, + &[ + delta.get_days(), + delta.get_seconds(), + delta.get_microseconds(), + ], + ) } #[pyfunction] -fn make_datetime(py: Python, year: u32, month: u32, day: u32, - hour: u32, minute: u32, second: u32, microsecond: u32, - tzinfo: Option<&PyTzInfo>) -> PyResult> { - let tzi : PyObject = match tzinfo { +fn make_datetime( + py: Python, + year: u32, + month: u32, + day: u32, + hour: u32, + minute: u32, + second: u32, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, +) -> PyResult> { + let tzi: PyObject = match tzinfo { Some(t) => t.to_object(py), None => py.None(), }; - PyDateTime::new(py, year, month, day, hour, minute, second, microsecond, &tzi) + PyDateTime::new( + py, + year, + month, + day, + hour, + minute, + second, + microsecond, + &tzi, + ) } #[pyfunction] fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py { - PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(), - dt.get_hour(), dt.get_minute(), dt.get_second(), - dt.get_microsecond()]) + PyTuple::new( + py, + &[ + dt.get_year(), + dt.get_month(), + dt.get_day(), + dt.get_hour(), + dt.get_minute(), + dt.get_second(), + dt.get_microsecond(), + ], + ) } #[cfg(Py_3_6)] #[pyfunction] fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py { - PyTuple::new(py, &[dt.get_year(), dt.get_month(), dt.get_day(), - dt.get_hour(), dt.get_minute(), dt.get_second(), - dt.get_microsecond(), dt.get_fold() as u32]) + PyTuple::new( + py, + &[ + dt.get_year(), + dt.get_month(), + dt.get_day(), + dt.get_hour(), + dt.get_minute(), + dt.get_second(), + dt.get_microsecond(), + dt.get_fold() as u32, + ], + ) } #[pyfunction] fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResult> { - let timestamp : PyObject = ts.to_object(py); - let tzi : PyObject = match tz { + let timestamp: PyObject = ts.to_object(py); + let tzi: PyObject = match tz { Some(t) => t.to_object(py), - None => py.None() + None => py.None(), }; let args = PyTuple::new(py, &[timestamp, tzi]); @@ -118,10 +189,6 @@ fn datetime_from_timestamp(py: Python, ts: f64, tz: Option<&PyTzInfo>) -> PyResu PyDateTime::from_timestamp(py, &args.to_object(py), &kwargs.to_object(py)) } - - - - #[pymodinit] fn datetime(_py: Python, m: &PyModule) -> PyResult<()> { m.add_function(wrap_function!(make_date))?; diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 0fffb396..2908905f 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -6,42 +6,40 @@ use pyo3::prelude::*; use pyo3::ffi::*; -fn _get_subclasses<'p>(py: &'p Python, py_type: &str, args: &str) -> - (&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef) { +fn _get_subclasses<'p>( + py: &'p Python, + py_type: &str, + args: &str, +) -> (&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef) { macro_rules! unwrap_py { - ($e:expr) => { ($e).map_err(|e| e.print(*py)).unwrap() } + ($e:expr) => { + ($e).map_err(|e| e.print(*py)).unwrap() + }; }; // Import the class from Python and create some subclasses let datetime = unwrap_py!(py.import("datetime")); let locals = PyDict::new(*py); - locals.set_item(py_type, datetime.get(py_type).unwrap()) + locals + .set_item(py_type, datetime.get(py_type).unwrap()) .unwrap(); - let make_subclass_py = - format!("class Subklass({}):\n pass", py_type); + let make_subclass_py = format!("class Subklass({}):\n pass", py_type); - let make_sub_subclass_py = - "class SubSubklass(Subklass):\n pass"; + let make_sub_subclass_py = "class SubSubklass(Subklass):\n pass"; unwrap_py!(py.run(&make_subclass_py, None, Some(&locals))); unwrap_py!(py.run(&make_sub_subclass_py, None, Some(&locals))); // Construct an instance of the base class - let obj = unwrap_py!( - py.eval(&format!("{}({})", py_type, args), None, Some(&locals)) - ); + let obj = unwrap_py!(py.eval(&format!("{}({})", py_type, args), None, Some(&locals))); // Construct an instance of the subclass - let sub_obj = unwrap_py!( - py.eval(&format!("Subklass({})", args), None, Some(&locals)) - ); + let sub_obj = unwrap_py!(py.eval(&format!("Subklass({})", args), None, Some(&locals))); // Construct an instance of the sub-subclass - let sub_sub_obj = unwrap_py!( - py.eval(&format!("SubSubklass({})", args), None, Some(&locals)) - ); + let sub_sub_obj = unwrap_py!(py.eval(&format!("SubSubklass({})", args), None, Some(&locals))); (obj, sub_obj, sub_sub_obj) } @@ -52,7 +50,7 @@ macro_rules! assert_check_exact { assert!($check_func(($obj).as_ptr()) != 0); assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) != 0); } - } + }; } macro_rules! assert_check_only { @@ -61,17 +59,14 @@ macro_rules! assert_check_only { assert!($check_func(($obj).as_ptr()) != 0); assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) == 0); } - } + }; } - #[test] fn test_date_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, - "date", "2018, 1, 1" - ); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1"); assert_check_exact!(PyDate_Check, obj); assert_check_only!(PyDate_Check, sub_obj); @@ -82,9 +77,7 @@ fn test_date_check() { fn test_time_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, - "time", "12, 30, 15" - ); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15"); assert_check_exact!(PyTime_Check, obj); assert_check_only!(PyTime_Check, sub_obj); @@ -95,9 +88,7 @@ fn test_time_check() { fn test_datetime_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, - "datetime", "2018, 1, 1, 13, 30, 15" - ); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15"); assert_check_only!(PyDate_Check, obj); assert_check_exact!(PyDateTime_Check, obj); @@ -109,9 +100,7 @@ fn test_datetime_check() { fn test_delta_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, - "timedelta", "1, -3" - ); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3"); assert_check_exact!(PyDelta_Check, obj); assert_check_only!(PyDelta_Check, sub_obj); From 12c20a75d9426eb151c965a3f29a7a5de95b3fdc Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 14 Aug 2018 14:37:43 -0400 Subject: [PATCH 24/44] Move datetime interface to exclusively use functions --- src/ffi2/datetime.rs | 63 ++++++++-------------------------- src/ffi3/datetime.rs | 75 +++++++++++------------------------------ src/objects/datetime.rs | 17 ++++------ 3 files changed, 41 insertions(+), 114 deletions(-) diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs index bda33ab2..a9c0850c 100644 --- a/src/ffi2/datetime.rs +++ b/src/ffi2/datetime.rs @@ -191,59 +191,24 @@ macro_rules! _access_field { }; } -// Accessor functions for PyDateTime_Date -// Note: These have nonsensical names -#[macro_export] -macro_rules! PyDateTime_GET_YEAR { - // This is a macro in the C API and it's difficult to get the same behavior - // without making it a macro in Rust as well, or playing with pointers - ($o: expr) => { - (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) - }; +// Accessor functions for PyDateTime_Date and PyDateTime_DateTime +#[inline] +pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { + // This should work for Date or DateTime + let d = *(o as *mut PyDateTime_Date); + (c_int::from(d.data[0]) << 8 | c_int::from(d.data[1])) } -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int { - PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date) +#[inline] +pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int { + let d = *(o as *mut PyDateTime_Date); + c_int::from(d.data[2]) } -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { - PyDateTime_GET_YEAR!(o as *mut PyDateTime_DateTime) -} - -#[macro_export] -macro_rules! PyDateTime_GET_MONTH { - ($o: expr) => { - (*$o).data[2] as c_int - }; -} - -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int { - PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { - PyDateTime_GET_MONTH!(o as *mut PyDateTime_DateTime) -} - -#[macro_export] -macro_rules! PyDateTime_GET_DAY { - ($o: expr) => { - (*$o).data[3] as c_int - }; -} - -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int { - PyDateTime_GET_DAY!(o as *mut PyDateTime_Date) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { - PyDateTime_GET_DAY!(o as *mut PyDateTime_DateTime) +#[inline] +pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int { + let d = *(o as *mut PyDateTime_Date); + c_int::from(d.data[3]) } // Accessor macros for times diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index 0182b0d1..aa297db1 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -227,85 +227,50 @@ macro_rules! _access_field { }; } -// Accessor functions for PyDateTime_Date -// Note: These have nonsensical names -#[macro_export] -macro_rules! PyDateTime_GET_YEAR { - // This is a macro in the C API and it's difficult to get the same behavior - // without making it a macro in Rust as well, or playing with pointers - ($o: expr) => { - (((*$o).data[0] as c_int) << 8) | ((*$o).data[1] as c_int) - }; +// Accessor functions for PyDateTime_Date and PyDateTime_DateTime +#[inline] +pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { + // This should work for Date or DateTime + let d = *(o as *mut PyDateTime_Date); + (c_int::from(d.data[0]) << 8 | c_int::from(d.data[1])) } -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_YEAR(o: *mut PyObject) -> c_int { - PyDateTime_GET_YEAR!(o as *mut PyDateTime_Date) +#[inline] +pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int { + let d = *(o as *mut PyDateTime_Date); + c_int::from(d.data[2]) } -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_YEAR(o: *mut PyObject) -> c_int { - PyDateTime_GET_YEAR!(o as *mut PyDateTime_DateTime) -} - -#[macro_export] -macro_rules! PyDateTime_GET_MONTH { - ($o: expr) => { - (*$o).data[2] as c_int - }; -} - -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_MONTH(o: *mut PyObject) -> c_int { - PyDateTime_GET_MONTH!(o as *mut PyDateTime_Date) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_MONTH(o: *mut PyObject) -> c_int { - PyDateTime_GET_MONTH!(o as *mut PyDateTime_DateTime) -} - -#[macro_export] -macro_rules! PyDateTime_GET_DAY { - ($o: expr) => { - (*$o).data[3] as c_int - }; -} - -#[inline(always)] -pub unsafe fn PyDateTime_Date_GET_DAY(o: *mut PyObject) -> c_int { - PyDateTime_GET_DAY!(o as *mut PyDateTime_Date) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DateTime_GET_DAY(o: *mut PyObject) -> c_int { - PyDateTime_GET_DAY!(o as *mut PyDateTime_DateTime) +#[inline] +pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int { + let d = *(o as *mut PyDateTime_Date); + c_int::from(d.data[3]) } // Accessor macros for times macro_rules! _PyDateTime_GET_HOUR { ($o: expr, $offset:expr) => { - (*$o).data[$offset + 0] as c_int + c_int::from((*$o).data[$offset + 0]) }; } macro_rules! _PyDateTime_GET_MINUTE { ($o: expr, $offset:expr) => { - (*$o).data[$offset + 1] as c_int + c_int::from((*$o).data[$offset + 1]) }; } macro_rules! _PyDateTime_GET_SECOND { ($o: expr, $offset:expr) => { - (*$o).data[$offset + 2] as c_int + c_int::from((*$o).data[$offset + 2]) }; } macro_rules! _PyDateTime_GET_MICROSECOND { ($o: expr, $offset:expr) => { - (((*$o).data[$offset + 3] as c_int) << 16) - | (((*$o).data[$offset + 4] as c_int) << 8) - | ((*$o).data[$offset + 5] as c_int) + (c_int::from((*$o).data[$offset + 3]) << 16) + | (c_int::from((*$o).data[$offset + 4]) << 8) + | (c_int::from((*$o).data[$offset + 5])) }; } diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 90bc0429..a0a197ea 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -8,12 +8,9 @@ use ffi::{ use ffi::{ PyDateTime_DELTA_GET_DAYS, PyDateTime_DELTA_GET_MICROSECONDS, PyDateTime_DELTA_GET_SECONDS, }; -use ffi::{ - PyDateTime_DateTime_GET_DAY, PyDateTime_DateTime_GET_MONTH, PyDateTime_DateTime_GET_YEAR, -}; use ffi::{PyDateTime_DateType, PyDate_Check}; -use ffi::{PyDateTime_Date_GET_DAY, PyDateTime_Date_GET_MONTH, PyDateTime_Date_GET_YEAR}; use ffi::{PyDateTime_DeltaType, PyDelta_Check}; +use ffi::{PyDateTime_GET_DAY, PyDateTime_GET_MONTH, PyDateTime_GET_YEAR}; use ffi::{ PyDateTime_TIME_GET_HOUR, PyDateTime_TIME_GET_MICROSECOND, PyDateTime_TIME_GET_MINUTE, PyDateTime_TIME_GET_SECOND, @@ -78,15 +75,15 @@ impl PyDate { impl PyDateComponentAccess for PyDate { fn get_year(&self) -> u32 { - unsafe { PyDateTime_Date_GET_YEAR(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 } } fn get_month(&self) -> u32 { - unsafe { PyDateTime_Date_GET_MONTH(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u32 } } fn get_day(&self) -> u32 { - unsafe { PyDateTime_Date_GET_DAY(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u32 } } } @@ -148,15 +145,15 @@ impl PyDateTime { impl PyDateComponentAccess for PyDateTime { fn get_year(&self) -> u32 { - unsafe { PyDateTime_DateTime_GET_YEAR(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 } } fn get_month(&self) -> u32 { - unsafe { PyDateTime_DateTime_GET_MONTH(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u32 } } fn get_day(&self) -> u32 { - unsafe { PyDateTime_DateTime_GET_DAY(self.as_ptr()) as u32 } + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u32 } } } From 94bd0d7b6d393259abcc03c5cc5e92affa911653 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 12:10:17 -0400 Subject: [PATCH 25/44] Relax constraints on PyDateTimeAPI initialization As noted in the comments, using call_once to initialize the PyDateTimeAPI singleton is causing deadlocks with the GIL; these deadlocks can be avoided by falling back on CPython's own locking behavior. --- Cargo.toml | 1 - src/ffi2/datetime.rs | 59 +++++++++++++++++++++++++++++++++++++++----- src/ffi3/datetime.rs | 59 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 106 insertions(+), 13 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4cc38ab..672aa210 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,7 +24,6 @@ spin = "0.4.9" num-traits = "0.2.5" pyo3cls = { path = "pyo3cls", version = "=0.4.1" } mashup = "0.1.5" -lazy_static = "1.0" [dev-dependencies] docmatic = "0.1.2" diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs index a9c0850c..28c3e336 100644 --- a/src/ffi2/datetime.rs +++ b/src/ffi2/datetime.rs @@ -2,8 +2,11 @@ use ffi2::object::*; use ffi2::pycapsule::PyCapsule_Import; use ffi2::pyport::Py_hash_t; use std::ffi::CString; +use std::ops::Deref; use std::option::Option; use std::os::raw::{c_char, c_int, c_uchar}; +use std::ptr; +use std::sync::Once; #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { @@ -114,19 +117,63 @@ pub struct PyDateTime_Delta { } // C API Capsule -unsafe impl Sync for PyDateTime_CAPI {} +// Note: This is "roll-your-own" lazy-static implementation is necessary because +// of the interaction between the call_once locks and the GIL. It turns out that +// calling PyCapsule_Import releases and re-acquires the GIL during the import, +// so if you have two threads attempting to use the PyDateTimeAPI singleton +// under the GIL, it causes a deadlock; what happens is: +// +// Thread 1 acquires GIL +// Thread 1 acquires the call_once lock +// Thread 1 calls PyCapsule_Import, thus releasing the GIL +// Thread 2 acquires the GIL +// Thread 2 blocks waiting for the call_once lock +// Thread 1 blocks waiting for the GIL +// +// However, Python's import mechanism acquires a module-specific lock around +// each import call, so all call importing datetime will return the same +// object, making the call_once lock superfluous. As such, we can weaken +// the guarantees of the cache, such that PyDateTime_IMPORT can be called +// until __PY_DATETIME_API_UNSAFE_CACHE is populated, which will happen exactly +// one time. So long as PyDateTime_IMPORT has no side effects (it should not), +// this will be at most a slight waste of resources. +static __PY_DATETIME_API_ONCE: Once = Once::new(); +static mut __PY_DATETIME_API_UNSAFE_CACHE: *const PyDateTime_CAPI = ptr::null(); -lazy_static! { - pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +pub struct PyDateTimeAPI { + __private_field: (), +} +pub static PyDateTimeAPI: PyDateTimeAPI = PyDateTimeAPI { + __private_field: (), +}; + +impl Deref for PyDateTimeAPI { + type Target = PyDateTime_CAPI; + + fn deref(&self) -> &'static PyDateTime_CAPI { + unsafe { + let cache_val = if !__PY_DATETIME_API_UNSAFE_CACHE.is_null() { + return &(*__PY_DATETIME_API_UNSAFE_CACHE); + } else { + PyDateTime_IMPORT() + }; + + __PY_DATETIME_API_ONCE.call_once(move || { + __PY_DATETIME_API_UNSAFE_CACHE = cache_val; + }); + + &(*__PY_DATETIME_API_UNSAFE_CACHE) + } + } } #[inline(always)] -pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { +pub unsafe fn PyDateTime_IMPORT() -> *const PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); - let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 0); - *(capsule as *const PyDateTime_CAPI) + let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1); + capsule as *const PyDateTime_CAPI } // diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index aa297db1..f480a9ce 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -2,8 +2,11 @@ use ffi3::object::*; use ffi3::pycapsule::PyCapsule_Import; use ffi3::pyport::Py_hash_t; use std::ffi::CString; +use std::ops::Deref; use std::option::Option; use std::os::raw::{c_char, c_int, c_uchar}; +use std::ptr; +use std::sync::Once; #[cfg_attr(windows, link(name = "pythonXY"))] extern "C" { @@ -150,19 +153,63 @@ pub struct PyDateTime_Delta { } // C API Capsule -unsafe impl Sync for PyDateTime_CAPI {} +// Note: This is "roll-your-own" lazy-static implementation is necessary because +// of the interaction between the call_once locks and the GIL. It turns out that +// calling PyCapsule_Import releases and re-acquires the GIL during the import, +// so if you have two threads attempting to use the PyDateTimeAPI singleton +// under the GIL, it causes a deadlock; what happens is: +// +// Thread 1 acquires GIL +// Thread 1 acquires the call_once lock +// Thread 1 calls PyCapsule_Import, thus releasing the GIL +// Thread 2 acquires the GIL +// Thread 2 blocks waiting for the call_once lock +// Thread 1 blocks waiting for the GIL +// +// However, Python's import mechanism acquires a module-specific lock around +// each import call, so all call importing datetime will return the same +// object, making the call_once lock superfluous. As such, we can weaken +// the guarantees of the cache, such that PyDateTime_IMPORT can be called +// until __PY_DATETIME_API_UNSAFE_CACHE is populated, which will happen exactly +// one time. So long as PyDateTime_IMPORT has no side effects (it should not), +// this will be at most a slight waste of resources. +static __PY_DATETIME_API_ONCE: Once = Once::new(); +static mut __PY_DATETIME_API_UNSAFE_CACHE: *const PyDateTime_CAPI = ptr::null(); -lazy_static! { - pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +pub struct PyDateTimeAPI { + __private_field: (), +} +pub static PyDateTimeAPI: PyDateTimeAPI = PyDateTimeAPI { + __private_field: (), +}; + +impl Deref for PyDateTimeAPI { + type Target = PyDateTime_CAPI; + + fn deref(&self) -> &'static PyDateTime_CAPI { + unsafe { + let cache_val = if !__PY_DATETIME_API_UNSAFE_CACHE.is_null() { + return &(*__PY_DATETIME_API_UNSAFE_CACHE); + } else { + PyDateTime_IMPORT() + }; + + __PY_DATETIME_API_ONCE.call_once(move || { + __PY_DATETIME_API_UNSAFE_CACHE = cache_val; + }); + + &(*__PY_DATETIME_API_UNSAFE_CACHE) + } + } } #[inline(always)] -pub unsafe fn PyDateTime_IMPORT() -> PyDateTime_CAPI { +pub unsafe fn PyDateTime_IMPORT() -> *const PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); - let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 0); - *(capsule as *const PyDateTime_CAPI) + let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1); + capsule as *const PyDateTime_CAPI } // From bcc42315ffec9bdd8d059fe14dd014ad3fc1dc36 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 13:31:10 -0400 Subject: [PATCH 26/44] Inline parameter typecasts Clippy complains about the one-letter variables, but it's preferable to do the typecasts in-line anyway. --- src/objects/datetime.rs | 77 ++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index a0a197ea..a30e4313 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -54,12 +54,13 @@ pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); impl PyDate { pub fn new(py: Python, year: u32, month: u32, day: u32) -> PyResult> { - let y = year as c_int; - let m = month as c_int; - let d = day as c_int; - unsafe { - let ptr = PyDateTimeAPI.Date_FromDate.unwrap()(y, m, d, PyDateTimeAPI.DateType); + let ptr = PyDateTimeAPI.Date_FromDate.unwrap()( + year as c_int, + month as c_int, + day as c_int, + PyDateTimeAPI.DateType, + ); Py::from_owned_ptr_or_err(py, ptr) } } @@ -103,23 +104,15 @@ impl PyDateTime { microsecond: u32, tzinfo: &PyObject, ) -> PyResult> { - let y = year as c_int; - let mo = month as c_int; - let d = day as c_int; - let h = hour as c_int; - let mi = minute as c_int; - let s = second as c_int; - let u = microsecond as c_int; - unsafe { let ptr = PyDateTimeAPI.DateTime_FromDateAndTime.unwrap()( - y, - mo, - d, - h, - mi, - s, - u, + year as c_int, + month as c_int, + day as c_int, + hour as c_int, + minute as c_int, + second as c_int, + microsecond as c_int, tzinfo.as_ptr(), PyDateTimeAPI.DateTimeType, ); @@ -193,14 +186,15 @@ impl PyTime { microsecond: u32, tzinfo: &PyObject, ) -> PyResult> { - let h = hour as c_int; - let m = minute as c_int; - let s = second as c_int; - let u = microsecond as c_int; - let tzi = tzinfo.as_ptr(); - unsafe { - let ptr = PyDateTimeAPI.Time_FromTime.unwrap()(h, m, s, u, tzi, PyDateTimeAPI.TimeType); + let ptr = PyDateTimeAPI.Time_FromTime.unwrap()( + hour as c_int, + minute as c_int, + second as c_int, + microsecond as c_int, + tzinfo.as_ptr(), + PyDateTimeAPI.TimeType, + ); Py::from_owned_ptr_or_err(py, ptr) } } @@ -215,20 +209,14 @@ impl PyTime { tzinfo: &PyObject, fold: bool, ) -> PyResult> { - let h = hour as c_int; - let m = minute as c_int; - let s = second as c_int; - let u = microsecond as c_int; - - let f = fold as c_int; unsafe { let ptr = PyDateTimeAPI.Time_FromTimeAndFold.unwrap()( - h, - m, - s, - u, + hour as c_int, + minute as c_int, + second as c_int, + microsecond as c_int, tzinfo.as_ptr(), - f, + fold as c_int, PyDateTimeAPI.TimeType, ); Py::from_owned_ptr_or_err(py, ptr) @@ -275,13 +263,14 @@ impl PyDelta { microseconds: i32, normalize: bool, ) -> PyResult> { - let d = days as c_int; - let s = seconds as c_int; - let u = microseconds as c_int; - let n = normalize as c_int; - unsafe { - let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()(d, s, u, n, PyDateTimeAPI.DeltaType); + let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()( + days as c_int, + seconds as c_int, + microseconds as c_int, + normalize as c_int, + PyDateTimeAPI.DeltaType, + ); Py::from_owned_ptr_or_err(py, ptr) } } From 09ee50d4cade396c76ecbd979614ed9563d3f4c2 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 13:31:39 -0400 Subject: [PATCH 27/44] Add tests for datetime overflow behavior It *should* be safe to cast u32 to i32 in the case of the Py{Date}{Time} constructors, because any unsigned values that would overflow would raise an error anyway. This adds tests to ensure that this is the case - they currently fail on Python <3.6 because of a known issue with the Python C API. --- tests/test_datetime.rs | 84 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 2908905f..fe517311 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -106,3 +106,87 @@ fn test_delta_check() { assert_check_only!(PyDelta_Check, sub_obj); assert_check_only!(PyDelta_Check, sub_sub_obj); } + +#[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() { + let (year, month, day) = val; + let dt = PyDate::new(py, *year, *month, *day); + let msg = format!("Should have raised an error: {:#?}", val); + match dt { + Ok(_) => assert!(false, msg), + Err(_) => assert!(true), + } + } +} + +#[cfg(Py_3_6)] +#[test] +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() { + 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); + match dt { + Ok(_) => assert!(false, msg), + Err(_) => assert!(true), + } + } +} + +#[cfg(Py_3_6)] +#[test] +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), + ]; + + for val in vals.into_iter() { + let (year, month, day, hour, minute, second, microsecond) = val; + let dt = PyDateTime::new( + py, + *year, + *month, + *day, + *hour, + *minute, + *second, + *microsecond, + None, + ); + let msg = format!("Should have raised an error: {:#?}", val); + match dt { + Ok(_) => assert!(false, msg), + Err(_) => assert!(true), + } + } +} From bcc27bd522da223beef2579ca2fdfabe58bc56ac Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 13:52:19 -0400 Subject: [PATCH 28/44] Change datetime constructors to use Rust Option This makes it cleaner to call PyTime::new and PyDateTime::new from Rust without needing to retrieve a Python None. --- src/objects/datetime.rs | 21 +++++++++++++------ tests/rustapi_module/src/lib.rs | 36 ++++++++++++++++----------------- tests/test_datetime.rs | 23 +++++++++++++++++++++ 3 files changed, 56 insertions(+), 24 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index a30e4313..c48490e5 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -102,7 +102,7 @@ impl PyDateTime { minute: u32, second: u32, microsecond: u32, - tzinfo: &PyObject, + tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { let ptr = PyDateTimeAPI.DateTime_FromDateAndTime.unwrap()( @@ -113,7 +113,10 @@ impl PyDateTime { minute as c_int, second as c_int, microsecond as c_int, - tzinfo.as_ptr(), + match tzinfo { + Some(o) => o.as_ptr(), + None => py.None().as_ptr(), + }, PyDateTimeAPI.DateTimeType, ); Py::from_owned_ptr_or_err(py, ptr) @@ -184,7 +187,7 @@ impl PyTime { minute: u32, second: u32, microsecond: u32, - tzinfo: &PyObject, + tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { let ptr = PyDateTimeAPI.Time_FromTime.unwrap()( @@ -192,7 +195,10 @@ impl PyTime { minute as c_int, second as c_int, microsecond as c_int, - tzinfo.as_ptr(), + match tzinfo { + Some(o) => o.as_ptr(), + None => py.None().as_ptr(), + }, PyDateTimeAPI.TimeType, ); Py::from_owned_ptr_or_err(py, ptr) @@ -206,7 +212,7 @@ impl PyTime { minute: u32, second: u32, microsecond: u32, - tzinfo: &PyObject, + tzinfo: Option<&PyObject>, fold: bool, ) -> PyResult> { unsafe { @@ -215,7 +221,10 @@ impl PyTime { minute as c_int, second as c_int, microsecond as c_int, - tzinfo.as_ptr(), + match tzinfo { + Some(o) => o.as_ptr(), + None => py.None().as_ptr(), + }, fold as c_int, PyDateTimeAPI.TimeType, ); diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index 44becbd7..a69e2dde 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -13,15 +13,6 @@ use pyo3::prelude::{PyDict, PyTuple}; use pyo3::{ObjectProtocol, ToPyObject}; use pyo3::{Py, PyResult, Python}; -macro_rules! to_pyobject { - ($py:expr, $o:ident) => { - match $o { - Some(t) => t.to_object($py), - None => $py.None(), - } - }; -} - #[pyfunction] fn make_date(py: Python, year: u32, month: u32, day: u32) -> PyResult> { PyDate::new(py, year, month, day) @@ -48,8 +39,14 @@ fn make_time( microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult> { - let tzi: PyObject = to_pyobject!(py, tzinfo); - PyTime::new(py, hour, minute, second, microsecond, &tzi) + PyTime::new( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(|o| o.to_object(py)).as_ref(), + ) } #[cfg(Py_3_6)] @@ -63,8 +60,15 @@ fn time_with_fold( tzinfo: Option<&PyTzInfo>, fold: bool, ) -> PyResult> { - let tzi = to_pyobject!(py, tzinfo); - PyTime::new_with_fold(py, hour, minute, second, microsecond, &tzi, fold) + PyTime::new_with_fold( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(|o| o.to_object(py)).as_ref(), + fold, + ) } #[pyfunction] @@ -124,10 +128,6 @@ fn make_datetime( microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult> { - let tzi: PyObject = match tzinfo { - Some(t) => t.to_object(py), - None => py.None(), - }; PyDateTime::new( py, year, @@ -137,7 +137,7 @@ fn make_datetime( minute, second, microsecond, - &tzi, + tzinfo.map(|o| (o.to_object(py))).as_ref(), ) } diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index fe517311..b0eed461 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -107,6 +107,29 @@ fn test_delta_check() { assert_check_only!(PyDelta_Check, sub_sub_obj); } +#[test] +#[cfg(Py_3)] +fn test_datetime_utc() { + let gil = Python::acquire_gil(); + let py = gil.python(); + + let datetime = py.import("datetime").map_err(|e| e.print(py)).unwrap(); + let timezone = datetime.get("timezone").unwrap(); + let utc = timezone.getattr("utc").unwrap().to_object(py); + + let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, Some(&utc)).unwrap(); + + let locals = PyDict::new(py); + locals.set_item("dt", dt).unwrap(); + + let offset: f32 = py + .eval("dt.utcoffset().total_seconds()", None, Some(locals)) + .unwrap() + .extract() + .unwrap(); + assert_eq!(offset, 0f32); +} + #[cfg(Py_3_6)] #[test] fn test_pydate_out_of_bounds() { From f68d0932f42c9ba4656241be10763ac6afe2444c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 14:16:41 -0400 Subject: [PATCH 29/44] Unwrap PyDateTime_CAPI function fields Because the PyDateTime_CAPI is always accessed via a lazy-initialized static reference, it is not necessary to handle the case where the functions on the C struct have not been initialized. --- src/ffi2/datetime.rs | 67 ++++++++++------------- src/ffi3/datetime.rs | 115 ++++++++++++++++++---------------------- src/objects/datetime.rs | 15 +++--- 3 files changed, 87 insertions(+), 110 deletions(-) diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs index 28c3e336..69d32307 100644 --- a/src/ffi2/datetime.rs +++ b/src/ffi2/datetime.rs @@ -3,7 +3,6 @@ use ffi2::pycapsule::PyCapsule_Import; use ffi2::pyport::Py_hash_t; use std::ffi::CString; use std::ops::Deref; -use std::option::Option; use std::os::raw::{c_char, c_int, c_uchar}; use std::ptr; use std::sync::Once; @@ -27,48 +26,40 @@ pub struct PyDateTime_CAPI { pub DeltaType: *mut PyTypeObject, pub TZInfoType: *mut PyTypeObject, - pub Date_FromDate: Option< + pub Date_FromDate: unsafe extern "C" fn(year: c_int, month: c_int, day: c_int, cls: *mut PyTypeObject) -> *mut PyObject, - >, - pub DateTime_FromDateAndTime: Option< - unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, - pub Time_FromTime: Option< - unsafe extern "C" fn( - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, - pub Delta_FromDelta: Option< - unsafe extern "C" fn( - days: c_int, - seconds: c_int, - microseconds: c_int, - normalize: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, - pub DateTime_FromTimestamp: Option< + pub DateTime_FromDateAndTime: unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + pub Time_FromTime: unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + pub Delta_FromDelta: unsafe extern "C" fn( + days: c_int, + seconds: c_int, + microseconds: c_int, + normalize: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + pub DateTime_FromTimestamp: unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject, - >, pub Date_FromTimestamp: - Option *mut PyObject>, + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, } // Type struct wrappers diff --git a/src/ffi3/datetime.rs b/src/ffi3/datetime.rs index f480a9ce..8aa814d3 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi3/datetime.rs @@ -3,7 +3,6 @@ use ffi3::pycapsule::PyCapsule_Import; use ffi3::pyport::Py_hash_t; use std::ffi::CString; use std::ops::Deref; -use std::option::Option; use std::os::raw::{c_char, c_int, c_uchar}; use std::ptr; use std::sync::Once; @@ -29,78 +28,66 @@ pub struct PyDateTime_CAPI { #[cfg(Py_3_7)] pub TimeZone_UTC: *mut PyObject, - pub Date_FromDate: Option< + pub Date_FromDate: unsafe extern "C" fn(year: c_int, month: c_int, day: c_int, cls: *mut PyTypeObject) -> *mut PyObject, - >, - pub DateTime_FromDateAndTime: Option< - unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, - pub Time_FromTime: Option< - unsafe extern "C" fn( - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, - pub Delta_FromDelta: Option< - unsafe extern "C" fn( - days: c_int, - seconds: c_int, - microseconds: c_int, - normalize: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, + pub DateTime_FromDateAndTime: unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + pub Time_FromTime: unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + cls: *mut PyTypeObject, + ) -> *mut PyObject, + pub Delta_FromDelta: unsafe extern "C" fn( + days: c_int, + seconds: c_int, + microseconds: c_int, + normalize: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, #[cfg(Py_3_7)] pub TimeZone_FromTimeZone: - Option *mut PyObject>, - pub DateTime_FromTimestamp: Option< + unsafe extern "C" fn(offset: *mut PyObject, name: *mut PyObject) -> *mut PyObject, + pub DateTime_FromTimestamp: unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) -> *mut PyObject, - >, pub Date_FromTimestamp: - Option *mut PyObject>, + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, #[cfg(Py_3_6)] - pub DateTime_FromDateAndTimeAndFold: Option< - unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - fold: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, + pub DateTime_FromDateAndTimeAndFold: unsafe extern "C" fn( + year: c_int, + month: c_int, + day: c_int, + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + fold: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, #[cfg(Py_3_6)] - pub Time_FromTimeAndFold: Option< - unsafe extern "C" fn( - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - fold: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - >, + pub Time_FromTimeAndFold: unsafe extern "C" fn( + hour: c_int, + minute: c_int, + second: c_int, + microsecond: c_int, + tzinfo: *mut PyObject, + fold: c_int, + cls: *mut PyTypeObject, + ) -> *mut PyObject, } // Type struct wrappers diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index c48490e5..d742ff83 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -55,7 +55,7 @@ pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); impl PyDate { pub fn new(py: Python, year: u32, month: u32, day: u32) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.Date_FromDate.unwrap()( + let ptr = (PyDateTimeAPI.Date_FromDate)( year as c_int, month as c_int, day as c_int, @@ -67,8 +67,7 @@ impl PyDate { pub fn from_timestamp(py: Python, args: &PyObject) -> PyResult> { unsafe { - let ptr = - PyDateTimeAPI.Date_FromTimestamp.unwrap()(PyDateTimeAPI.DateType, args.as_ptr()); + let ptr = (PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, args.as_ptr()); Py::from_owned_ptr_or_err(py, ptr) } } @@ -105,7 +104,7 @@ impl PyDateTime { tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.DateTime_FromDateAndTime.unwrap()( + let ptr = (PyDateTimeAPI.DateTime_FromDateAndTime)( year as c_int, month as c_int, day as c_int, @@ -129,7 +128,7 @@ impl PyDateTime { kwargs: &PyObject, ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.DateTime_FromTimestamp.unwrap()( + let ptr = (PyDateTimeAPI.DateTime_FromTimestamp)( PyDateTimeAPI.DateTimeType, args.as_ptr(), kwargs.as_ptr(), @@ -190,7 +189,7 @@ impl PyTime { tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.Time_FromTime.unwrap()( + let ptr = (PyDateTimeAPI.Time_FromTime)( hour as c_int, minute as c_int, second as c_int, @@ -216,7 +215,7 @@ impl PyTime { fold: bool, ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.Time_FromTimeAndFold.unwrap()( + let ptr = (PyDateTimeAPI.Time_FromTimeAndFold)( hour as c_int, minute as c_int, second as c_int, @@ -273,7 +272,7 @@ impl PyDelta { normalize: bool, ) -> PyResult> { unsafe { - let ptr = PyDateTimeAPI.Delta_FromDelta.unwrap()( + let ptr = (PyDateTimeAPI.Delta_FromDelta)( days as c_int, seconds as c_int, microseconds as c_int, From 003351ba619290cfacd93238429a290eee5526bd Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 15:11:54 -0400 Subject: [PATCH 30/44] Get rid of unwrap_py! macro --- tests/test_datetime.rs | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index b0eed461..5f3f0071 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -10,38 +10,30 @@ fn _get_subclasses<'p>( py: &'p Python, py_type: &str, args: &str, -) -> (&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef) { - macro_rules! unwrap_py { - ($e:expr) => { - ($e).map_err(|e| e.print(*py)).unwrap() - }; - }; - +) -> PyResult<(&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef)> { // Import the class from Python and create some subclasses - let datetime = unwrap_py!(py.import("datetime")); + let datetime = py.import("datetime")?; let locals = PyDict::new(*py); - locals - .set_item(py_type, datetime.get(py_type).unwrap()) - .unwrap(); + locals.set_item(py_type, datetime.get(py_type)?).unwrap(); let make_subclass_py = format!("class Subklass({}):\n pass", py_type); let make_sub_subclass_py = "class SubSubklass(Subklass):\n pass"; - unwrap_py!(py.run(&make_subclass_py, None, Some(&locals))); - unwrap_py!(py.run(&make_sub_subclass_py, None, Some(&locals))); + py.run(&make_subclass_py, None, Some(&locals))?; + py.run(&make_sub_subclass_py, None, Some(&locals))?; // Construct an instance of the base class - let obj = unwrap_py!(py.eval(&format!("{}({})", py_type, args), None, Some(&locals))); + let obj = py.eval(&format!("{}({})", py_type, args), None, Some(&locals))?; // Construct an instance of the subclass - let sub_obj = unwrap_py!(py.eval(&format!("Subklass({})", args), None, Some(&locals))); + let sub_obj = py.eval(&format!("Subklass({})", args), None, Some(&locals))?; // Construct an instance of the sub-subclass - let sub_sub_obj = unwrap_py!(py.eval(&format!("SubSubklass({})", args), None, Some(&locals))); + let sub_sub_obj = py.eval(&format!("SubSubklass({})", args), None, Some(&locals))?; - (obj, sub_obj, sub_sub_obj) + Ok((obj, sub_obj, sub_sub_obj)) } macro_rules! assert_check_exact { @@ -66,7 +58,7 @@ macro_rules! assert_check_only { fn test_date_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1"); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1").unwrap(); assert_check_exact!(PyDate_Check, obj); assert_check_only!(PyDate_Check, sub_obj); @@ -77,7 +69,7 @@ fn test_date_check() { fn test_time_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15"); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15").unwrap(); assert_check_exact!(PyTime_Check, obj); assert_check_only!(PyTime_Check, sub_obj); @@ -88,7 +80,8 @@ fn test_time_check() { fn test_datetime_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15"); + let (obj, sub_obj, sub_sub_obj) = + _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15").unwrap(); assert_check_only!(PyDate_Check, obj); assert_check_exact!(PyDateTime_Check, obj); @@ -100,7 +93,7 @@ fn test_datetime_check() { fn test_delta_check() { let gil = Python::acquire_gil(); let py = gil.python(); - let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3"); + let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3").unwrap(); assert_check_exact!(PyDelta_Check, obj); assert_check_only!(PyDelta_Check, sub_obj); From 3357fabb3577916062ae1cd4a4c692a396660bd8 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Mon, 20 Aug 2018 15:15:11 -0400 Subject: [PATCH 31/44] Remove 'Component' from ComponentAccess Traits --- src/objects/datetime.rs | 16 ++++++++-------- src/objects/mod.rs | 4 ++-- tests/rustapi_module/src/lib.rs | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index d742ff83..17e48f28 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -27,19 +27,19 @@ use instance::Py; use python::{Python, ToPyPointer}; // Traits -pub trait PyDateComponentAccess { +pub trait PyDateAccess { fn get_year(&self) -> u32; fn get_month(&self) -> u32; fn get_day(&self) -> u32; } -pub trait PyDeltaComponentAccess { +pub trait PyDeltaAccess { fn get_days(&self) -> i32; fn get_seconds(&self) -> i32; fn get_microseconds(&self) -> i32; } -pub trait PyTimeComponentAccess { +pub trait PyTimeAccess { fn get_hour(&self) -> u32; fn get_minute(&self) -> u32; fn get_second(&self) -> u32; @@ -73,7 +73,7 @@ impl PyDate { } } -impl PyDateComponentAccess for PyDate { +impl PyDateAccess for PyDate { fn get_year(&self) -> u32 { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 } } @@ -138,7 +138,7 @@ impl PyDateTime { } } -impl PyDateComponentAccess for PyDateTime { +impl PyDateAccess for PyDateTime { fn get_year(&self) -> u32 { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as u32 } } @@ -152,7 +152,7 @@ impl PyDateComponentAccess for PyDateTime { } } -impl PyTimeComponentAccess for PyDateTime { +impl PyTimeAccess for PyDateTime { fn get_hour(&self) -> u32 { unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u32 } } @@ -232,7 +232,7 @@ impl PyTime { } } -impl PyTimeComponentAccess for PyTime { +impl PyTimeAccess for PyTime { fn get_hour(&self) -> u32 { unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u32 } } @@ -284,7 +284,7 @@ impl PyDelta { } } -impl PyDeltaComponentAccess for PyDelta { +impl PyDeltaAccess for PyDelta { fn get_days(&self) -> i32 { unsafe { PyDateTime_DELTA_GET_DAYS(self.as_ptr()) as i32 } } diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 29105349..1181c7a6 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,9 +5,9 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; -pub use self::datetime::PyDeltaComponentAccess; +pub use self::datetime::PyDeltaAccess; pub use self::datetime::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo}; -pub use self::datetime::{PyDateComponentAccess, PyTimeComponentAccess}; +pub use self::datetime::{PyDateAccess, PyTimeAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; diff --git a/tests/rustapi_module/src/lib.rs b/tests/rustapi_module/src/lib.rs index a69e2dde..2182ddc5 100644 --- a/tests/rustapi_module/src/lib.rs +++ b/tests/rustapi_module/src/lib.rs @@ -3,12 +3,12 @@ #[macro_use] extern crate pyo3; -use pyo3::prelude::PyDeltaComponentAccess; +use pyo3::prelude::PyDeltaAccess; use pyo3::prelude::PyModule; use pyo3::prelude::PyObject; use pyo3::prelude::{pyfunction, pymodinit}; use pyo3::prelude::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo}; -use pyo3::prelude::{PyDateComponentAccess, PyTimeComponentAccess}; +use pyo3::prelude::{PyDateAccess, PyTimeAccess}; use pyo3::prelude::{PyDict, PyTuple}; use pyo3::{ObjectProtocol, ToPyObject}; use pyo3::{Py, PyResult, Python}; From a271ba9ed90ae1e41089ccb6a533a8cf17bc6fd1 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 12:06:20 -0400 Subject: [PATCH 32/44] Use unified implementation for ffi::datetime In the future we can make ffi::object, ffi::pycapsule, etc as crate-public, but importing the specific symbols is a light touch way to do this. --- src/{ffi3 => ffi}/datetime.rs | 7 +- src/ffi/mod.rs | 12 ++ src/ffi2/datetime.rs | 357 ---------------------------------- src/ffi2/mod.rs | 2 - src/ffi3/mod.rs | 2 - src/lib.rs | 13 +- 6 files changed, 18 insertions(+), 375 deletions(-) rename src/{ffi3 => ffi}/datetime.rs (98%) create mode 100644 src/ffi/mod.rs delete mode 100644 src/ffi2/datetime.rs diff --git a/src/ffi3/datetime.rs b/src/ffi/datetime.rs similarity index 98% rename from src/ffi3/datetime.rs rename to src/ffi/datetime.rs index 8aa814d3..0ab4927e 100644 --- a/src/ffi3/datetime.rs +++ b/src/ffi/datetime.rs @@ -1,6 +1,7 @@ -use ffi3::object::*; -use ffi3::pycapsule::PyCapsule_Import; -use ffi3::pyport::Py_hash_t; +use ffi::{PyObject, PyTypeObject}; +use ffi::{Py_TYPE, PyObject_TypeCheck}; +use ffi::PyCapsule_Import; +use ffi::Py_hash_t; use std::ffi::CString; use std::ops::Deref; use std::os::raw::{c_char, c_int, c_uchar}; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 00000000..72bd6c3c --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,12 @@ +#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] +#![cfg_attr(feature="cargo-clippy", allow(inline_always))] + +#[cfg(not(Py_3))] +pub use ffi2::*; + +#[cfg(Py_3)] +pub use ffi3::*; + +pub use self::datetime::*; + +pub(crate) mod datetime; diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs deleted file mode 100644 index 69d32307..00000000 --- a/src/ffi2/datetime.rs +++ /dev/null @@ -1,357 +0,0 @@ -use ffi2::object::*; -use ffi2::pycapsule::PyCapsule_Import; -use ffi2::pyport::Py_hash_t; -use std::ffi::CString; -use std::ops::Deref; -use std::os::raw::{c_char, c_int, c_uchar}; -use std::ptr; -use std::sync::Once; - -#[cfg_attr(windows, link(name = "pythonXY"))] -extern "C" { - pub static mut PyDateTime_DateType: PyTypeObject; - pub static mut PyDateTime_TimeType: PyTypeObject; - pub static mut PyDateTime_DateTimeType: PyTypeObject; - - pub static mut PyDateTime_DeltaType: PyTypeObject; - pub static mut PyDateTime_TZInfoType: PyTypeObject; -} - -#[repr(C)] -#[derive(Debug, Copy, Clone)] -pub struct PyDateTime_CAPI { - pub DateType: *mut PyTypeObject, - pub DateTimeType: *mut PyTypeObject, - pub TimeType: *mut PyTypeObject, - pub DeltaType: *mut PyTypeObject, - pub TZInfoType: *mut PyTypeObject, - - pub Date_FromDate: - unsafe extern "C" fn(year: c_int, month: c_int, day: c_int, cls: *mut PyTypeObject) - -> *mut PyObject, - pub DateTime_FromDateAndTime: unsafe extern "C" fn( - year: c_int, - month: c_int, - day: c_int, - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - pub Time_FromTime: unsafe extern "C" fn( - hour: c_int, - minute: c_int, - second: c_int, - microsecond: c_int, - tzinfo: *mut PyObject, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - pub Delta_FromDelta: unsafe extern "C" fn( - days: c_int, - seconds: c_int, - microseconds: c_int, - normalize: c_int, - cls: *mut PyTypeObject, - ) -> *mut PyObject, - pub DateTime_FromTimestamp: - unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject, kwargs: *mut PyObject) - -> *mut PyObject, - pub Date_FromTimestamp: - unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, -} - -// Type struct wrappers - -const _PyDateTime_DATE_DATASIZE: usize = 4; -const _PyDateTime_TIME_DATASIZE: usize = 6; -const _PyDateTime_DATETIME_DATASIZE: usize = 10; - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct PyDateTime_Date { - pub ob_base: PyObject, - pub hashcode: Py_hash_t, - pub hastzinfo: c_char, - pub data: [c_uchar; _PyDateTime_DATE_DATASIZE], -} - -#[repr(C)] -#[derive(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], - pub tzinfo: *mut PyObject, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct PyDateTime_DateTime { - pub ob_base: PyObject, - pub hashcode: Py_hash_t, - pub hastzinfo: c_char, - pub data: [c_uchar; _PyDateTime_DATETIME_DATASIZE], - pub tzinfo: *mut PyObject, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub struct PyDateTime_Delta { - pub ob_base: PyObject, - pub hashcode: Py_hash_t, - pub days: c_int, - pub seconds: c_int, - pub microseconds: c_int, -} - -// C API Capsule -// Note: This is "roll-your-own" lazy-static implementation is necessary because -// of the interaction between the call_once locks and the GIL. It turns out that -// calling PyCapsule_Import releases and re-acquires the GIL during the import, -// so if you have two threads attempting to use the PyDateTimeAPI singleton -// under the GIL, it causes a deadlock; what happens is: -// -// Thread 1 acquires GIL -// Thread 1 acquires the call_once lock -// Thread 1 calls PyCapsule_Import, thus releasing the GIL -// Thread 2 acquires the GIL -// Thread 2 blocks waiting for the call_once lock -// Thread 1 blocks waiting for the GIL -// -// However, Python's import mechanism acquires a module-specific lock around -// each import call, so all call importing datetime will return the same -// object, making the call_once lock superfluous. As such, we can weaken -// the guarantees of the cache, such that PyDateTime_IMPORT can be called -// until __PY_DATETIME_API_UNSAFE_CACHE is populated, which will happen exactly -// one time. So long as PyDateTime_IMPORT has no side effects (it should not), -// this will be at most a slight waste of resources. -static __PY_DATETIME_API_ONCE: Once = Once::new(); -static mut __PY_DATETIME_API_UNSAFE_CACHE: *const PyDateTime_CAPI = ptr::null(); - -pub struct PyDateTimeAPI { - __private_field: (), -} -pub static PyDateTimeAPI: PyDateTimeAPI = PyDateTimeAPI { - __private_field: (), -}; - -impl Deref for PyDateTimeAPI { - type Target = PyDateTime_CAPI; - - fn deref(&self) -> &'static PyDateTime_CAPI { - unsafe { - let cache_val = if !__PY_DATETIME_API_UNSAFE_CACHE.is_null() { - return &(*__PY_DATETIME_API_UNSAFE_CACHE); - } else { - PyDateTime_IMPORT() - }; - - __PY_DATETIME_API_ONCE.call_once(move || { - __PY_DATETIME_API_UNSAFE_CACHE = cache_val; - }); - - &(*__PY_DATETIME_API_UNSAFE_CACHE) - } - } -} - -#[inline(always)] -pub unsafe fn PyDateTime_IMPORT() -> *const PyDateTime_CAPI { - // PyDateTime_CAPSULE_NAME is a macro in C - let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); - - let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1); - capsule as *const PyDateTime_CAPI -} - -// -// Type Check macros -// -#[inline(always)] -pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int -} - -#[inline(always)] -pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int -} - -#[inline(always)] -pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int -} - -#[inline(always)] -pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int -} - -#[inline(always)] -pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int -} - -#[inline(always)] -pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int -} - -#[inline(always)] -pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int -} - -#[inline(always)] -pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int -} - -#[inline(always)] -pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { - PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int -} - -#[inline(always)] -pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { - (Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int -} - -// -// Accessor functions -// -macro_rules! _access_field { - ($obj:expr, $type: ident, $field:tt) => { - (*($obj as *mut $type)).$field - }; -} - -// Accessor functions for PyDateTime_Date and PyDateTime_DateTime -#[inline] -pub unsafe fn PyDateTime_GET_YEAR(o: *mut PyObject) -> c_int { - // This should work for Date or DateTime - let d = *(o as *mut PyDateTime_Date); - (c_int::from(d.data[0]) << 8 | c_int::from(d.data[1])) -} - -#[inline] -pub unsafe fn PyDateTime_GET_MONTH(o: *mut PyObject) -> c_int { - let d = *(o as *mut PyDateTime_Date); - c_int::from(d.data[2]) -} - -#[inline] -pub unsafe fn PyDateTime_GET_DAY(o: *mut PyObject) -> c_int { - let d = *(o as *mut PyDateTime_Date); - c_int::from(d.data[3]) -} - -// Accessor macros for times -macro_rules! _PyDateTime_GET_HOUR { - ($o: expr, $offset:expr) => { - (*$o).data[$offset + 0] as c_int - }; -} - -macro_rules! _PyDateTime_GET_MINUTE { - ($o: expr, $offset:expr) => { - (*$o).data[$offset + 1] as c_int - }; -} - -macro_rules! _PyDateTime_GET_SECOND { - ($o: expr, $offset:expr) => { - (*$o).data[$offset + 2] as c_int - }; -} - -macro_rules! _PyDateTime_GET_MICROSECOND { - ($o: expr, $offset:expr) => { - (((*$o).data[$offset + 3] as c_int) << 16) - | (((*$o).data[$offset + 4] as c_int) << 8) - | ((*$o).data[$offset + 5] as c_int) - }; -} - -macro_rules! _PyDateTime_GET_TZINFO { - ($o: expr) => { - (*$o).tzinfo - }; -} - -// Accessor functions for DateTime -#[inline(always)] -pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int { - _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int { - _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int { - _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int { - _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) -} - -#[inline(always)] -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) -} - -#[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 { - ($obj:expr, $field:tt) => { - _access_field!($obj, PyDateTime_Delta, $field) - }; -} - -#[inline(always)] -pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int { - _access_delta_field!(o, days) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int { - _access_delta_field!(o, seconds) -} - -#[inline(always)] -pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int { - _access_delta_field!(o, microseconds) -} diff --git a/src/ffi2/mod.rs b/src/ffi2/mod.rs index 3a30b67d..29a160a7 100644 --- a/src/ffi2/mod.rs +++ b/src/ffi2/mod.rs @@ -16,7 +16,6 @@ pub use self::cobject::*; pub use self::code::*; pub use self::compile::*; pub use self::complexobject::*; -pub use self::datetime::*; pub use self::descrobject::*; pub use self::dictobject::*; pub use self::enumobject::*; @@ -67,7 +66,6 @@ mod cellobject; mod classobject; mod cobject; mod complexobject; -mod datetime; mod descrobject; mod dictobject; mod enumobject; diff --git a/src/ffi3/mod.rs b/src/ffi3/mod.rs index 9b416111..8c6b68dd 100644 --- a/src/ffi3/mod.rs +++ b/src/ffi3/mod.rs @@ -17,7 +17,6 @@ pub use self::boolobject::*; pub use self::bytearrayobject::*; pub use self::bytesobject::*; pub use self::complexobject::*; -pub use self::datetime::*; pub use self::descrobject::*; pub use self::dictobject::*; pub use self::enumobject::*; @@ -118,7 +117,6 @@ mod weakrefobject; // TODO supports PEP-384 only; needs adjustment for Python 3. mod codecs; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 mod pyerrors; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 -mod datetime; mod pystate; // TODO supports PEP-384 only; needs adjustment for Python 3.3 and 3.5 #[cfg(Py_LIMITED_API)] diff --git a/src/lib.rs b/src/lib.rs index bf9b8038..8835e2e7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,8 +129,8 @@ extern crate spin; #[doc(hidden)] pub extern crate mashup; -#[macro_use] -extern crate lazy_static; +/// Rust FFI declarations for Python +pub mod ffi; #[cfg(not(Py_3))] mod ffi2; @@ -138,15 +138,6 @@ mod ffi2; #[cfg(Py_3)] mod ffi3; -/// Rust FFI declarations for Python -pub mod ffi { - #[cfg(not(Py_3))] - pub use ffi2::*; - - #[cfg(Py_3)] - pub use ffi3::*; -} - pub use conversion::{ FromPyObject, IntoPyObject, IntoPyTuple, PyTryFrom, PyTryInto, ReturnTypeIntoPyResult, ToBorrowedObject, ToPyObject, From cf3b1d2cc352561a7c4984d60e80964b22bbb84a Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 12:16:27 -0400 Subject: [PATCH 33/44] Remove leading __ from private cache objects These were basically cargo culted from lazy_static's implementation, but they are not idiomatic or necessary to indicate that these are private variables. --- src/ffi/datetime.rs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index 0ab4927e..476f2a6e 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -1,7 +1,7 @@ -use ffi::{PyObject, PyTypeObject}; -use ffi::{Py_TYPE, PyObject_TypeCheck}; use ffi::PyCapsule_Import; use ffi::Py_hash_t; +use ffi::{PyObject, PyTypeObject}; +use ffi::{PyObject_TypeCheck, Py_TYPE}; use std::ffi::CString; use std::ops::Deref; use std::os::raw::{c_char, c_int, c_uchar}; @@ -161,8 +161,8 @@ pub struct PyDateTime_Delta { // until __PY_DATETIME_API_UNSAFE_CACHE is populated, which will happen exactly // one time. So long as PyDateTime_IMPORT has no side effects (it should not), // this will be at most a slight waste of resources. -static __PY_DATETIME_API_ONCE: Once = Once::new(); -static mut __PY_DATETIME_API_UNSAFE_CACHE: *const PyDateTime_CAPI = ptr::null(); +static PY_DATETIME_API_ONCE: Once = Once::new(); +static mut PY_DATETIME_API_UNSAFE_CACHE: *const PyDateTime_CAPI = ptr::null(); pub struct PyDateTimeAPI { __private_field: (), @@ -176,17 +176,17 @@ impl Deref for PyDateTimeAPI { fn deref(&self) -> &'static PyDateTime_CAPI { unsafe { - let cache_val = if !__PY_DATETIME_API_UNSAFE_CACHE.is_null() { - return &(*__PY_DATETIME_API_UNSAFE_CACHE); + let cache_val = if !PY_DATETIME_API_UNSAFE_CACHE.is_null() { + return &(*PY_DATETIME_API_UNSAFE_CACHE); } else { PyDateTime_IMPORT() }; - __PY_DATETIME_API_ONCE.call_once(move || { - __PY_DATETIME_API_UNSAFE_CACHE = cache_val; + PY_DATETIME_API_ONCE.call_once(move || { + PY_DATETIME_API_UNSAFE_CACHE = cache_val; }); - &(*__PY_DATETIME_API_UNSAFE_CACHE) + &(*PY_DATETIME_API_UNSAFE_CACHE) } } } From 26c5397618365ed89ddc8aff1e82a803148708a5 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 12:45:39 -0400 Subject: [PATCH 34/44] Implement Debug for PyObject in Python 2 For whatever reason I cannot build rustapi_module without this, and rather than figure out the core problem, I figured that, for symmetry, it makes sense to just implement Debug for PyObject. --- src/ffi2/object.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi2/object.rs b/src/ffi2/object.rs index f12d4cb9..be4fd28e 100644 --- a/src/ffi2/object.rs +++ b/src/ffi2/object.rs @@ -6,7 +6,7 @@ use std::os::raw::{c_char, c_double, c_int, c_long, c_uint, c_void}; use std::ptr; #[repr(C)] -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub struct PyObject { #[cfg(py_sys_config = "Py_TRACE_REFS")] pub _ob_next: *mut PyObject, From b66ab7fabc2c01c43de97c905724a15e5042b97c Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 13:10:16 -0400 Subject: [PATCH 35/44] Move cache population logic into PyDateTime_IMPORT This more closely mimics the CPython API, since the import logic populates the global, it should also populate the cache. This also allows users to eagerly initialize the Python C API if preferred (for example, doing so before populating a bunch of threads, or before making performance measurements that will be thrown off by a lazy import). --- src/ffi/datetime.rs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index 476f2a6e..ca525cb3 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -176,28 +176,27 @@ impl Deref for PyDateTimeAPI { fn deref(&self) -> &'static PyDateTime_CAPI { unsafe { - let cache_val = if !PY_DATETIME_API_UNSAFE_CACHE.is_null() { - return &(*PY_DATETIME_API_UNSAFE_CACHE); + if !PY_DATETIME_API_UNSAFE_CACHE.is_null() { + &(*PY_DATETIME_API_UNSAFE_CACHE) } else { PyDateTime_IMPORT() - }; - - PY_DATETIME_API_ONCE.call_once(move || { - PY_DATETIME_API_UNSAFE_CACHE = cache_val; - }); - - &(*PY_DATETIME_API_UNSAFE_CACHE) + } } } } #[inline(always)] -pub unsafe fn PyDateTime_IMPORT() -> *const PyDateTime_CAPI { +pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); - let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1); - capsule as *const PyDateTime_CAPI + let capsule = PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *const PyDateTime_CAPI; + + PY_DATETIME_API_ONCE.call_once(move || { + PY_DATETIME_API_UNSAFE_CACHE = capsule; + }); + + &(*PY_DATETIME_API_UNSAFE_CACHE) } // From 078bea4345f1497d1e6f2bec23e10ef0f2adb804 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 12:13:53 -0400 Subject: [PATCH 36/44] Move rustapi_module into examples This is really a test module, but the Rust convention is to put something like this under examples/, and when it lands, we can take advantage of "Project-based Examples for Cargo Projects" - RFC link at https://github.com/rust-lang/rfcs/pull/2517 --- Cargo.toml | 1 - ci/travis/test.sh | 12 ++++++------ {tests => examples}/rustapi_module/.gitignore | 0 {tests => examples}/rustapi_module/Cargo.toml | 0 {tests => examples}/rustapi_module/pyproject.toml | 0 .../rustapi_module/requirements-dev.txt | 0 .../rustapi_module/rustapi_module/__init__.py | 0 {tests => examples}/rustapi_module/setup.py | 0 {tests => examples}/rustapi_module/src/lib.rs | 0 .../rustapi_module/tests/test_datetime.py | 0 {tests => examples}/rustapi_module/tox.ini | 0 11 files changed, 6 insertions(+), 7 deletions(-) rename {tests => examples}/rustapi_module/.gitignore (100%) rename {tests => examples}/rustapi_module/Cargo.toml (100%) rename {tests => examples}/rustapi_module/pyproject.toml (100%) rename {tests => examples}/rustapi_module/requirements-dev.txt (100%) rename {tests => examples}/rustapi_module/rustapi_module/__init__.py (100%) rename {tests => examples}/rustapi_module/setup.py (100%) rename {tests => examples}/rustapi_module/src/lib.rs (100%) rename {tests => examples}/rustapi_module/tests/test_datetime.py (100%) rename {tests => examples}/rustapi_module/tox.ini (100%) diff --git a/Cargo.toml b/Cargo.toml index 672aa210..c6cd5141 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,5 +56,4 @@ members = [ "pyo3cls", "pyo3-derive-backend", "examples/*", - "tests/rustapi_module", ] diff --git a/ci/travis/test.sh b/ci/travis/test.sh index 4f69b17a..cd4b9c5f 100755 --- a/ci/travis/test.sh +++ b/ci/travis/test.sh @@ -7,11 +7,11 @@ cargo test --features $FEATURES for example in examples/*; do cd $example - python setup.py install - pytest -v tests + if [ -f tox.ini ]; then + tox -e py + else + pip install -e . + pytest -v tests + fi cd $TRAVIS_BUILD_DIR done - -cd tests/rustapi_module -tox -e py -cd $TRAVIS_BUILD_DIR diff --git a/tests/rustapi_module/.gitignore b/examples/rustapi_module/.gitignore similarity index 100% rename from tests/rustapi_module/.gitignore rename to examples/rustapi_module/.gitignore diff --git a/tests/rustapi_module/Cargo.toml b/examples/rustapi_module/Cargo.toml similarity index 100% rename from tests/rustapi_module/Cargo.toml rename to examples/rustapi_module/Cargo.toml diff --git a/tests/rustapi_module/pyproject.toml b/examples/rustapi_module/pyproject.toml similarity index 100% rename from tests/rustapi_module/pyproject.toml rename to examples/rustapi_module/pyproject.toml diff --git a/tests/rustapi_module/requirements-dev.txt b/examples/rustapi_module/requirements-dev.txt similarity index 100% rename from tests/rustapi_module/requirements-dev.txt rename to examples/rustapi_module/requirements-dev.txt diff --git a/tests/rustapi_module/rustapi_module/__init__.py b/examples/rustapi_module/rustapi_module/__init__.py similarity index 100% rename from tests/rustapi_module/rustapi_module/__init__.py rename to examples/rustapi_module/rustapi_module/__init__.py diff --git a/tests/rustapi_module/setup.py b/examples/rustapi_module/setup.py similarity index 100% rename from tests/rustapi_module/setup.py rename to examples/rustapi_module/setup.py diff --git a/tests/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs similarity index 100% rename from tests/rustapi_module/src/lib.rs rename to examples/rustapi_module/src/lib.rs diff --git a/tests/rustapi_module/tests/test_datetime.py b/examples/rustapi_module/tests/test_datetime.py similarity index 100% rename from tests/rustapi_module/tests/test_datetime.py rename to examples/rustapi_module/tests/test_datetime.py diff --git a/tests/rustapi_module/tox.ini b/examples/rustapi_module/tox.ini similarity index 100% rename from tests/rustapi_module/tox.ini rename to examples/rustapi_module/tox.ini From 113de1bcd3d15378755a393cb6b5047e5baeb6c3 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 12:48:22 -0400 Subject: [PATCH 37/44] Drop setup_requires from rustapi_module The PEP 518 way to do this is with pyproject.toml. tox doesn't support PEP 518 yet, but we get around that by using pip install -e . as part of the tox build until PEP 518 support arrives in tox. --- examples/rustapi_module/setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/rustapi_module/setup.py b/examples/rustapi_module/setup.py index e55d8d61..0efaae03 100644 --- a/examples/rustapi_module/setup.py +++ b/examples/rustapi_module/setup.py @@ -30,7 +30,6 @@ def get_py_version_cfgs(): return out_cfg -setup_requires = ['setuptools-rust>=0.6.1'] install_requires = [] tests_require = install_requires + ['pytest', 'pytest-benchmark'] @@ -51,7 +50,6 @@ setup( rustc_flags=get_py_version_cfgs())], install_requires=install_requires, tests_require=tests_require, - setup_requires=setup_requires, include_package_data=True, zip_safe=False, cmdclass=dict(test=PyTest) From 5d5689f95bbe2332301cc33a83f3c75f0ac8b838 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 13:43:32 -0400 Subject: [PATCH 38/44] 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. --- examples/rustapi_module/src/lib.rs | 36 +++++----- src/objects/datetime.rs | 102 ++++++++++++++--------------- tests/test_datetime.rs | 70 ++++++++++++-------- 3 files changed, 112 insertions(+), 96 deletions(-) diff --git a/examples/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs index 2182ddc5..054289bb 100644 --- a/examples/rustapi_module/src/lib.rs +++ b/examples/rustapi_module/src/lib.rs @@ -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> { +fn make_date(py: Python, year: i32, month: i32, day: i32) -> PyResult> { PyDate::new(py, year, month, day) } @@ -33,10 +33,10 @@ fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { #[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> { 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> { @@ -94,7 +94,7 @@ fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py { 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 { #[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> { PyDateTime::new( @@ -170,7 +170,7 @@ fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py { dt.get_minute(), dt.get_second(), dt.get_microsecond(), - dt.get_fold() as u32, + dt.get_fold() as i32, ], ) } diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 17e48f28..1539b748 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -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> { + pub fn new(py: Python, year: i32, month: i32, day: i32) -> PyResult> { 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> { 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> { 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> { @@ -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)] diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index 5f3f0071..dff2ba2b 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -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, From a05a78f7e0acdc7c4311d1944f9df7e9e67994ba Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 14:14:01 -0400 Subject: [PATCH 39/44] Use smallest types for Py{Date}{Time} constructors Because it's unlikely that anything other than the `year` parameter will change in the C Python API, the rest can be restricted to their logical ranges, which improves the compile-time error checking. --- examples/rustapi_module/src/lib.rs | 73 ++++++++++++------------ src/objects/datetime.rs | 90 +++++++++++++++--------------- tests/test_datetime.rs | 39 +++++-------- 3 files changed, 96 insertions(+), 106 deletions(-) diff --git a/examples/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs index 054289bb..8087851a 100644 --- a/examples/rustapi_module/src/lib.rs +++ b/examples/rustapi_module/src/lib.rs @@ -14,13 +14,16 @@ use pyo3::{ObjectProtocol, ToPyObject}; use pyo3::{Py, PyResult, Python}; #[pyfunction] -fn make_date(py: Python, year: i32, month: i32, day: i32) -> PyResult> { +fn make_date(py: Python, year: i32, month: u8, day: u8) -> PyResult> { PyDate::new(py, year, month, day) } #[pyfunction] fn get_date_tuple(py: Python, d: &PyDate) -> Py { - PyTuple::new(py, &[d.get_year(), d.get_month(), d.get_day()]) + PyTuple::new( + py, + &[d.get_year(), d.get_month() as i32, d.get_day() as i32], + ) } #[pyfunction] @@ -33,10 +36,10 @@ fn date_from_timestamp(py: Python, ts: i64) -> PyResult> { #[pyfunction] fn make_time( py: Python, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult> { PyTime::new( @@ -53,10 +56,10 @@ fn make_time( #[pyfunction] fn time_with_fold( py: Python, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyTzInfo>, fold: bool, ) -> PyResult> { @@ -76,9 +79,9 @@ fn get_time_tuple(py: Python, dt: &PyTime) -> Py { PyTuple::new( py, &[ - dt.get_hour(), - dt.get_minute(), - dt.get_second(), + dt.get_hour() as u32, + dt.get_minute() as u32, + dt.get_second() as u32, dt.get_microsecond(), ], ) @@ -90,11 +93,11 @@ fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py { PyTuple::new( py, &[ - dt.get_hour(), - dt.get_minute(), - dt.get_second(), + dt.get_hour() as u32, + dt.get_minute() as u32, + dt.get_second() as u32, dt.get_microsecond(), - dt.get_fold() as i32, + dt.get_fold() as u32, ], ) } @@ -120,12 +123,12 @@ fn get_delta_tuple(py: Python, delta: &PyDelta) -> Py { fn make_datetime( py: Python, year: i32, - month: i32, - day: i32, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyTzInfo>, ) -> PyResult> { PyDateTime::new( @@ -147,12 +150,12 @@ fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py { py, &[ dt.get_year(), - dt.get_month(), - dt.get_day(), - dt.get_hour(), - dt.get_minute(), - dt.get_second(), - dt.get_microsecond(), + dt.get_month() as i32, + dt.get_day() as i32, + dt.get_hour() as i32, + dt.get_minute() as i32, + dt.get_second() as i32, + dt.get_microsecond() as i32, ], ) } @@ -164,12 +167,12 @@ fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py { py, &[ dt.get_year(), - dt.get_month(), - dt.get_day(), - dt.get_hour(), - dt.get_minute(), - dt.get_second(), - dt.get_microsecond(), + dt.get_month() as i32, + dt.get_day() as i32, + dt.get_hour() as i32, + dt.get_minute() as i32, + dt.get_second() as i32, + dt.get_microsecond() as i32, dt.get_fold() as i32, ], ) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index 1539b748..ae1d6d9b 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -29,8 +29,8 @@ use python::{Python, ToPyPointer}; // Traits pub trait PyDateAccess { fn get_year(&self) -> i32; - fn get_month(&self) -> i32; - fn get_day(&self) -> i32; + fn get_month(&self) -> u8; + fn get_day(&self) -> u8; } pub trait PyDeltaAccess { @@ -40,10 +40,10 @@ pub trait PyDeltaAccess { } pub trait PyTimeAccess { - fn get_hour(&self) -> i32; - fn get_minute(&self) -> i32; - fn get_second(&self) -> i32; - fn get_microsecond(&self) -> i32; + fn get_hour(&self) -> u8; + fn get_minute(&self) -> u8; + fn get_second(&self) -> u8; + fn get_microsecond(&self) -> u32; #[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: i32, month: i32, day: i32) -> PyResult> { + pub fn new(py: Python, year: i32, month: u8, day: u8) -> PyResult> { unsafe { let ptr = (PyDateTimeAPI.Date_FromDate)( year as c_int, @@ -78,12 +78,12 @@ impl PyDateAccess for PyDate { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 } } - fn get_month(&self) -> i32 { - unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as i32 } + fn get_month(&self) -> u8 { + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } } - fn get_day(&self) -> i32 { - unsafe { PyDateTime_GET_DAY(self.as_ptr()) as i32 } + fn get_day(&self) -> u8 { + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } } } @@ -95,12 +95,12 @@ impl PyDateTime { pub fn new( py: Python, year: i32, - month: i32, - day: i32, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { @@ -143,30 +143,30 @@ impl PyDateAccess for PyDateTime { unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 } } - fn get_month(&self) -> i32 { - unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as i32 } + fn get_month(&self) -> u8 { + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } } - fn get_day(&self) -> i32 { - unsafe { PyDateTime_GET_DAY(self.as_ptr()) as i32 } + fn get_day(&self) -> u8 { + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } } } impl PyTimeAccess for PyDateTime { - fn get_hour(&self) -> i32 { - unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as i32 } + fn get_hour(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u8 } } - fn get_minute(&self) -> i32 { - unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as i32 } + fn get_minute(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u8 } } - fn get_second(&self) -> i32 { - unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as i32 } + fn get_second(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u8 } } - fn get_microsecond(&self) -> i32 { - unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as i32 } + fn get_microsecond(&self) -> u32 { + unsafe { PyDateTime_DATE_GET_MICROSECOND(self.as_ptr()) as u32 } } #[cfg(Py_3_6)] @@ -182,10 +182,10 @@ pyobject_native_type!(PyTime, PyDateTime_TimeType, PyTime_Check); impl PyTime { pub fn new( py: Python, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyObject>, ) -> PyResult> { unsafe { @@ -207,10 +207,10 @@ impl PyTime { #[cfg(Py_3_6)] pub fn new_with_fold( py: Python, - hour: i32, - minute: i32, - second: i32, - microsecond: i32, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, tzinfo: Option<&PyObject>, fold: bool, ) -> PyResult> { @@ -233,20 +233,20 @@ impl PyTime { } impl PyTimeAccess for PyTime { - fn get_hour(&self) -> i32 { - unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as i32 } + fn get_hour(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u8 } } - fn get_minute(&self) -> i32 { - unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as i32 } + fn get_minute(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u8 } } - fn get_second(&self) -> i32 { - unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as i32 } + fn get_second(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u8 } } - fn get_microsecond(&self) -> i32 { - unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as i32 } + fn get_microsecond(&self) -> u32 { + unsafe { PyDateTime_TIME_GET_MICROSECOND(self.as_ptr()) as u32 } } #[cfg(Py_3_6)] diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs index dff2ba2b..61eb7b5f 100644 --- a/tests/test_datetime.rs +++ b/tests/test_datetime.rs @@ -4,8 +4,8 @@ extern crate pyo3; use std::iter; -use pyo3::prelude::*; use pyo3::ffi::*; +use pyo3::prelude::*; fn _get_subclasses<'p>( py: &'p Python, @@ -124,33 +124,20 @@ 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), +static INVALID_DATES: &'static [(i32, u8, u8)] = &[ + (-1, 1, 1), + (0, 1, 1), + (10000, 1, 1), + (2 << 30, 1, 1), + (2018, 0, 1), + (2018, 13, 1), + (2018, 1, 0), + (2017, 2, 29), + (2018, 1, 32), ]; +static INVALID_TIMES: &'static [(u8, u8, u8, u32)] = + &[(25, 0, 0, 0), (255, 0, 0, 0), (0, 60, 0, 0), (0, 0, 61, 0)]; #[cfg(Py_3_6)] #[test] From 7053c897aa7a19425b3e756f3fb71bc8129d7c99 Mon Sep 17 00:00:00 2001 From: konstin Date: Tue, 21 Aug 2018 21:07:07 +0200 Subject: [PATCH 40/44] Get rid of #[inline(always)] and replace it with #[inline] I've just seen that this had been hidden from clippy through the ffi module reordering, but fixing this on master would cause merge conflicts, so I'm fixing this here directly --- pyo3-derive-backend/src/py_class.rs | 4 +-- src/err.rs | 2 +- src/ffi/datetime.rs | 52 ++++++++++++++--------------- src/ffi/mod.rs | 1 - src/ffi2/boolobject.rs | 6 ++-- src/ffi2/bufferobject.rs | 2 +- src/ffi2/bytearrayobject.rs | 4 +-- src/ffi2/cellobject.rs | 6 ++-- src/ffi2/classobject.rs | 12 +++---- src/ffi2/cobject.rs | 2 +- src/ffi2/code.rs | 2 +- src/ffi2/complexobject.rs | 4 +-- src/ffi2/descrobject.rs | 2 +- src/ffi2/dictobject.rs | 4 +-- src/ffi2/fileobject.rs | 4 +-- src/ffi2/floatobject.rs | 4 +-- src/ffi2/funcobject.rs | 2 +- src/ffi2/genobject.rs | 4 +-- src/ffi2/intobject.rs | 4 +-- src/ffi2/iterobject.rs | 4 +-- src/ffi2/listobject.rs | 10 +++--- src/ffi2/longobject.rs | 4 +-- src/ffi2/memoryobject.rs | 6 ++-- src/ffi2/methodobject.rs | 4 +-- src/ffi2/mod.rs | 4 +-- src/ffi2/modsupport.rs | 10 +++--- src/ffi2/moduleobject.rs | 4 +-- src/ffi2/object.rs | 34 +++++++++---------- src/ffi2/objimpl.rs | 8 ++--- src/ffi2/pystate.rs | 4 +-- src/ffi2/rangeobject.rs | 2 +- src/ffi2/sliceobject.rs | 4 +-- src/ffi2/stringobject.rs | 10 +++--- src/ffi2/traceback.rs | 2 +- src/ffi2/tupleobject.rs | 10 +++--- src/ffi2/unicodeobject.rs | 24 ++++++------- src/ffi2/weakrefobject.rs | 10 +++--- src/ffi3/boolobject.rs | 6 ++-- src/ffi3/bytearrayobject.rs | 4 +-- src/ffi3/bytesobject.rs | 4 +-- src/ffi3/complexobject.rs | 4 +-- src/ffi3/dictobject.rs | 12 +++---- src/ffi3/floatobject.rs | 4 +-- src/ffi3/genobject.rs | 12 +++---- src/ffi3/iterobject.rs | 4 +-- src/ffi3/listobject.rs | 4 +-- src/ffi3/longobject.rs | 4 +-- src/ffi3/memoryobject.rs | 2 +- src/ffi3/methodobject.rs | 4 +-- src/ffi3/moduleobject.rs | 4 +-- src/ffi3/object.rs | 32 +++++++++--------- src/ffi3/objimpl.rs | 8 ++--- src/ffi3/pystate.rs | 2 +- src/ffi3/rangeobject.rs | 2 +- src/ffi3/sliceobject.rs | 4 +-- src/ffi3/traceback.rs | 2 +- src/ffi3/tupleobject.rs | 10 +++--- src/ffi3/unicodeobject.rs | 4 +-- src/ffi3/weakrefobject.rs | 8 ++--- src/instance.rs | 2 +- src/objects/exc.rs | 2 +- src/objects/exc_impl.rs | 2 +- src/objects/mod.rs | 4 +-- src/typeob.rs | 2 +- 64 files changed, 213 insertions(+), 214 deletions(-) diff --git a/pyo3-derive-backend/src/py_class.rs b/pyo3-derive-backend/src/py_class.rs index da09a509..cab748fc 100644 --- a/pyo3-derive-backend/src/py_class.rs +++ b/pyo3-derive-backend/src/py_class.rs @@ -86,7 +86,7 @@ fn impl_class( let extra = if let Some(token) = token { Some(quote! { impl ::pyo3::PyObjectWithToken for #cls { - #[inline(always)] + #[inline] fn py<'p>(&'p self) -> ::pyo3::Python<'p> { self.#token.py() } @@ -243,7 +243,7 @@ fn impl_class( } impl ::pyo3::typeob::PyTypeObject for #cls { - #[inline(always)] + #[inline] fn init_type() { static START: std::sync::Once = std::sync::ONCE_INIT; START.call_once(|| { diff --git a/src/err.rs b/src/err.rs index 8023f108..8012ac8a 100644 --- a/src/err.rs +++ b/src/err.rs @@ -90,7 +90,7 @@ macro_rules! py_exception { } impl $crate::typeob::PyTypeObject for $name { - #[inline(always)] + #[inline] fn init_type() { let _ = $name::type_object(); } diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index ca525cb3..eb94902e 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -185,7 +185,7 @@ impl Deref for PyDateTimeAPI { } } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI { // PyDateTime_CAPSULE_NAME is a macro in C let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap(); @@ -202,52 +202,52 @@ pub unsafe fn PyDateTime_IMPORT() -> &'static PyDateTime_CAPI { // // Type Check macros // -#[inline(always)] +#[inline] pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == PyDateTimeAPI.TZInfoType) as c_int } @@ -322,65 +322,65 @@ macro_rules! _PyDateTime_GET_TZINFO { } // Accessor functions for DateTime -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int { _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DATE_GET_MINUTE(o: *mut PyObject) -> c_int { _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int { _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DATE_GET_MICROSECOND(o: *mut PyObject) -> c_int { _PyDateTime_GET_MICROSECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) } #[cfg(Py_3_6)] -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar { _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime) } -#[inline(always)] +#[inline] 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)] +#[inline] pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int { _PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int { _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int { _PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0) } -#[inline(always)] +#[inline] 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)] +#[inline] pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar { _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_TIME_GET_TZINFO(o: *mut PyObject) -> *mut PyObject { _PyDateTime_GET_TZINFO!(o as *mut PyDateTime_Time) } @@ -392,17 +392,17 @@ macro_rules! _access_delta_field { }; } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int { _access_delta_field!(o, days) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int { _access_delta_field!(o, seconds) } -#[inline(always)] +#[inline] pub unsafe fn PyDateTime_DELTA_GET_MICROSECONDS(o: *mut PyObject) -> c_int { _access_delta_field!(o, microseconds) } diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 72bd6c3c..346857f7 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -1,5 +1,4 @@ #![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] -#![cfg_attr(feature="cargo-clippy", allow(inline_always))] #[cfg(not(Py_3))] pub use ffi2::*; diff --git a/src/ffi2/boolobject.rs b/src/ffi2/boolobject.rs index 0248736f..2cb7553f 100644 --- a/src/ffi2/boolobject.rs +++ b/src/ffi2/boolobject.rs @@ -12,18 +12,18 @@ extern "C" { pub fn PyBool_FromLong(arg1: c_long) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PyBool_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyBool_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn Py_False() -> *mut PyObject { &mut _Py_ZeroStruct as *mut PyBoolObject as *mut PyObject } -#[inline(always)] +#[inline] pub unsafe fn Py_True() -> *mut PyObject { &mut _Py_TrueStruct as *mut PyBoolObject as *mut PyObject } diff --git a/src/ffi2/bufferobject.rs b/src/ffi2/bufferobject.rs index 7af2feec..7cc5a96b 100644 --- a/src/ffi2/bufferobject.rs +++ b/src/ffi2/bufferobject.rs @@ -7,7 +7,7 @@ extern "C" { pub static mut PyBuffer_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyBuffer_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyBuffer_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/bytearrayobject.rs b/src/ffi2/bytearrayobject.rs index 5dcd64b2..a86203b2 100644 --- a/src/ffi2/bytearrayobject.rs +++ b/src/ffi2/bytearrayobject.rs @@ -42,7 +42,7 @@ extern "C" { pub fn PyByteArray_Resize(bytearray: *mut PyObject, len: Py_ssize_t) -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyByteArray_AS_STRING(o: *mut PyObject) -> *mut c_char { PyByteArray_AsString(o) // #define PyByteArray_AS_STRING(self) \ @@ -50,7 +50,7 @@ pub unsafe fn PyByteArray_AS_STRING(o: *mut PyObject) -> *mut c_char { // Py_SIZE(self) ? ((PyByteArrayObject *)(self))->ob_bytes : _PyByteArray_empty_string) } -#[inline(always)] +#[inline] pub unsafe fn PyByteArray_GET_SIZE(o: *mut PyObject) -> Py_ssize_t { // #define PyByteArray_GET_SIZE(self) (assert(PyByteArray_Check(self)),Py_SIZE(self)) PyByteArray_Size(o) diff --git a/src/ffi2/cellobject.rs b/src/ffi2/cellobject.rs index 486f0bae..a74c3298 100644 --- a/src/ffi2/cellobject.rs +++ b/src/ffi2/cellobject.rs @@ -19,7 +19,7 @@ extern "C" { pub static mut PyCell_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCell_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCell_Type) as c_int } @@ -31,12 +31,12 @@ extern "C" { pub fn PyCell_Set(op: *mut PyObject, obj: *mut PyObject) -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyCell_GET(op: *mut PyObject) -> *mut PyObject { (*(op as *mut PyCellObject)).ob_ref } -#[inline(always)] +#[inline] pub unsafe fn PyCell_SET(op: *mut PyObject, obj: *mut PyObject) { (*(op as *mut PyCellObject)).ob_ref = obj; } diff --git a/src/ffi2/classobject.rs b/src/ffi2/classobject.rs index cf4132bc..92384371 100644 --- a/src/ffi2/classobject.rs +++ b/src/ffi2/classobject.rs @@ -56,19 +56,19 @@ extern "C" { pub static mut PyMethod_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyClass_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyClass_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyInstance_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyInstance_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyMethod_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyMethod_Type; (Py_TYPE(op) == u) as c_int @@ -100,17 +100,17 @@ extern "C" { pub fn PyMethod_ClearFreeList() -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyMethod_GET_FUNCTION(meth: *mut PyObject) -> *mut PyObject { (*(meth as *mut PyMethodObject)).im_func } -#[inline(always)] +#[inline] pub unsafe fn PyMethod_GET_SELF(meth: *mut PyObject) -> *mut PyObject { (*(meth as *mut PyMethodObject)).im_self } -#[inline(always)] +#[inline] pub unsafe fn PyMethod_GET_CLASS(meth: *mut PyObject) -> *mut PyObject { (*(meth as *mut PyMethodObject)).im_class } diff --git a/src/ffi2/cobject.rs b/src/ffi2/cobject.rs index 8c1ab3cd..60f7d55a 100644 --- a/src/ffi2/cobject.rs +++ b/src/ffi2/cobject.rs @@ -6,7 +6,7 @@ extern "C" { pub static mut PyCObject_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCObject_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCObject_Type) as c_int } diff --git a/src/ffi2/code.rs b/src/ffi2/code.rs index 80c5be19..baea6be3 100644 --- a/src/ffi2/code.rs +++ b/src/ffi2/code.rs @@ -88,7 +88,7 @@ extern "C" { ) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCode_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCode_Type) as c_int } diff --git a/src/ffi2/complexobject.rs b/src/ffi2/complexobject.rs index 48a9b032..f650c4de 100644 --- a/src/ffi2/complexobject.rs +++ b/src/ffi2/complexobject.rs @@ -37,12 +37,12 @@ extern "C" { pub static mut PyComplex_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyComplex_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyComplex_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyComplex_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyComplex_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/descrobject.rs b/src/ffi2/descrobject.rs index 5c92cca5..c2a6c3ee 100644 --- a/src/ffi2/descrobject.rs +++ b/src/ffi2/descrobject.rs @@ -86,7 +86,7 @@ extern "C" { ) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PyDescr_IsData(d: *mut PyObject) -> c_int { (*Py_TYPE(d)).tp_descr_set.is_some() as c_int } diff --git a/src/ffi2/dictobject.rs b/src/ffi2/dictobject.rs index ec043bdc..036b05c6 100644 --- a/src/ffi2/dictobject.rs +++ b/src/ffi2/dictobject.rs @@ -15,12 +15,12 @@ extern "C" { pub static mut PyDictValues_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyDict_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyDict_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyDict_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/fileobject.rs b/src/ffi2/fileobject.rs index 591d87e6..cfe881f3 100644 --- a/src/ffi2/fileobject.rs +++ b/src/ffi2/fileobject.rs @@ -7,12 +7,12 @@ extern "C" { pub static mut PyFile_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyFile_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyFile_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyFile_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyFile_Type) as c_int } diff --git a/src/ffi2/floatobject.rs b/src/ffi2/floatobject.rs index efc96e20..a07c65e6 100644 --- a/src/ffi2/floatobject.rs +++ b/src/ffi2/floatobject.rs @@ -19,12 +19,12 @@ extern "C" { pub static mut PyFloat_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyFloat_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyFloat_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyFloat_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyFloat_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/funcobject.rs b/src/ffi2/funcobject.rs index 36ed08d1..ce0c1b44 100644 --- a/src/ffi2/funcobject.rs +++ b/src/ffi2/funcobject.rs @@ -6,7 +6,7 @@ extern "C" { pub static mut PyFunction_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyFunction_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyFunction_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/genobject.rs b/src/ffi2/genobject.rs index 43c3c197..82004d7b 100644 --- a/src/ffi2/genobject.rs +++ b/src/ffi2/genobject.rs @@ -23,12 +23,12 @@ extern "C" { pub static mut PyGen_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyGen_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyGen_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyGen_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyGen_Type) as c_int } diff --git a/src/ffi2/intobject.rs b/src/ffi2/intobject.rs index 25461132..921f8d37 100644 --- a/src/ffi2/intobject.rs +++ b/src/ffi2/intobject.rs @@ -20,12 +20,12 @@ extern "C" { pub static mut PyInt_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyInt_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_INT_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyInt_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyInt_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/iterobject.rs b/src/ffi2/iterobject.rs index 7f39f445..ee9e927a 100644 --- a/src/ffi2/iterobject.rs +++ b/src/ffi2/iterobject.rs @@ -10,12 +10,12 @@ extern "C" { pub fn PyCallIter_New(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PySeqIter_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PySeqIter_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyCallIter_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCallIter_Type) as c_int } diff --git a/src/ffi2/listobject.rs b/src/ffi2/listobject.rs index 6600aeb7..eefa4569 100644 --- a/src/ffi2/listobject.rs +++ b/src/ffi2/listobject.rs @@ -21,30 +21,30 @@ extern "C" { pub static mut PyList_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyList_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyList_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyList_Type; (Py_TYPE(op) == u) as c_int } // Macro, trading safety for speed -#[inline(always)] +#[inline] pub unsafe fn PyList_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { *(*(op as *mut PyListObject)).ob_item.offset(i as isize) } -#[inline(always)] +#[inline] pub unsafe fn PyList_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { Py_SIZE(op) } /// Macro, *only* to be used to fill in brand new lists -#[inline(always)] +#[inline] pub unsafe fn PyList_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) { *(*(op as *mut PyListObject)).ob_item.offset(i as isize) = v; } diff --git a/src/ffi2/longobject.rs b/src/ffi2/longobject.rs index 8dd4a234..c2cf0a42 100644 --- a/src/ffi2/longobject.rs +++ b/src/ffi2/longobject.rs @@ -12,12 +12,12 @@ extern "C" { pub static mut PyLong_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyLong_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyLong_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyLong_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/memoryobject.rs b/src/ffi2/memoryobject.rs index 80dddcf4..667e0ac8 100644 --- a/src/ffi2/memoryobject.rs +++ b/src/ffi2/memoryobject.rs @@ -7,18 +7,18 @@ extern "C" { pub static mut PyMemoryView_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyMemoryView_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyMemoryView_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyMemoryView_GET_BUFFER(op: *mut PyObject) -> *mut Py_buffer { &mut (*(op as *mut PyMemoryViewObject)).view } -#[inline(always)] +#[inline] pub unsafe fn PyMemoryView_GET_BASE(op: *mut PyObject) -> *mut PyObject { (*(op as *mut PyMemoryViewObject)).view.obj } diff --git a/src/ffi2/methodobject.rs b/src/ffi2/methodobject.rs index 64912efa..973849ff 100644 --- a/src/ffi2/methodobject.rs +++ b/src/ffi2/methodobject.rs @@ -7,7 +7,7 @@ extern "C" { pub static mut PyCFunction_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyCFunction_Type; (Py_TYPE(op) == u) as c_int @@ -119,7 +119,7 @@ extern "C" { pub fn PyCFunction_ClearFreeList() -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyCFunction_New(ml: *mut PyMethodDef, slf: *mut PyObject) -> *mut PyObject { PyCFunction_NewEx(ml, slf, ptr::null_mut()) } diff --git a/src/ffi2/mod.rs b/src/ffi2/mod.rs index 29a160a7..14b6a75d 100644 --- a/src/ffi2/mod.rs +++ b/src/ffi2/mod.rs @@ -132,13 +132,13 @@ pub const Py_file_input: c_int = 257; pub const Py_eval_input: c_int = 258; #[cfg(not(py_sys_config = "Py_USING_UNICODE"))] -#[inline(always)] +#[inline] pub fn PyUnicode_Check(op: *mut PyObject) -> libc::c_int { 0 } #[cfg(not(py_sys_config = "Py_USING_UNICODE"))] -#[inline(always)] +#[inline] pub fn PyUnicode_CheckExact(op: *mut PyObject) -> libc::c_int { 0 } diff --git a/src/ffi2/modsupport.rs b/src/ffi2/modsupport.rs index ba66fead..b915d4ca 100644 --- a/src/ffi2/modsupport.rs +++ b/src/ffi2/modsupport.rs @@ -95,7 +95,7 @@ pub const PYTHON_API_VERSION: c_int = 1013; target_pointer_width = "64", not(py_sys_config = "Py_TRACE_REFS") ))] -#[inline(always)] +#[inline] pub unsafe fn Py_InitModule4( name: *const c_char, methods: *mut PyMethodDef, @@ -107,7 +107,7 @@ pub unsafe fn Py_InitModule4( } #[cfg(all(target_pointer_width = "64", py_sys_config = "Py_TRACE_REFS"))] -#[inline(always)] +#[inline] pub unsafe fn Py_InitModule4( name: *const c_char, methods: *mut PyMethodDef, @@ -122,7 +122,7 @@ pub unsafe fn Py_InitModule4( not(target_pointer_width = "64"), py_sys_config = "Py_TRACE_REFS" ))] -#[inline(always)] +#[inline] pub unsafe fn Py_InitModule4( name: *const c_char, methods: *mut PyMethodDef, @@ -133,7 +133,7 @@ pub unsafe fn Py_InitModule4( Py_InitModule4TraceRefs(name, methods, doc, _self, apiver) } -#[inline(always)] +#[inline] pub unsafe fn Py_InitModule(name: *const c_char, methods: *mut PyMethodDef) -> *mut PyObject { Py_InitModule4( name, @@ -144,7 +144,7 @@ pub unsafe fn Py_InitModule(name: *const c_char, methods: *mut PyMethodDef) -> * ) } -#[inline(always)] +#[inline] pub unsafe fn Py_InitModule3( name: *const c_char, methods: *mut PyMethodDef, diff --git a/src/ffi2/moduleobject.rs b/src/ffi2/moduleobject.rs index 52546b18..fc6c3959 100644 --- a/src/ffi2/moduleobject.rs +++ b/src/ffi2/moduleobject.rs @@ -8,12 +8,12 @@ extern "C" { pub static mut PyModule_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyModule_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyModule_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyModule_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyModule_Type) as c_int } diff --git a/src/ffi2/object.rs b/src/ffi2/object.rs index be4fd28e..c9f88079 100644 --- a/src/ffi2/object.rs +++ b/src/ffi2/object.rs @@ -37,17 +37,17 @@ pub struct PyVarObject { pub ob_size: Py_ssize_t, } -#[inline(always)] +#[inline] pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { (*ob).ob_refcnt } -#[inline(always)] +#[inline] pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { (*ob).ob_type } -#[inline(always)] +#[inline] pub unsafe fn Py_SIZE(ob: *mut PyObject) -> Py_ssize_t { (*(ob as *mut PyVarObject)).ob_size } @@ -564,7 +564,7 @@ extern "C" { pub fn PyType_IsSubtype(a: *mut PyTypeObject, b: *mut PyTypeObject) -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyObject_TypeCheck(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { (Py_TYPE(ob) == tp || PyType_IsSubtype(Py_TYPE(ob), tp) != 0) as c_int } @@ -576,12 +576,12 @@ extern "C" { pub static mut PySuper_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyType_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyType_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == (&mut PyType_Type as *mut _)) as c_int } @@ -611,7 +611,7 @@ extern "C" { pub fn PyObject_Str(o: *mut PyObject) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PyObject_Bytes(o: *mut PyObject) -> *mut PyObject { PyObject_Str(o) } @@ -755,18 +755,18 @@ pub const Py_TPFLAGS_DEFAULT: c_long = (Py_TPFLAGS_HAVE_GETCHARBUFFER | Py_TPFLAGS_HAVE_INDEX | 0); -#[inline(always)] +#[inline] pub unsafe fn PyType_HasFeature(t: *mut PyTypeObject, f: c_long) -> c_int { (((*t).tp_flags & f) != 0) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyType_FastSubclass(t: *mut PyTypeObject, f: c_long) -> c_int { PyType_HasFeature(t, f) } // Reference counting macros. -#[inline(always)] +#[inline] pub unsafe fn Py_INCREF(op: *mut PyObject) { if cfg!(py_sys_config = "Py_REF_DEBUG") { Py_IncRef(op) @@ -775,7 +775,7 @@ pub unsafe fn Py_INCREF(op: *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_DECREF(op: *mut PyObject) { if cfg!(py_sys_config = "Py_REF_DEBUG") || cfg!(py_sys_config = "COUNT_ALLOCS") { Py_DecRef(op) @@ -787,7 +787,7 @@ pub unsafe fn Py_DECREF(op: *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_CLEAR(op: &mut *mut PyObject) { let tmp = *op; if !tmp.is_null() { @@ -796,14 +796,14 @@ pub unsafe fn Py_CLEAR(op: &mut *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_XINCREF(op: *mut PyObject) { if !op.is_null() { Py_INCREF(op) } } -#[inline(always)] +#[inline] pub unsafe fn Py_XDECREF(op: *mut PyObject) { if !op.is_null() { Py_DECREF(op) @@ -819,12 +819,12 @@ extern "C" { static mut _Py_NotImplementedStruct: PyObject; } -#[inline(always)] +#[inline] pub unsafe fn Py_None() -> *mut PyObject { &mut _Py_NoneStruct } -#[inline(always)] +#[inline] pub unsafe fn Py_NotImplemented() -> *mut PyObject { &mut _Py_NotImplementedStruct } @@ -855,7 +855,7 @@ extern "C" { pub const PyTrash_UNWIND_LEVEL: c_int = 50; -#[inline(always)] +#[inline] pub unsafe fn Py_TRASHCAN ()>(op: *mut PyObject, body: F) { let tstate = ffi2::pystate::PyThreadState_GET(); if tstate.is_null() || (*tstate).trash_delete_nesting < PyTrash_UNWIND_LEVEL { diff --git a/src/ffi2/objimpl.rs b/src/ffi2/objimpl.rs index e9342989..ff98af60 100644 --- a/src/ffi2/objimpl.rs +++ b/src/ffi2/objimpl.rs @@ -30,14 +30,14 @@ extern "C" { } /// Test if a type has a GC head -#[inline(always)] +#[inline] #[allow(unused_parens)] pub unsafe fn PyType_IS_GC(t: *mut PyTypeObject) -> c_int { PyType_HasFeature((t), Py_TPFLAGS_HAVE_GC) } /// Test if an object has a GC head -#[inline(always)] +#[inline] pub unsafe fn PyObject_IS_GC(o: *mut PyObject) -> c_int { (PyType_IS_GC(Py_TYPE(o)) != 0 && match (*Py_TYPE(o)).tp_is_gc { Some(tp_is_gc) => tp_is_gc(o) != 0, @@ -46,13 +46,13 @@ pub unsafe fn PyObject_IS_GC(o: *mut PyObject) -> c_int { } /* Test if a type supports weak references */ -#[inline(always)] +#[inline] #[allow(unused_parens)] pub unsafe fn PyType_SUPPORTS_WEAKREFS(t: *mut PyTypeObject) -> c_int { (PyType_HasFeature((t), Py_TPFLAGS_HAVE_WEAKREFS) != 0 && ((*t).tp_weaklistoffset > 0)) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyObject_GET_WEAKREFS_LISTPTR(o: *mut PyObject) -> *mut *mut PyObject { let weaklistoffset = (*Py_TYPE(o)).tp_weaklistoffset as isize; (o as *mut c_char).offset(weaklistoffset) as *mut *mut PyObject diff --git a/src/ffi2/pystate.rs b/src/ffi2/pystate.rs index eb2d5320..f17807b8 100644 --- a/src/ffi2/pystate.rs +++ b/src/ffi2/pystate.rs @@ -92,13 +92,13 @@ extern "C" { } #[cfg(py_sys_config = "Py_DEBUG")] -#[inline(always)] +#[inline] pub unsafe fn PyThreadState_GET() -> *mut PyThreadState { PyThreadState_Get() } #[cfg(not(py_sys_config = "Py_DEBUG"))] -#[inline(always)] +#[inline] pub unsafe fn PyThreadState_GET() -> *mut PyThreadState { _PyThreadState_Current } diff --git a/src/ffi2/rangeobject.rs b/src/ffi2/rangeobject.rs index 4674aac8..7153a802 100644 --- a/src/ffi2/rangeobject.rs +++ b/src/ffi2/rangeobject.rs @@ -6,7 +6,7 @@ extern "C" { pub static mut PyRange_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyRange_Check(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyRange_Type; (Py_TYPE(op) == u) as c_int diff --git a/src/ffi2/sliceobject.rs b/src/ffi2/sliceobject.rs index 8fa6c264..c3594da2 100644 --- a/src/ffi2/sliceobject.rs +++ b/src/ffi2/sliceobject.rs @@ -7,7 +7,7 @@ extern "C" { static mut _Py_EllipsisObject: PyObject; } -#[inline(always)] +#[inline] pub unsafe fn Py_Ellipsis() -> *mut PyObject { &mut _Py_EllipsisObject } @@ -32,7 +32,7 @@ extern "C" { pub static mut PyEllipsis_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PySlice_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PySlice_Type) as c_int } diff --git a/src/ffi2/stringobject.rs b/src/ffi2/stringobject.rs index 80fc742d..ca69720f 100644 --- a/src/ffi2/stringobject.rs +++ b/src/ffi2/stringobject.rs @@ -23,12 +23,12 @@ extern "C" { pub static mut PyString_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyString_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_STRING_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyBaseString_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass( Py_TYPE(op), @@ -36,18 +36,18 @@ pub unsafe fn PyBaseString_Check(op: *mut PyObject) -> c_int { ) } -#[inline(always)] +#[inline] pub unsafe fn PyString_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyString_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyString_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { (*(op as *mut PyStringObject)).ob_size } -#[inline(always)] +#[inline] pub unsafe fn PyString_AS_STRING(op: *mut PyObject) -> *mut c_char { (*(op as *mut PyStringObject)).ob_sval.as_mut_ptr() } diff --git a/src/ffi2/traceback.rs b/src/ffi2/traceback.rs index 346e5771..2f98da2b 100644 --- a/src/ffi2/traceback.rs +++ b/src/ffi2/traceback.rs @@ -26,7 +26,7 @@ extern "C" { pub static mut PyTraceBack_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyTraceBack_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyTraceBack_Type) as c_int } diff --git a/src/ffi2/tupleobject.rs b/src/ffi2/tupleobject.rs index eed43b31..5b8eff7e 100644 --- a/src/ffi2/tupleobject.rs +++ b/src/ffi2/tupleobject.rs @@ -20,19 +20,19 @@ extern "C" { pub static mut PyTuple_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyTuple_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyTuple_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyTuple_Type; (Py_TYPE(op) == u) as c_int } // Macro, trading safety for speed -#[inline(always)] +#[inline] pub unsafe fn PyTuple_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { *(*(op as *mut PyTupleObject)) .ob_item @@ -40,13 +40,13 @@ pub unsafe fn PyTuple_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObjec .offset(i as isize) } -#[inline(always)] +#[inline] pub unsafe fn PyTuple_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { Py_SIZE(op) } /// Macro, *only* to be used to fill in brand new tuples -#[inline(always)] +#[inline] pub unsafe fn PyTuple_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) { *(*(op as *mut PyTupleObject)) .ob_item diff --git a/src/ffi2/unicodeobject.rs b/src/ffi2/unicodeobject.rs index 0bb57d2a..bbd7add1 100644 --- a/src/ffi2/unicodeobject.rs +++ b/src/ffi2/unicodeobject.rs @@ -35,33 +35,33 @@ extern "C" { pub static mut PyUnicode_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_CheckExact(op: *mut PyObject) -> c_int { let u: *mut PyTypeObject = &mut PyUnicode_Type; (Py_TYPE(op) == u) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_GET_SIZE(o: *mut PyObject) -> Py_ssize_t { (*(o as *mut PyUnicodeObject)).length } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_GET_DATA_SIZE(o: *mut PyObject) -> Py_ssize_t { (*(o as *mut PyUnicodeObject)).length * Py_UNICODE_SIZE } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_AS_UNICODE(o: *mut PyObject) -> *mut Py_UNICODE { (*(o as *mut PyUnicodeObject)).data } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_AS_DATA(o: *mut PyObject) -> *const c_char { (*(o as *mut PyUnicodeObject)).data as *const c_char } @@ -642,31 +642,31 @@ extern "C" { fn _PyUnicodeUCS2_IsAlpha(ch: Py_UNICODE) -> c_int; } -#[inline(always)] +#[inline] #[cfg(py_sys_config = "Py_UNICODE_SIZE_4")] pub unsafe fn PyUnicode_FromStringAndSize(u: *const c_char, size: Py_ssize_t) -> *mut PyObject { PyUnicodeUCS4_FromStringAndSize(u, size) } -#[inline(always)] +#[inline] #[cfg(not(py_sys_config = "Py_UNICODE_SIZE_4"))] pub unsafe fn PyUnicode_FromStringAndSize(u: *const c_char, size: Py_ssize_t) -> *mut PyObject { PyUnicodeUCS2_FromStringAndSize(u, size) } -#[inline(always)] +#[inline] #[cfg(py_sys_config = "Py_UNICODE_SIZE_4")] pub unsafe fn PyUnicode_AsUTF8String(u: *mut PyObject) -> *mut PyObject { PyUnicodeUCS4_AsUTF8String(u) } -#[inline(always)] +#[inline] #[cfg(not(py_sys_config = "Py_UNICODE_SIZE_4"))] pub unsafe fn PyUnicode_AsUTF8String(u: *mut PyObject) -> *mut PyObject { PyUnicodeUCS2_AsUTF8String(u) } -#[inline(always)] +#[inline] #[cfg(py_sys_config = "Py_UNICODE_SIZE_4")] pub unsafe fn PyUnicode_FromEncodedObject( obj: *mut PyObject, @@ -676,7 +676,7 @@ pub unsafe fn PyUnicode_FromEncodedObject( PyUnicodeUCS4_FromEncodedObject(obj, encoding, errors) } -#[inline(always)] +#[inline] #[cfg(not(py_sys_config = "Py_UNICODE_SIZE_4"))] pub unsafe fn PyUnicode_FromEncodedObject( obj: *mut PyObject, diff --git a/src/ffi2/weakrefobject.rs b/src/ffi2/weakrefobject.rs index abf00b61..a1bd3e3c 100644 --- a/src/ffi2/weakrefobject.rs +++ b/src/ffi2/weakrefobject.rs @@ -25,23 +25,23 @@ extern "C" { static mut _PyWeakref_CallableProxyType: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckRef(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut _PyWeakref_RefType) } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckRefExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut _PyWeakref_RefType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckProxy(op: *mut PyObject) -> c_int { ((Py_TYPE(op) == &mut _PyWeakref_ProxyType) || (Py_TYPE(op) == &mut _PyWeakref_CallableProxyType)) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_Check(op: *mut PyObject) -> c_int { (PyWeakref_CheckRef(op) != 0 || PyWeakref_CheckProxy(op) != 0) as c_int } @@ -56,7 +56,7 @@ extern "C" { pub fn _PyWeakref_ClearRef(slf: *mut PyWeakReference); } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_GET_OBJECT(_ref: *mut PyObject) -> *mut PyObject { let obj = (*(_ref as *mut PyWeakReference)).wr_object; if Py_REFCNT(obj) > 0 { diff --git a/src/ffi3/boolobject.rs b/src/ffi3/boolobject.rs index 6949067c..0c669b2f 100644 --- a/src/ffi3/boolobject.rs +++ b/src/ffi3/boolobject.rs @@ -10,17 +10,17 @@ extern "C" { pub fn PyBool_FromLong(arg1: c_long) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PyBool_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyBool_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn Py_False() -> *mut PyObject { &mut _Py_FalseStruct as *mut PyLongObject as *mut PyObject } -#[inline(always)] +#[inline] pub unsafe fn Py_True() -> *mut PyObject { &mut _Py_TrueStruct as *mut PyLongObject as *mut PyObject } diff --git a/src/ffi3/bytearrayobject.rs b/src/ffi3/bytearrayobject.rs index 40799b40..1e545528 100644 --- a/src/ffi3/bytearrayobject.rs +++ b/src/ffi3/bytearrayobject.rs @@ -8,12 +8,12 @@ extern "C" { pub static mut PyByteArrayIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyByteArray_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyByteArray_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyByteArray_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyByteArray_Type) as c_int } diff --git a/src/ffi3/bytesobject.rs b/src/ffi3/bytesobject.rs index ca7569d0..4d1469e1 100644 --- a/src/ffi3/bytesobject.rs +++ b/src/ffi3/bytesobject.rs @@ -8,12 +8,12 @@ extern "C" { pub static mut PyBytesIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyBytes_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_BYTES_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyBytes_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyBytes_Type) as c_int } diff --git a/src/ffi3/complexobject.rs b/src/ffi3/complexobject.rs index b003e666..8b3ff0f5 100644 --- a/src/ffi3/complexobject.rs +++ b/src/ffi3/complexobject.rs @@ -6,12 +6,12 @@ extern "C" { pub static mut PyComplex_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyComplex_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyComplex_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyComplex_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyComplex_Type) as c_int } diff --git a/src/ffi3/dictobject.rs b/src/ffi3/dictobject.rs index 20d58d1a..dc4c16d8 100644 --- a/src/ffi3/dictobject.rs +++ b/src/ffi3/dictobject.rs @@ -13,32 +13,32 @@ extern "C" { pub static mut PyDictValues_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyDict_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_DICT_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyDict_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyDict_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDictKeys_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyDictKeys_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDictItems_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyDictItems_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDictValues_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyDictValues_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyDictViewSet_Check(op: *mut PyObject) -> c_int { (PyDictKeys_Check(op) != 0 || PyDictItems_Check(op) != 0) as c_int } diff --git a/src/ffi3/floatobject.rs b/src/ffi3/floatobject.rs index 6c3bdb46..f1b5439e 100644 --- a/src/ffi3/floatobject.rs +++ b/src/ffi3/floatobject.rs @@ -6,12 +6,12 @@ extern "C" { pub static mut PyFloat_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyFloat_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyFloat_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyFloat_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyFloat_Type) as c_int } diff --git a/src/ffi3/genobject.rs b/src/ffi3/genobject.rs index e1dad5ae..ff22fea0 100644 --- a/src/ffi3/genobject.rs +++ b/src/ffi3/genobject.rs @@ -23,12 +23,12 @@ extern "C" { pub static mut PyGen_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyGen_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyGen_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyGen_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyGen_Type) as c_int } @@ -44,7 +44,7 @@ extern "C" { pub static mut PyCoro_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCoro_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyCoro_Type) } @@ -54,7 +54,7 @@ extern "C" { pub static mut _PyCoroWrapper_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCoroWrapper_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut _PyCoroWrapper_Type) } @@ -66,13 +66,13 @@ extern "C" { } #[cfg(Py_3_6)] -#[inline(always)] +#[inline] pub unsafe fn PyAsyncGen_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyAsyncGen_Type) } #[cfg(not(Py_3_6))] -#[inline(always)] +#[inline] pub unsafe fn PyAsyncGen_Check(_op: *mut PyObject) -> c_int { 0 } diff --git a/src/ffi3/iterobject.rs b/src/ffi3/iterobject.rs index df110ffe..30bd9038 100644 --- a/src/ffi3/iterobject.rs +++ b/src/ffi3/iterobject.rs @@ -10,12 +10,12 @@ extern "C" { pub fn PyCallIter_New(arg1: *mut PyObject, arg2: *mut PyObject) -> *mut PyObject; } -#[inline(always)] +#[inline] pub unsafe fn PySeqIter_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PySeqIter_Type) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyCallIter_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCallIter_Type) as c_int } diff --git a/src/ffi3/listobject.rs b/src/ffi3/listobject.rs index fa526b8d..63c8efe2 100644 --- a/src/ffi3/listobject.rs +++ b/src/ffi3/listobject.rs @@ -9,12 +9,12 @@ extern "C" { pub static mut PyListRevIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyList_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LIST_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyList_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyList_Type) as c_int } diff --git a/src/ffi3/longobject.rs b/src/ffi3/longobject.rs index e4e7cb64..49aebc55 100644 --- a/src/ffi3/longobject.rs +++ b/src/ffi3/longobject.rs @@ -12,12 +12,12 @@ extern "C" { pub static mut PyLong_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyLong_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_LONG_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyLong_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyLong_Type) as c_int } diff --git a/src/ffi3/memoryobject.rs b/src/ffi3/memoryobject.rs index 6b839acf..90a7ccec 100644 --- a/src/ffi3/memoryobject.rs +++ b/src/ffi3/memoryobject.rs @@ -7,7 +7,7 @@ extern "C" { pub static mut PyMemoryView_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyMemoryView_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyMemoryView_Type) as c_int } diff --git a/src/ffi3/methodobject.rs b/src/ffi3/methodobject.rs index 33c4b118..b90f5232 100644 --- a/src/ffi3/methodobject.rs +++ b/src/ffi3/methodobject.rs @@ -7,7 +7,7 @@ extern "C" { pub static mut PyCFunction_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyCFunction_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyCFunction_Type) as c_int } @@ -63,7 +63,7 @@ impl Default for PyMethodDef { } } -#[inline(always)] +#[inline] pub unsafe fn PyCFunction_New(ml: *mut PyMethodDef, slf: *mut PyObject) -> *mut PyObject { PyCFunction_NewEx(ml, slf, ptr::null_mut()) } diff --git a/src/ffi3/moduleobject.rs b/src/ffi3/moduleobject.rs index becdb4ec..bd842026 100644 --- a/src/ffi3/moduleobject.rs +++ b/src/ffi3/moduleobject.rs @@ -8,12 +8,12 @@ extern "C" { pub static mut PyModule_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyModule_Check(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut PyModule_Type) } -#[inline(always)] +#[inline] pub unsafe fn PyModule_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyModule_Type) as c_int } diff --git a/src/ffi3/object.rs b/src/ffi3/object.rs index 78f10c66..a8c03487 100644 --- a/src/ffi3/object.rs +++ b/src/ffi3/object.rs @@ -34,7 +34,7 @@ pub struct PyVarObject { pub ob_size: Py_ssize_t, } -#[inline(always)] +#[inline] pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { if ob.is_null() { panic!(); @@ -42,12 +42,12 @@ pub unsafe fn Py_REFCNT(ob: *mut PyObject) -> Py_ssize_t { (*ob).ob_refcnt } -#[inline(always)] +#[inline] pub unsafe fn Py_TYPE(ob: *mut PyObject) -> *mut PyTypeObject { (*ob).ob_type } -#[inline(always)] +#[inline] pub unsafe fn Py_SIZE(ob: *mut PyObject) -> Py_ssize_t { (*(ob as *mut PyVarObject)).ob_size } @@ -616,7 +616,7 @@ extern "C" { pub fn PyType_IsSubtype(a: *mut PyTypeObject, b: *mut PyTypeObject) -> c_int; } -#[inline(always)] +#[inline] pub unsafe fn PyObject_TypeCheck(ob: *mut PyObject, tp: *mut PyTypeObject) -> c_int { (Py_TYPE(ob) == tp || PyType_IsSubtype(Py_TYPE(ob), tp) != 0) as c_int } @@ -633,12 +633,12 @@ extern "C" { pub fn PyType_GetFlags(arg1: *mut PyTypeObject) -> c_ulong; } -#[inline(always)] +#[inline] pub unsafe fn PyType_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TYPE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyType_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyType_Type) as c_int } @@ -753,19 +753,19 @@ pub const Py_TPFLAGS_DEFAULT: c_ulong = pub const Py_TPFLAGS_HAVE_FINALIZE: c_ulong = 1; -#[inline(always)] +#[inline] #[cfg(Py_LIMITED_API)] pub unsafe fn PyType_HasFeature(t: *mut PyTypeObject, f: c_ulong) -> c_int { ((PyType_GetFlags(t) & f) != 0) as c_int } -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyType_HasFeature(t: *mut PyTypeObject, f: c_ulong) -> c_int { (((*t).tp_flags & f) != 0) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyType_FastSubclass(t: *mut PyTypeObject, f: c_ulong) -> c_int { PyType_HasFeature(t, f) } @@ -776,7 +776,7 @@ extern "C" { } // Reference counting macros. -#[inline(always)] +#[inline] pub unsafe fn Py_INCREF(op: *mut PyObject) { if cfg!(py_sys_config = "Py_REF_DEBUG") { Py_IncRef(op) @@ -785,7 +785,7 @@ pub unsafe fn Py_INCREF(op: *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_DECREF(op: *mut PyObject) { if cfg!(py_sys_config = "Py_REF_DEBUG") { Py_DecRef(op) @@ -797,7 +797,7 @@ pub unsafe fn Py_DECREF(op: *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_CLEAR(op: &mut *mut PyObject) { let tmp = *op; if !tmp.is_null() { @@ -806,14 +806,14 @@ pub unsafe fn Py_CLEAR(op: &mut *mut PyObject) { } } -#[inline(always)] +#[inline] pub unsafe fn Py_XINCREF(op: *mut PyObject) { if !op.is_null() { Py_INCREF(op) } } -#[inline(always)] +#[inline] pub unsafe fn Py_XDECREF(op: *mut PyObject) { if !op.is_null() { Py_DECREF(op) @@ -829,12 +829,12 @@ extern "C" { static mut _Py_NotImplementedStruct: PyObject; } -#[inline(always)] +#[inline] pub unsafe fn Py_None() -> *mut PyObject { &mut _Py_NoneStruct } -#[inline(always)] +#[inline] pub unsafe fn Py_NotImplemented() -> *mut PyObject { &mut _Py_NotImplementedStruct } diff --git a/src/ffi3/objimpl.rs b/src/ffi3/objimpl.rs index 2b50f36e..ba22cc57 100644 --- a/src/ffi3/objimpl.rs +++ b/src/ffi3/objimpl.rs @@ -48,14 +48,14 @@ extern "C" { } /// Test if a type has a GC head -#[inline(always)] +#[inline] #[allow(unused_parens)] pub unsafe fn PyType_IS_GC(t: *mut PyTypeObject) -> c_int { PyType_HasFeature(t, Py_TPFLAGS_HAVE_GC) } /// Test if an object has a GC head -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyObject_IS_GC(o: *mut PyObject) -> c_int { (PyType_IS_GC(Py_TYPE(o)) != 0 && match (*Py_TYPE(o)).tp_is_gc { @@ -80,13 +80,13 @@ extern "C" { } /// Test if a type supports weak references -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyType_SUPPORTS_WEAKREFS(t: *mut PyTypeObject) -> c_int { ((*t).tp_weaklistoffset > 0) as c_int } -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyObject_GET_WEAKREFS_LISTPTR(o: *mut PyObject) -> *mut *mut PyObject { let weaklistoffset = (*Py_TYPE(o)).tp_weaklistoffset as isize; diff --git a/src/ffi3/pystate.rs b/src/ffi3/pystate.rs index c3322728..83357640 100644 --- a/src/ffi3/pystate.rs +++ b/src/ffi3/pystate.rs @@ -58,7 +58,7 @@ extern "C" { pub fn PyGILState_GetThisThreadState() -> *mut PyThreadState; } -#[inline(always)] +#[inline] pub unsafe fn PyThreadState_GET() -> *mut PyThreadState { PyThreadState_Get() } diff --git a/src/ffi3/rangeobject.rs b/src/ffi3/rangeobject.rs index 93388240..bdd8e339 100644 --- a/src/ffi3/rangeobject.rs +++ b/src/ffi3/rangeobject.rs @@ -8,7 +8,7 @@ extern "C" { pub static mut PyLongRangeIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyRange_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyRange_Type) as c_int } diff --git a/src/ffi3/sliceobject.rs b/src/ffi3/sliceobject.rs index cda1b1be..2a973c0f 100644 --- a/src/ffi3/sliceobject.rs +++ b/src/ffi3/sliceobject.rs @@ -7,7 +7,7 @@ extern "C" { static mut _Py_EllipsisObject: PyObject; } -#[inline(always)] +#[inline] pub unsafe fn Py_Ellipsis() -> *mut PyObject { &mut _Py_EllipsisObject } @@ -18,7 +18,7 @@ extern "C" { pub static mut PyEllipsis_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PySlice_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PySlice_Type) as c_int } diff --git a/src/ffi3/traceback.rs b/src/ffi3/traceback.rs index 211fe53a..096e9b1c 100644 --- a/src/ffi3/traceback.rs +++ b/src/ffi3/traceback.rs @@ -8,7 +8,7 @@ extern "C" { pub static mut PyTraceBack_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyTraceBack_Check(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyTraceBack_Type) as c_int } diff --git a/src/ffi3/tupleobject.rs b/src/ffi3/tupleobject.rs index 4751f53d..994f70e5 100644 --- a/src/ffi3/tupleobject.rs +++ b/src/ffi3/tupleobject.rs @@ -15,12 +15,12 @@ extern "C" { pub static mut PyTupleIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyTuple_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_TUPLE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyTuple_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyTuple_Type) as c_int } @@ -41,7 +41,7 @@ extern "C" { } // Macro, trading safety for speed -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyTuple_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObject { *(*(op as *mut PyTupleObject)) @@ -50,14 +50,14 @@ pub unsafe fn PyTuple_GET_ITEM(op: *mut PyObject, i: Py_ssize_t) -> *mut PyObjec .offset(i as isize) } -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyTuple_GET_SIZE(op: *mut PyObject) -> Py_ssize_t { Py_SIZE(op) } /// Macro, *only* to be used to fill in brand new tuples -#[inline(always)] +#[inline] #[cfg(not(Py_LIMITED_API))] pub unsafe fn PyTuple_SET_ITEM(op: *mut PyObject, i: Py_ssize_t, v: *mut PyObject) { *(*(op as *mut PyTupleObject)) diff --git a/src/ffi3/unicodeobject.rs b/src/ffi3/unicodeobject.rs index a227eae1..04893b29 100644 --- a/src/ffi3/unicodeobject.rs +++ b/src/ffi3/unicodeobject.rs @@ -16,12 +16,12 @@ extern "C" { pub static mut PyUnicodeIter_Type: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_Check(op: *mut PyObject) -> c_int { PyType_FastSubclass(Py_TYPE(op), Py_TPFLAGS_UNICODE_SUBCLASS) } -#[inline(always)] +#[inline] pub unsafe fn PyUnicode_CheckExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut PyUnicode_Type) as c_int } diff --git a/src/ffi3/weakrefobject.rs b/src/ffi3/weakrefobject.rs index 07198d6d..554bc930 100644 --- a/src/ffi3/weakrefobject.rs +++ b/src/ffi3/weakrefobject.rs @@ -10,23 +10,23 @@ extern "C" { static mut _PyWeakref_CallableProxyType: PyTypeObject; } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckRef(op: *mut PyObject) -> c_int { PyObject_TypeCheck(op, &mut _PyWeakref_RefType) } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckRefExact(op: *mut PyObject) -> c_int { (Py_TYPE(op) == &mut _PyWeakref_RefType) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_CheckProxy(op: *mut PyObject) -> c_int { ((Py_TYPE(op) == &mut _PyWeakref_ProxyType) || (Py_TYPE(op) == &mut _PyWeakref_CallableProxyType)) as c_int } -#[inline(always)] +#[inline] pub unsafe fn PyWeakref_Check(op: *mut PyObject) -> c_int { (PyWeakref_CheckRef(op) != 0 || PyWeakref_CheckProxy(op) != 0) as c_int } diff --git a/src/instance.rs b/src/instance.rs index b46458ec..17d27c6a 100644 --- a/src/instance.rs +++ b/src/instance.rs @@ -21,7 +21,7 @@ impl PyToken { PyToken(PhantomData) } - #[inline(always)] + #[inline] pub fn py(&self) -> Python { unsafe { Python::assume_gil_acquired() } diff --git a/src/objects/exc.rs b/src/objects/exc.rs index de8ddda1..866263fd 100644 --- a/src/objects/exc.rs +++ b/src/objects/exc.rs @@ -38,7 +38,7 @@ macro_rules! exc_type( } } impl PyTypeObject for $name { - #[inline(always)] + #[inline] fn init_type() {} #[inline] diff --git a/src/objects/exc_impl.rs b/src/objects/exc_impl.rs index 4846764e..c6eb50bf 100644 --- a/src/objects/exc_impl.rs +++ b/src/objects/exc_impl.rs @@ -69,7 +69,7 @@ macro_rules! import_exception { } impl $crate::typeob::PyTypeObject for $name { - #[inline(always)] + #[inline] fn init_type() {} #[inline] diff --git a/src/objects/mod.rs b/src/objects/mod.rs index 1181c7a6..9e644e00 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -68,7 +68,7 @@ macro_rules! pyobject_native_type_named( } impl<$($type_param,)*> $crate::PyObjectWithToken for $name { - #[inline(always)] + #[inline] fn py(&self) -> $crate::Python { unsafe { $crate::Python::assume_gil_acquired() } } @@ -129,7 +129,7 @@ macro_rules! pyobject_native_type_convert( } impl<$($type_param,)*> $crate::typeob::PyTypeObject for $name { - #[inline(always)] + #[inline] fn init_type() {} #[inline] diff --git a/src/typeob.rs b/src/typeob.rs index 88a1f1d4..df5fa3c4 100644 --- a/src/typeob.rs +++ b/src/typeob.rs @@ -205,7 +205,7 @@ impl IntoPyPointer for PyRawObject { } impl PyObjectWithToken for PyRawObject { - #[inline(always)] + #[inline] fn py(&self) -> Python { unsafe { Python::assume_gil_acquired() } } From a09ffad32a852c6d2ae9dcf1878dfefbf2241c58 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 17:19:18 -0400 Subject: [PATCH 41/44] Add convnience function to unwrap Option<&PyObject> --- src/objects/datetime.rs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs index ae1d6d9b..630fe42c 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -1,4 +1,5 @@ use err::PyResult; +use ffi; use ffi::PyDateTimeAPI; use ffi::{PyDateTime_Check, PyDateTime_DateTimeType}; use ffi::{ @@ -112,10 +113,7 @@ impl PyDateTime { minute as c_int, second as c_int, microsecond as c_int, - match tzinfo { - Some(o) => o.as_ptr(), - None => py.None().as_ptr(), - }, + opt_to_pyobj(py, tzinfo), PyDateTimeAPI.DateTimeType, ); Py::from_owned_ptr_or_err(py, ptr) @@ -194,10 +192,7 @@ impl PyTime { minute as c_int, second as c_int, microsecond as c_int, - match tzinfo { - Some(o) => o.as_ptr(), - None => py.None().as_ptr(), - }, + opt_to_pyobj(py, tzinfo), PyDateTimeAPI.TimeType, ); Py::from_owned_ptr_or_err(py, ptr) @@ -220,10 +215,7 @@ impl PyTime { minute as c_int, second as c_int, microsecond as c_int, - match tzinfo { - Some(o) => o.as_ptr(), - None => py.None().as_ptr(), - }, + opt_to_pyobj(py, tzinfo), fold as c_int, PyDateTimeAPI.TimeType, ); @@ -297,3 +289,12 @@ impl PyDeltaAccess for PyDelta { unsafe { PyDateTime_DELTA_GET_MICROSECONDS(self.as_ptr()) as i32 } } } + +// Utility function +unsafe fn opt_to_pyobj(py: Python, opt: Option<&PyObject>) -> *mut ffi::PyObject { + // Convenience function for unpacking Options to either an Object or None + match opt { + Some(tzi) => tzi.as_ptr(), + None => py.None().as_ptr(), + } +} From c7a967c34008388c4100d6043bc028514d48ec35 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 17:21:49 -0400 Subject: [PATCH 42/44] Silence type complexity warnings in ffi --- src/ffi/datetime.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ffi/datetime.rs b/src/ffi/datetime.rs index eb94902e..6cc01762 100644 --- a/src/ffi/datetime.rs +++ b/src/ffi/datetime.rs @@ -1,3 +1,5 @@ +#![cfg_attr(feature="cargo-clippy", allow(type_complexity))] + use ffi::PyCapsule_Import; use ffi::Py_hash_t; use ffi::{PyObject, PyTypeObject}; From c69634e0fd5572dc7a6938904d4b178df20d150b Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 17:25:25 -0400 Subject: [PATCH 43/44] Remove use_extern_macros, which is now stable --- examples/rustapi_module/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs index 8087851a..6c2ef34c 100644 --- a/examples/rustapi_module/src/lib.rs +++ b/examples/rustapi_module/src/lib.rs @@ -1,4 +1,4 @@ -#![feature(use_extern_macros, specialization)] +#![feature(specialization)] #[macro_use] extern crate pyo3; From 265a6802d724d0ad5212fedbc0a64c172204cea1 Mon Sep 17 00:00:00 2001 From: Paul Ganssle Date: Tue, 21 Aug 2018 18:22:08 -0400 Subject: [PATCH 44/44] Enable extension-module --- examples/rustapi_module/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/rustapi_module/Cargo.toml b/examples/rustapi_module/Cargo.toml index e3901ee3..abff4443 100644 --- a/examples/rustapi_module/Cargo.toml +++ b/examples/rustapi_module/Cargo.toml @@ -8,7 +8,7 @@ description = "A Python wrapper for the Rust API for purposes of testing" [dependencies.pyo3] path = "../../" -# features = ["extension-module"] +features = ["extension-module"] [lib] name = "rustapi_module"