Skip to content

Commit

Permalink
create unsafe bindings for datetime API
Browse files Browse the repository at this point in the history
  • Loading branch information
pascalkuthe committed Jan 25, 2022
1 parent 945a622 commit 96b4ef9
Show file tree
Hide file tree
Showing 4 changed files with 238 additions and 190 deletions.
139 changes: 136 additions & 3 deletions pyo3-ffi/src/datetime.rs
Expand Up @@ -9,10 +9,14 @@
//! Support for `PyDateTime_CAPI` is limited as of PyPy 7.0.0.
//! `DateTime_FromTimestamp` and `Date_FromTimestamp` are currently not supported.

#[cfg(not(PyPy))]
use crate::Py_hash_t;
use crate::{PyObject, PyTypeObject};
use crate::{PyObject, PyObject_TypeCheck, PyTypeObject, Py_TYPE};
use std::os::raw::{c_char, c_int, c_uchar};
use std::ptr;
#[cfg(not(PyPy))]
use {
crate::{PyCapsule_Import, Py_hash_t},
std::ffi::CString,
};

// Type struct wrappers
const _PyDateTime_DATE_DATASIZE: usize = 4;
Expand Down Expand Up @@ -426,3 +430,132 @@ pub struct PyDateTime_CAPI {

// Python already shares this object between threads, so it's no more evil for us to do it too!
unsafe impl Sync for PyDateTime_CAPI {}

pub static mut PyDateTimeAPI: *mut PyDateTime_CAPI = ptr::null_mut();

#[cfg(not(all(PyPy, not(Py_3_8))))]
pub unsafe fn PyDateTime_TimeZone_UTC() -> *mut PyObject {
(*PyDateTimeAPI).TimeZone_UTC
}

/// Populates the `PyDateTimeAPI` object
pub unsafe fn PyDateTime_IMPORT() {
PyDateTimeAPI = {
// PyPy expects the C-API to be initialized via PyDateTime_Import, so trying to use
// `PyCapsule_Import` will behave unexpectedly in pypy.
#[cfg(PyPy)]
let py_datetime_c_api = PyDateTime_Import();

#[cfg(not(PyPy))]
let py_datetime_c_api = {
// PyDateTime_CAPSULE_NAME is a macro in C
let PyDateTime_CAPSULE_NAME = CString::new("datetime.datetime_CAPI").unwrap();

PyCapsule_Import(PyDateTime_CAPSULE_NAME.as_ptr(), 1) as *mut PyDateTime_CAPI
};

py_datetime_c_api
};
}

// skipped non-limited PyDateTime_TimeZone_UTC

/// Type Check macros
///
/// These are bindings around the C API typecheck macros, all of them return
/// `1` if True and `0` if False. In all type check macros, the argument (`op`)
/// must not be `NULL`.
#[inline]
/// Check if `op` is a `PyDateTimeAPI.DateType` or subtype.
pub unsafe fn PyDate_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, (*PyDateTimeAPI).DateType) as c_int
}

