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 00000000000..831aa924f9d --- /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 8c6b68dd0d9..c68d6cfcb64 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 c6cd5141abb..f2167f7c56d 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 0107453650f..842ab0e88ff 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 00000000000..ec0e166c7e6 --- /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 2a4a1a13dc6..62b098d62cd 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 00000000000..e3901ee3979 --- /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 00000000000..e69de29bb2d diff --git a/tests/rustapi_module/setup.py b/tests/rustapi_module/setup.py new file mode 100644 index 00000000000..e0a8230a533 --- /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 00000000000..4781f13a681 --- /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 00000000000..313beb3818c --- /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 831aa924f9d..6d85c8c182f 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 ec0e166c7e6..724c796c01e 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 62b098d62cd..36c6ff1d2b1 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 4781f13a681..862ab59857d 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 313beb3818c..cc01331c875 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 6d85c8c182f..2d4e2150590 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 724c796c01e..c740914bdb1 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 36c6ff1d2b1..8520880f5ab 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 862ab59857d..2a8994cfb9c 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 cc01331c875..5b922679b16 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,6 +4,8 @@ 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 2d4e2150590..fc7c6a4de49 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 c740914bdb1..594659d4da6 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 8520880f5ab..65539649e22 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 2a8994cfb9c..13ace97814f 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 5b922679b16..5f9968c8f1a 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,7 +4,14 @@ 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 00000000000..270740dd205 --- /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 fc7c6a4de49..106be3989f9 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 594659d4da6..b594dcf238a 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 13ace97814f..20d31aa045e 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 5f9968c8f1a..b48ce14bc16 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -4,6 +4,9 @@ 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 @@ 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 b594dcf238a..0e8821f88e2 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 20d31aa045e..ca51bab1db9 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 b48ce14bc16..5774ef4118b 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -5,7 +5,7 @@ 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 @@ # 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 0e8821f88e2..e3147b800b1 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 ca51bab1db9..5d5f4dae06c 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 5774ef4118b..3bb7b4fd06c 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -16,6 +16,8 @@ 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 e3147b800b1..ee30e486260 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 | 124 ++++++++++++++++++-------------- 2 files changed, 72 insertions(+), 53 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index f2167f7c56d..e4cc38ab3a2 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 5d5f4dae06c..9d9e9b321ca 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); + + 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) +} - let args = PyTuple::new(py, &[timestamp, tzi]); - let kwargs = PyDict::new(py); - 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))?; + 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 00000000000..7a6ac170309 --- /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 00000000000..0f58585acf6 --- /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 270740dd205..b1000dbbb17 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 e0a8230a533..e55d8d61835 100644 --- a/tests/rustapi_module/setup.py +++ b/tests/rustapi_module/setup.py @@ -2,17 +2,7 @@ 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 @@ def run(self): 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 @@ def run(self): '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 00000000000..11e866b5d0d --- /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 106be3989f9..95ee36ce544 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 ee30e486260..6f340b28328 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 65539649e22..18d6109e2af 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 9d9e9b321ca..edcaf4dbb52 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 3bb7b4fd06c..76fed521803 100644 --- a/tests/rustapi_module/tests/test_datetime.py +++ b/tests/rustapi_module/tests/test_datetime.py @@ -5,6 +5,7 @@ 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 95ee36ce544..af82c50a59a 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 6f340b28328..9d755e0f2c0 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 18d6109e2af..810989ac326 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 edcaf4dbb52..a8267660c39 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 76fed521803..6678f4a892d 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 af82c50a59a..18dad30352a 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 9d755e0f2c0..55fdf9b85f2 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 810989ac326..199785d10c9 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 a8267660c39..c43a1fba3c9 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 6678f4a892d..252b356dcfb 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 18dad30352a..f09ea3da039 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 55fdf9b85f2..d30b482f89f 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 c43a1fba3c9..5ccff192a62 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 252b356dcfb..285ecb1f1bf 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 f09ea3da039..f90e0347788 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 285ecb1f1bf..93764f31c8a 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 00000000000..deadb49e358 --- /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 00000000000..02e16f9955e --- /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 29a160a73ef..3a30b67d1e1 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 93764f31c8a..ccfa6ae281e 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,28 +10,52 @@ 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) -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 _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) -HAS_FOLD = getattr(pdt.datetime, 'fold', False) + def dst(self, dt): + return pdt.timedelta(0) + def tzname(self, dt): + return "UTC" -# Helper functions -def get_timestamp(dt): - return dt.timestamp() + 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) +HAS_FOLD = getattr(pdt.datetime, 'fold', False) +# Helper functions +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 deadb49e358..0fffb396c86 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 08a8ed2507b..008d4c88007 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 095ad69b779..4f69b17a176 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 | 216 ++++++++++++++++---------------- src/objects/mod.rs | 4 +- tests/rustapi_module/src/lib.rs | 149 ++++++++++++++++------ tests/test_datetime.rs | 53 ++++---- 8 files changed, 303 insertions(+), 266 deletions(-) diff --git a/src/ffi2/datetime.rs b/src/ffi2/datetime.rs index 02e16f9955e..bda33ab2247 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 f90e0347788..0182b0d1f35 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 c68d6cfcb64..9b4161119d5 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 842ab0e88ff..bf9b8038296 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 d30b482f89f..90bc0429ebb 100644 --- a/src/objects/datetime.rs +++ b/src/objects/datetime.rs @@ -1,25 +1,33 @@ use err::PyResult; -use object::PyObject; -use std::os::raw::c_int; -use ffi::{PyDateTimeAPI}; +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_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_Date_GET_DAY, PyDateTime_Date_GET_MONTH, PyDateTime_Date_GET_YEAR}; 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_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; #[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 199785d10c9..291053499fa 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 5ccff192a62..44becbd7dda 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 0fffb396c86..2908905f376 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 bda33ab2247..a9c0850cbda 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) - }; -} - -#[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 - }; +// 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_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] +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_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 0182b0d1f35..aa297db110e 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) - }; -} - -#[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 - }; +// 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_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] +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_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 90bc0429ebb..a0a197ea267 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 | 61 +++++++++++++++++++++++++++++++++++++++----- src/ffi3/datetime.rs | 61 +++++++++++++++++++++++++++++++++++++++----- 3 files changed, 108 insertions(+), 15 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index e4cc38ab3a2..672aa210d1a 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 a9c0850cbda..28c3e336223 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 {} - -lazy_static! { - pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +// 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() -> 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 aa297db110e..f480a9cea4a 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 {} - -lazy_static! { - pub static ref PyDateTimeAPI: PyDateTime_CAPI = unsafe { PyDateTime_IMPORT() }; +// 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() -> 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 a0a197ea267..a30e4313f76 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 2908905f376..fe517311d6c 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 a30e4313f76..c48490e5f7a 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 44becbd7dda..a69e2ddeab2 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 fe517311d6c..b0eed461675 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 28c3e336223..69d32307ebd 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 f480a9cea4a..8aa814d33a3 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 c48490e5f7a..d742ff83f5b 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 b0eed461675..5f3f0071b30 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 d742ff83f5b..17e48f280ac 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 291053499fa..1181c7a6b46 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 a69e2ddeab2..2182ddc57ee 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 8aa814d33a3..0ab4927e986 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 00000000000..72bd6c3c094 --- /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 69d32307ebd..00000000000 --- 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 3a30b67d1e1..29a160a73ef 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 9b4161119d5..8c6b68dd0d9 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 bf9b8038296..8835e2e72b3 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 0ab4927e986..476f2a6edf1 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 f12d4cb9fac..be4fd28e54c 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 476f2a6edf1..ca525cb3b9f 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 672aa210d1a..c6cd5141abb 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 4f69b17a176..cd4b9c5f351 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 e55d8d61835..0efaae03f9a 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 @@ def get_py_version_cfgs(): 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 | 72 ++++++++++++-------- 3 files changed, 113 insertions(+), 97 deletions(-) diff --git a/examples/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs index 2182ddc57ee..054289bb5cd 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 17e48f280ac..1539b748a25 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 5f3f0071b30..dff2ba2bdfb 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), - ]; - - for val in vals.into_iter() { - let (year, month, day, hour, minute, second, microsecond) = val; + let valid_time = (0, 0, 0, 0); + let valid_date = (2018, 1, 1); + + 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 054289bb5cd..8087851adda 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 1539b748a25..ae1d6d9becc 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 dff2ba2bdfb..61eb7b5f7ab 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 da09a50930d..cab748fc197 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 8023f108eef..8012ac8a014 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 ca525cb3b9f..eb94902e1f7 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 72bd6c3c094..346857f7369 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 0248736f5e0..2cb7553fc03 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 7af2feecbf5..7cc5a96bfb3 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 5dcd64b21f8..a86203b2027 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 486f0bae914..a74c3298c01 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 cf4132bcbeb..9238437144b 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 8c1ab3cde91..60f7d55a3cc 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 80c5be196f2..baea6be3101 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 48a9b032842..f650c4de28b 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 5c92cca527e..c2a6c3ee5a6 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 ec043bdce34..036b05c6510 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 591d87e60c1..cfe881f3d2e 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 efc96e20f65..a07c65e6816 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 36ed08d1153..ce0c1b44a5e 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 43c3c197b39..82004d7b82e 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 254611328f4..921f8d37893 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 7f39f445723..ee9e927a0df 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 6600aeb798c..eefa4569060 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 8dd4a234aaa..c2cf0a4262b 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 80dddcf46f9..667e0ac8b47 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 64912efa3a0..973849ff650 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 29a160a73ef..14b6a75dad9 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 ba66fead5ae..b915d4ca7dc 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 52546b185de..fc6c3959434 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 be4fd28e54c..c9f88079bb4 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 e9342989551..ff98af60981 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 eb2d5320d37..f17807b8dcc 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 4674aac8435..7153a802530 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 8fa6c2647d5..c3594da2f7d 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 80fc742d92e..ca69720f322 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 346e5771964..2f98da2b635 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 eed43b313d6..5b8eff7e26e 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 0bb57d2a822..bbd7add1972 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 abf00b61e9f..a1bd3e3c2e7 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 6949067cac3..0c669b2f864 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 40799b408fe..1e5455286d6 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 ca7569d0ebf..4d1469e103a 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 b003e666f53..8b3ff0f5d5d 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 20d58d1a4eb..dc4c16d8174 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 6c3bdb46122..f1b5439e664 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 e1dad5aef44..ff22fea0d3e 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 df110ffe4ab..30bd9038912 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 fa526b8d018..63c8efe2893 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 e4e7cb64b0b..49aebc559ae 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 6b839acfa5c..90a7ccece44 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 33c4b1185c6..b90f52329d2 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 becdb4ecda0..bd842026767 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 78f10c66685..a8c0348783c 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 2b50f36ec31..ba22cc5790f 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 c33227280bb..83357640134 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 93388240150..bdd8e3396a9 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 cda1b1be1e6..2a973c0f4f0 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 211fe53aa3a..096e9b1c8be 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 4751f53de0a..994f70e552e 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 a227eae10c1..04893b298ba 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 07198d6dd6d..554bc9302cf 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 b46458ecdaa..17d27c6a2cc 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 de8ddda16df..866263fd057 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 4846764ea90..c6eb50bf83d 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 1181c7a6b46..9e644e007c1 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 88a1f1d47fd..df5fa3c49c3 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 ae1d6d9becc..630fe42ca0e 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 eb94902e1f7..6cc01762d97 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 8087851adda..6c2ef34c619 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 e3901ee3979..abff444343b 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"