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..cd4b9c5f351 100755 --- a/ci/travis/test.sh +++ b/ci/travis/test.sh @@ -7,7 +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 diff --git a/examples/rustapi_module/.gitignore b/examples/rustapi_module/.gitignore new file mode 100644 index 00000000000..7a6ac170309 --- /dev/null +++ b/examples/rustapi_module/.gitignore @@ -0,0 +1,6 @@ +.pytest_cache +.mypy_cache +.hypothesis +.tox + +*.py[co] diff --git a/examples/rustapi_module/Cargo.toml b/examples/rustapi_module/Cargo.toml new file mode 100644 index 00000000000..abff444343b --- /dev/null +++ b/examples/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/examples/rustapi_module/pyproject.toml b/examples/rustapi_module/pyproject.toml new file mode 100644 index 00000000000..0f58585acf6 --- /dev/null +++ b/examples/rustapi_module/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires = ["setuptools", "wheel", "setuptools_rust>=0.10.2"] diff --git a/examples/rustapi_module/requirements-dev.txt b/examples/rustapi_module/requirements-dev.txt new file mode 100644 index 00000000000..b1000dbbb17 --- /dev/null +++ b/examples/rustapi_module/requirements-dev.txt @@ -0,0 +1,3 @@ +hypothesis>=3.55 +pytest>=3.5.0 +setuptools-rust>=0.10.2 diff --git a/examples/rustapi_module/rustapi_module/__init__.py b/examples/rustapi_module/rustapi_module/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/examples/rustapi_module/setup.py b/examples/rustapi_module/setup.py new file mode 100644 index 00000000000..0efaae03f9a --- /dev/null +++ b/examples/rustapi_module/setup.py @@ -0,0 +1,56 @@ +import sys + +from setuptools import setup +from setuptools.command.test import test as TestCommand +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) + + +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 + +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', + rustc_flags=get_py_version_cfgs())], + install_requires=install_requires, + tests_require=tests_require, + include_package_data=True, + zip_safe=False, + cmdclass=dict(test=PyTest) +) diff --git a/examples/rustapi_module/src/lib.rs b/examples/rustapi_module/src/lib.rs new file mode 100644 index 00000000000..6c2ef34c619 --- /dev/null +++ b/examples/rustapi_module/src/lib.rs @@ -0,0 +1,217 @@ +#![feature(specialization)] + +#[macro_use] +extern crate pyo3; + +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::{PyDateAccess, PyTimeAccess}; +use pyo3::prelude::{PyDict, PyTuple}; +use pyo3::{ObjectProtocol, ToPyObject}; +use pyo3::{Py, PyResult, Python}; + +#[pyfunction] +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() as i32, d.get_day() as i32], + ) +} + +#[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)) +} + +#[pyfunction] +fn make_time( + py: Python, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, +) -> PyResult> { + PyTime::new( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(|o| o.to_object(py)).as_ref(), + ) +} + +#[cfg(Py_3_6)] +#[pyfunction] +fn time_with_fold( + py: Python, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, + fold: bool, +) -> PyResult> { + PyTime::new_with_fold( + py, + hour, + minute, + second, + microsecond, + tzinfo.map(|o| o.to_object(py)).as_ref(), + fold, + ) +} + +#[pyfunction] +fn get_time_tuple(py: Python, dt: &PyTime) -> Py { + PyTuple::new( + py, + &[ + dt.get_hour() as u32, + dt.get_minute() as u32, + dt.get_second() as u32, + dt.get_microsecond(), + ], + ) +} + +#[cfg(Py_3_6)] +#[pyfunction] +fn get_time_tuple_fold(py: Python, dt: &PyTime) -> Py { + PyTuple::new( + py, + &[ + dt.get_hour() as u32, + dt.get_minute() as u32, + dt.get_second() as u32, + 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) +} + +#[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: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyTzInfo>, +) -> PyResult> { + PyDateTime::new( + py, + year, + month, + day, + hour, + minute, + second, + microsecond, + tzinfo.map(|o| (o.to_object(py))).as_ref(), + ) +} + +#[pyfunction] +fn get_datetime_tuple(py: Python, dt: &PyDateTime) -> Py { + PyTuple::new( + py, + &[ + dt.get_year(), + 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, + ], + ) +} + +#[cfg(Py_3_6)] +#[pyfunction] +fn get_datetime_tuple_fold(py: Python, dt: &PyDateTime) -> Py { + PyTuple::new( + py, + &[ + dt.get_year(), + 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, + ], + ) +} + +#[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(), + }; + + 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!(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))?; + 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_time_tuple_fold)); + m.add_function(wrap_function!(get_datetime_tuple_fold)); + } + + Ok(()) +} diff --git a/examples/rustapi_module/tests/test_datetime.py b/examples/rustapi_module/tests/test_datetime.py new file mode 100644 index 00000000000..ccfa6ae281e --- /dev/null +++ b/examples/rustapi_module/tests/test_datetime.py @@ -0,0 +1,263 @@ +import rustapi_module.datetime as rdt + +import sys +import datetime as pdt + +import pytest + +from hypothesis import given +from hypothesis import strategies as st +from hypothesis.strategies import dates, datetimes + +# Constants +def _get_utc(): + timezone = getattr(pdt, 'timezone', None) + if timezone: + return timezone.utc + else: + class UTC(pdt.tzinfo): + def utcoffset(self, dt): + return pdt.timedelta(0) + + def dst(self, dt): + return pdt.timedelta(0) + + def tzname(self, dt): + return "UTC" + + return UTC() + +UTC = _get_utc() + +MAX_SECONDS = int(pdt.timedelta.max.total_seconds()) +MIN_SECONDS = int(pdt.timedelta.min.total_seconds()) + +try: + MAX_DAYS = pdt.timedelta.max // pdt.timedelta(days=1) + MIN_DAYS = pdt.timedelta.min // pdt.timedelta(days=1) +except Exception: + # Python 2 compatibility + MAX_DAYS = MAX_SECONDS // pdt.timedelta(days=1).total_seconds() + MIN_DAYS = MIN_SECONDS // pdt.timedelta(days=1).total_seconds() + +MAX_MICROSECONDS = int(pdt.timedelta.max.total_seconds() * 1e6) +MIN_MICROSECONDS = int(pdt.timedelta.min.total_seconds() * 1e6) + +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(): + 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 + + +@xfail_date_bounds +def test_invalid_date_fails(): + with pytest.raises(ValueError): + 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(int(ts)) == pdt.date.fromtimestamp(ts) + + +@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 + + +@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): + 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), + (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) + + +@xfail_date_bounds +@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': 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 + + +@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=1) + dt_nofold = dt.replace(fold=0) + + 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 + + +@xfail_date_bounds +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) + + +@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), + (-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 + + +@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), + ((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) + diff --git a/examples/rustapi_module/tox.ini b/examples/rustapi_module/tox.ini new file mode 100644 index 00000000000..11e866b5d0d --- /dev/null +++ b/examples/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 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 new file mode 100644 index 00000000000..6cc01762d97 --- /dev/null +++ b/src/ffi/datetime.rs @@ -0,0 +1,410 @@ +#![cfg_attr(feature="cargo-clippy", allow(type_complexity))] + +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}; +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, + #[cfg(Py_3_7)] + pub TimeZone_UTC: *mut PyObject, + + 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, + #[cfg(Py_3_7)] + pub TimeZone_FromTimeZone: + 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: + unsafe extern "C" fn(cls: *mut PyTypeObject, args: *mut PyObject) -> *mut PyObject, + #[cfg(Py_3_6)] + 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: 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 + +const _PyDateTime_DATE_DATASIZE: usize = 4; +const _PyDateTime_TIME_DATASIZE: usize = 6; +const _PyDateTime_DATETIME_DATASIZE: usize = 10; + +#[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_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 { + 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 { + 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 { + if !PY_DATETIME_API_UNSAFE_CACHE.is_null() { + &(*PY_DATETIME_API_UNSAFE_CACHE) + } else { + PyDateTime_IMPORT() + } + } + } +} + +#[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(); + + 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) +} + +// +// Type Check macros +// +#[inline] +pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DateType) as c_int +} + +#[inline] +pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateType) as c_int +} + +#[inline] +pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DateTimeType) as c_int +} + +#[inline] +pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DateTimeType) as c_int +} + +#[inline] +pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TimeType) as c_int +} + +#[inline] +pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.TimeType) as c_int +} + +#[inline] +pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.DeltaType) as c_int +} + +#[inline] +pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int { + (Py_TYPE(op) == PyDateTimeAPI.DeltaType) as c_int +} + +#[inline] +pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int { + PyObject_TypeCheck(op, PyDateTimeAPI.TZInfoType) as c_int +} + +#[inline] +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) => { + c_int::from((*$o).data[$offset + 0]) + }; +} + +macro_rules! _PyDateTime_GET_MINUTE { + ($o: expr, $offset:expr) => { + c_int::from((*$o).data[$offset + 1]) + }; +} + +macro_rules! _PyDateTime_GET_SECOND { + ($o: expr, $offset:expr) => { + c_int::from((*$o).data[$offset + 2]) + }; +} + +macro_rules! _PyDateTime_GET_MICROSECOND { + ($o: expr, $offset:expr) => { + (c_int::from((*$o).data[$offset + 3]) << 16) + | (c_int::from((*$o).data[$offset + 4]) << 8) + | (c_int::from((*$o).data[$offset + 5])) + }; +} + +#[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] +pub unsafe fn PyDateTime_DATE_GET_HOUR(o: *mut PyObject) -> c_int { + _PyDateTime_GET_HOUR!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[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] +pub unsafe fn PyDateTime_DATE_GET_SECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_SECOND!((o as *mut PyDateTime_DateTime), _PyDateTime_DATE_DATASIZE) +} + +#[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] +pub unsafe fn PyDateTime_DATE_GET_FOLD(o: *mut PyObject) -> c_uchar { + _PyDateTime_GET_FOLD!(o as *mut PyDateTime_DateTime) +} + +#[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] +pub unsafe fn PyDateTime_TIME_GET_HOUR(o: *mut PyObject) -> c_int { + _PyDateTime_GET_HOUR!((o as *mut PyDateTime_Time), 0) +} + +#[inline] +pub unsafe fn PyDateTime_TIME_GET_MINUTE(o: *mut PyObject) -> c_int { + _PyDateTime_GET_MINUTE!((o as *mut PyDateTime_Time), 0) +} + +#[inline] +pub unsafe fn PyDateTime_TIME_GET_SECOND(o: *mut PyObject) -> c_int { + _PyDateTime_GET_SECOND!((o as *mut PyDateTime_Time), 0) +} + +#[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] +pub unsafe fn PyDateTime_TIME_GET_FOLD(o: *mut PyObject) -> c_uchar { + _PyDateTime_GET_FOLD!(o as *mut PyDateTime_Time) +} + +#[inline] +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] +pub unsafe fn PyDateTime_DELTA_GET_DAYS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, days) +} + +#[inline] +pub unsafe fn PyDateTime_DELTA_GET_SECONDS(o: *mut PyObject) -> c_int { + _access_delta_field!(o, seconds) +} + +#[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 new file mode 100644 index 00000000000..346857f7369 --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,11 @@ +#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)] + +#[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/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 f12d4cb9fac..c9f88079bb4 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, @@ -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/lib.rs b/src/lib.rs index 0107453650f..8835e2e72b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -129,21 +129,15 @@ extern crate spin; #[doc(hidden)] pub extern crate mashup; +/// Rust FFI declarations for Python +pub mod ffi; + #[cfg(not(Py_3))] 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, diff --git a/src/objects/datetime.rs b/src/objects/datetime.rs new file mode 100644 index 00000000000..630fe42ca0e --- /dev/null +++ b/src/objects/datetime.rs @@ -0,0 +1,300 @@ +use err::PyResult; +use ffi; +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_DateType, PyDate_Check}; +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, +}; +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 instance::Py; +use python::{Python, ToPyPointer}; + +// Traits +pub trait PyDateAccess { + fn get_year(&self) -> i32; + fn get_month(&self) -> u8; + fn get_day(&self) -> u8; +} + +pub trait PyDeltaAccess { + fn get_days(&self) -> i32; + fn get_seconds(&self) -> i32; + fn get_microseconds(&self) -> i32; +} + +pub trait PyTimeAccess { + 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; +} + +// datetime.date bindings +pub struct PyDate(PyObject); +pyobject_native_type!(PyDate, PyDateTime_DateType, PyDate_Check); + +impl PyDate { + pub fn new(py: Python, year: i32, month: u8, day: u8) -> PyResult> { + unsafe { + let ptr = (PyDateTimeAPI.Date_FromDate)( + year as c_int, + month as c_int, + day as c_int, + PyDateTimeAPI.DateType, + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } + + pub fn from_timestamp(py: Python, args: &PyObject) -> PyResult> { + unsafe { + let ptr = (PyDateTimeAPI.Date_FromTimestamp)(PyDateTimeAPI.DateType, args.as_ptr()); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + +impl PyDateAccess for PyDate { + fn get_year(&self) -> i32 { + unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 } + } + + fn get_month(&self) -> u8 { + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } + } + + fn get_day(&self) -> u8 { + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } + } +} + +// datetime.datetime bindings +pub struct PyDateTime(PyObject); +pyobject_native_type!(PyDateTime, PyDateTime_DateTimeType, PyDateTime_Check); + +impl PyDateTime { + pub fn new( + py: Python, + year: i32, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyObject>, + ) -> PyResult> { + unsafe { + let ptr = (PyDateTimeAPI.DateTime_FromDateAndTime)( + 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, + opt_to_pyobj(py, tzinfo), + PyDateTimeAPI.DateTimeType, + ); + 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)( + PyDateTimeAPI.DateTimeType, + args.as_ptr(), + kwargs.as_ptr(), + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + +impl PyDateAccess for PyDateTime { + fn get_year(&self) -> i32 { + unsafe { PyDateTime_GET_YEAR(self.as_ptr()) as i32 } + } + + fn get_month(&self) -> u8 { + unsafe { PyDateTime_GET_MONTH(self.as_ptr()) as u8 } + } + + fn get_day(&self) -> u8 { + unsafe { PyDateTime_GET_DAY(self.as_ptr()) as u8 } + } +} + +impl PyTimeAccess for PyDateTime { + fn get_hour(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_HOUR(self.as_ptr()) as u8 } + } + + fn get_minute(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_MINUTE(self.as_ptr()) as u8 } + } + + fn get_second(&self) -> u8 { + unsafe { PyDateTime_DATE_GET_SECOND(self.as_ptr()) as u8 } + } + + 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); + +impl PyTime { + pub fn new( + py: Python, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyObject>, + ) -> PyResult> { + unsafe { + let ptr = (PyDateTimeAPI.Time_FromTime)( + hour as c_int, + minute as c_int, + second as c_int, + microsecond as c_int, + opt_to_pyobj(py, tzinfo), + PyDateTimeAPI.TimeType, + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } + + #[cfg(Py_3_6)] + pub fn new_with_fold( + py: Python, + hour: u8, + minute: u8, + second: u8, + microsecond: u32, + tzinfo: Option<&PyObject>, + fold: bool, + ) -> PyResult> { + unsafe { + let ptr = (PyDateTimeAPI.Time_FromTimeAndFold)( + hour as c_int, + minute as c_int, + second as c_int, + microsecond as c_int, + opt_to_pyobj(py, tzinfo), + fold as c_int, + PyDateTimeAPI.TimeType, + ); + Py::from_owned_ptr_or_err(py, ptr) + } + } +} + +impl PyTimeAccess for PyTime { + fn get_hour(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_HOUR(self.as_ptr()) as u8 } + } + + fn get_minute(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_MINUTE(self.as_ptr()) as u8 } + } + + fn get_second(&self) -> u8 { + unsafe { PyDateTime_TIME_GET_SECOND(self.as_ptr()) as u8 } + } + + 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); + +// 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> { + unsafe { + let ptr = (PyDateTimeAPI.Delta_FromDelta)( + 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) + } + } +} + +impl PyDeltaAccess 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 } + } +} + +// 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(), + } +} 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 2a4a1a13dc6..9e644e007c1 100644 --- a/src/objects/mod.rs +++ b/src/objects/mod.rs @@ -5,6 +5,9 @@ mod exc_impl; pub use self::boolobject::PyBool; pub use self::bytearray::PyByteArray; +pub use self::datetime::PyDeltaAccess; +pub use self::datetime::{PyDate, PyDateTime, PyDelta, PyTime, PyTzInfo}; +pub use self::datetime::{PyDateAccess, PyTimeAccess}; pub use self::dict::PyDict; pub use self::floatob::PyFloat; pub use self::iterator::PyIterator; @@ -65,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() } } @@ -126,7 +129,7 @@ macro_rules! pyobject_native_type_convert( } impl<$($type_param,)*> $crate::typeob::PyTypeObject for $name { - #[inline(always)] + #[inline] fn init_type() {} #[inline] @@ -185,6 +188,7 @@ pyobject_native_type_convert!(PyObjectRef, ffi::PyBaseObject_Type, ffi::PyObject mod boolobject; mod bytearray; +mod datetime; mod dict; pub mod exc; mod floatob; 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() } } diff --git a/tests/test_datetime.rs b/tests/test_datetime.rs new file mode 100644 index 00000000000..61eb7b5f7ab --- /dev/null +++ b/tests/test_datetime.rs @@ -0,0 +1,211 @@ +#![feature(concat_idents)] + +extern crate pyo3; + +use std::iter; + +use pyo3::ffi::*; +use pyo3::prelude::*; + +fn _get_subclasses<'p>( + py: &'p Python, + py_type: &str, + args: &str, +) -> PyResult<(&'p PyObjectRef, &'p PyObjectRef, &'p PyObjectRef)> { + // Import the class from Python and create some subclasses + let datetime = py.import("datetime")?; + + let locals = PyDict::new(*py); + 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"; + + 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 = py.eval(&format!("{}({})", py_type, args), None, Some(&locals))?; + + // Construct an instance of the subclass + let sub_obj = py.eval(&format!("Subklass({})", args), None, Some(&locals))?; + + // Construct an instance of the sub-subclass + let sub_sub_obj = py.eval(&format!("SubSubklass({})", args), None, Some(&locals))?; + + Ok((obj, sub_obj, sub_sub_obj)) +} + +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); + } + }; +} + +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] +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").unwrap(); + + assert_check_exact!(PyDate_Check, obj); + assert_check_only!(PyDate_Check, sub_obj); + assert_check_only!(PyDate_Check, sub_sub_obj); +} + +#[test] +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").unwrap(); + + assert_check_exact!(PyTime_Check, obj); + assert_check_only!(PyTime_Check, sub_obj); + assert_check_only!(PyTime_Check, sub_sub_obj); +} + +#[test] +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").unwrap(); + + 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] +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").unwrap(); + + assert_check_exact!(PyDelta_Check, obj); + assert_check_only!(PyDelta_Check, sub_obj); + 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); +} + +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] +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(); + 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); + 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(); + 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); + 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 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, + *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), + } + } +}