#[inline]
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateType`.
pub unsafe fn PyDate_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == (*PyDateTimeAPI).DateType) as c_int
}

#[inline]
/// Check if `op` is a `PyDateTimeAPI.DateTimeType` or subtype.
pub unsafe fn PyDateTime_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, (*PyDateTimeAPI).DateTimeType) as c_int
}

#[inline]
/// Check if `op`'s type is exactly `PyDateTimeAPI.DateTimeType`.
pub unsafe fn PyDateTime_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == (*PyDateTimeAPI).DateTimeType) as c_int
}

#[inline]
/// Check if `op` is a `PyDateTimeAPI.TimeType` or subtype.
pub unsafe fn PyTime_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, (*PyDateTimeAPI).TimeType) as c_int
}

#[inline]
/// Check if `op`'s type is exactly `PyDateTimeAPI.TimeType`.
pub unsafe fn PyTime_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == (*PyDateTimeAPI).TimeType) as c_int
}

#[inline]
/// Check if `op` is a `PyDateTimeAPI.DetaType` or subtype.
pub unsafe fn PyDelta_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, (*PyDateTimeAPI).DeltaType) as c_int
}

#[inline]
/// Check if `op`'s type is exactly `PyDateTimeAPI.DeltaType`.
pub unsafe fn PyDelta_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == (*PyDateTimeAPI).DeltaType) as c_int
}

#[inline]
/// Check if `op` is a `PyDateTimeAPI.TZInfoType` or subtype.
pub unsafe fn PyTZInfo_Check(op: *mut PyObject) -> c_int {
PyObject_TypeCheck(op, (*PyDateTimeAPI).TZInfoType) as c_int
}

#[inline]
/// Check if `op`'s type is exactly `PyDateTimeAPI.TZInfoType`.
pub unsafe fn PyTZInfo_CheckExact(op: *mut PyObject) -> c_int {
(Py_TYPE(op) == (*PyDateTimeAPI).TZInfoType) as c_int
}

// skipped non-limited PyDate_FromDate
// skipped non-limited PyDateTime_FromDateAndTime
// skipped non-limited PyDateTime_FromDateAndTimeAndFold
// skipped non-limited PyTime_FromTime
// skipped non-limited PyTime_FromTimeAndFold
// skipped non-limited PyDelta_FromDSU
// skipped non-limited PyTimeZone_FromOffset
// skipped non-limited PyTimeZone_FromOffsetAndName

#[cfg(not(PyPy))]
pub unsafe fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
let f = (*PyDateTimeAPI).DateTime_FromTimestamp;
f((*PyDateTimeAPI).DateTimeType, args, std::ptr::null_mut())
}

#[cfg(not(PyPy))]
pub unsafe fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject {
let f = (*PyDateTimeAPI).Date_FromTimestamp;
f((*PyDateTimeAPI).DateType, args)
}

#[cfg(PyPy)]
extern "C" {
#[link_name = "PyPyDate_FromTimestamp"]
pub fn PyDate_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
#[link_name = "PyPyDateTime_FromTimestamp"]
pub fn PyDateTime_FromTimestamp(args: *mut PyObject) -> *mut PyObject;
}

#[cfg(PyPy)]
extern "C" {
#[link_name = "_PyPyDateTime_Import"]
pub fn PyDateTime_Import() -> *mut PyDateTime_CAPI;
}
34 changes: 16 additions & 18 deletions pyo3-ffi/src/lib.rs
Expand Up @@ -85,9 +85,9 @@
//! ```rust
//! use std::intrinsics::transmute;
//! use std::os::raw::c_char;
//!
//!
//! use pyo3_ffi::*;
//!
//!
//! #[allow(non_snake_case)]
//! #[no_mangle]
//! pub unsafe extern "C" fn PyInit_string_sum() -> *mut PyObject {
Expand All @@ -102,22 +102,22 @@
//! m_clear: None,
//! m_free: None,
//! };
//!
//!
//! let mptr = PyModule_Create(Box::into_raw(Box::new(init)));
//! let version = env!("CARGO_PKG_VERSION");
//! PyModule_AddObject(
//! mptr,
//! "__version__\0".as_ptr() as *const c_char,
//! PyUnicode_FromStringAndSize(version.as_ptr() as *const c_char, version.len() as isize),
//! );
//!
//!
//! let wrapped_sum_as_string = PyMethodDef {
//! ml_name: "sum_as_string\0".as_ptr() as *const c_char,
//! ml_meth: Some(transmute::<_PyCFunctionFast, PyCFunction>(sum_as_string)),
//! ml_flags: METH_FASTCALL,
//! ml_doc: "returns the sum of two integers as a string".as_ptr() as *const c_char,
//! };
//!
//!
//! PyModule_AddObject(
//! mptr,
//! "sum_as_string\0".as_ptr() as *const c_char,
Expand All @@ -127,9 +127,9 @@
//! PyUnicode_InternFromString("string_sum\0".as_ptr() as *const c_char),
//! ),
//! );
//!
//!
//! let all = ["__all__\0", "__version__\0", "sum_as_string\0"];
//!
//!
//! let pyall = PyTuple_New(all.len() as isize);
//! for (i, obj) in all.iter().enumerate() {
//! PyTuple_SET_ITEM(
Expand All @@ -138,12 +138,12 @@
//! PyUnicode_InternFromString(obj.as_ptr() as *const c_char),
//! )
//! }
//!
//!
//! PyModule_AddObject(mptr, "__all__\0".as_ptr() as *const c_char, pyall);
//!
//!
//! mptr
//! }
//!
//!
//! pub unsafe extern "C" fn sum_as_string(
//! _self: *mut PyObject,
//! args: *mut *mut PyObject,
Expand All @@ -152,24 +152,24 @@
//! if nargs != 2 {
//! return raise_type_error("sum_as_string() expected 2 positional arguments");
//! }
//!
//!
//! let arg1 = *args;
//! if PyLong_Check(arg1) == 0 {
//! return raise_type_error("sum_as_string() expected an int for positional argument 1");
//! }
//! let arg1 = PyLong_AsLong(arg1);
//!
//!
//! let arg2 = *args.add(1);
//! if PyLong_Check(arg2) == 0 {
//! return raise_type_error("sum_as_string() expected an int for positional argument 2");
//! }
//!
//!
//! let arg2 = PyLong_AsLong(arg2);
//!
//!
//! let res = (arg1 + arg2).to_string();
//! PyUnicode_FromStringAndSize(res.as_ptr() as *const c_char, res.len() as isize)
//! }
//!
//!
//! #[cold]
//! #[inline(never)]
//! fn raise_type_error(msg: &str) -> *mut PyObject {
Expand Down Expand Up @@ -217,7 +217,7 @@
//! ```bash
//! sudo apt install python3-dev
//! ```
//!
//!
//! While most projects use the save wrapper provided by pyo3,
//! you can take a look at the [`orjson`] as an example on how to use ffi directly.
//! For those well versed in C and Rust the [tutorials] from the cpython documentation
Expand All @@ -234,8 +234,6 @@
//! [PEP 384]: https://www.python.org/dev/peps/pep-0384 "PEP 384 -- Defining a Stable ABI"
//! [Features chapter of the guide]: https://pyo3.rs/latest/features.html#features-reference "Features Reference - PyO3 user guide"



#![allow(
missing_docs,
non_camel_case_types,
Expand Down

0 comments on commit 96b4ef9

Please sign in to comment.