pyo3/tests/test_datetime.rs

219 lines
6.2 KiB
Rust
Raw Normal View History

2018-08-09 18:17:34 +00:00
#![feature(concat_idents)]
use pyo3::ffi::*;
use pyo3::prelude::*;
2020-03-18 04:31:22 +00:00
use pyo3::types::IntoPyDict;
2018-08-09 18:17:34 +00:00
#[allow(clippy::trivially_copy_pass_by_ref)]
fn _get_subclasses<'p>(
py: &'p Python,
py_type: &str,
args: &str,
2019-03-04 04:50:43 +00:00
) -> PyResult<(&'p PyAny, &'p PyAny, &'p PyAny)> {
2018-08-09 18:17:34 +00:00
// Import the class from Python and create some subclasses
2018-08-20 19:11:54 +00:00
let datetime = py.import("datetime")?;
2018-08-09 18:17:34 +00:00
let locals = [(py_type, datetime.get(py_type)?)].into_py_dict(*py);
2018-08-09 18:17:34 +00:00
let make_subclass_py = format!("class Subklass({}):\n pass", py_type);
2018-08-09 18:17:34 +00:00
let make_sub_subclass_py = "class SubSubklass(Subklass):\n pass";
2018-08-09 18:17:34 +00:00
2018-08-20 19:11:54 +00:00
py.run(&make_subclass_py, None, Some(&locals))?;
py.run(&make_sub_subclass_py, None, Some(&locals))?;
2018-08-09 18:17:34 +00:00
// Construct an instance of the base class
2018-08-20 19:11:54 +00:00
let obj = py.eval(&format!("{}({})", py_type, args), None, Some(&locals))?;
2018-08-09 18:17:34 +00:00
// Construct an instance of the subclass
2018-08-20 19:11:54 +00:00
let sub_obj = py.eval(&format!("Subklass({})", args), None, Some(&locals))?;
2018-08-09 18:17:34 +00:00
// Construct an instance of the sub-subclass
2018-08-20 19:11:54 +00:00
let sub_sub_obj = py.eval(&format!("SubSubklass({})", args), None, Some(&locals))?;
2018-08-09 18:17:34 +00:00
2018-08-20 19:11:54 +00:00
Ok((obj, sub_obj, sub_sub_obj))
2018-08-09 18:17:34 +00:00
}
macro_rules! assert_check_exact {
($check_func:ident, $obj: expr) => {
unsafe {
2019-02-24 07:17:44 +00:00
use pyo3::AsPyPointer;
2018-08-09 18:17:34 +00:00
assert!($check_func(($obj).as_ptr()) != 0);
assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) != 0);
}
};
2018-08-09 18:17:34 +00:00
}
macro_rules! assert_check_only {
($check_func:ident, $obj: expr) => {
unsafe {
2019-02-24 07:17:44 +00:00
use pyo3::AsPyPointer;
2018-08-09 18:17:34 +00:00
assert!($check_func(($obj).as_ptr()) != 0);
assert!(concat_idents!($check_func, Exact)(($obj).as_ptr()) == 0);
}
};
2018-08-09 18:17:34 +00:00
}
// Because of the relase pool unsoundness reported in https://github.com/PyO3/pyo3/issues/756,
// we need to stop other threads before calling `py.import()`.
// TODO(kngwyu): Remove this variable
static MUTEX: parking_lot::Mutex<()> = parking_lot::const_mutex(());
2018-08-09 18:17:34 +00:00
#[test]
fn test_date_check() {
let _lock = MUTEX.lock();
2018-08-09 18:17:34 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
2018-08-20 19:11:54 +00:00
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "date", "2018, 1, 1").unwrap();
2018-08-09 18:17:34 +00:00
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 _lock = MUTEX.lock();
2018-08-09 18:17:34 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
2018-08-20 19:11:54 +00:00
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "time", "12, 30, 15").unwrap();
2018-08-09 18:17:34 +00:00
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 _lock = MUTEX.lock();
2018-08-09 18:17:34 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
2020-01-12 14:44:15 +00:00
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "datetime", "2018, 1, 1, 13, 30, 15")
.map_err(|e| e.print(py))
.unwrap();
2018-08-09 18:17:34 +00:00
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 _lock = MUTEX.lock();
2018-08-09 18:17:34 +00:00
let gil = Python::acquire_gil();
let py = gil.python();
2018-08-20 19:11:54 +00:00
let (obj, sub_obj, sub_sub_obj) = _get_subclasses(&py, "timedelta", "1, -3").unwrap();
2018-08-09 18:17:34 +00:00
assert_check_exact!(PyDelta_Check, obj);
assert_check_only!(PyDelta_Check, sub_obj);
assert_check_only!(PyDelta_Check, sub_sub_obj);
}
#[test]
fn test_datetime_utc() {
2019-08-17 12:10:36 +00:00
use assert_approx_eq::assert_approx_eq;
2019-02-23 17:42:40 +00:00
use pyo3::types::PyDateTime;
let _lock = MUTEX.lock();
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 = [("dt", dt)].into_py_dict(py);
let offset: f32 = py
.eval("dt.utcoffset().total_seconds()", None, Some(locals))
.unwrap()
.extract()
.unwrap();
2019-08-17 12:10:36 +00:00
assert_approx_eq!(offset, 0f32);
}
2019-08-17 12:10:36 +00:00
#[cfg(Py_3_6)]
static INVALID_DATES: &[(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),
];
2019-08-17 12:10:36 +00:00
#[cfg(Py_3_6)]
static INVALID_TIMES: &[(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() {
2019-02-23 17:42:40 +00:00
use pyo3::types::PyDate;
// 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 {
let (year, month, day) = val;
let dt = PyDate::new(py, *year, *month, *day);
2019-02-23 17:38:00 +00:00
dt.unwrap_err();
}
}
#[cfg(Py_3_6)]
#[test]
fn test_pytime_out_of_bounds() {
2019-02-23 17:42:40 +00:00
use pyo3::types::PyTime;
// This test is an XFAIL on Python < 3.6 until bounds checking is implemented
let gil = Python::acquire_gil();
let py = gil.python();
2019-02-23 17:38:00 +00:00
for val in INVALID_TIMES {
let (hour, minute, second, microsecond) = val;
let dt = PyTime::new(py, *hour, *minute, *second, *microsecond, None);
2019-02-23 17:38:00 +00:00
dt.unwrap_err();
}
}
#[cfg(Py_3_6)]
#[test]
fn test_pydatetime_out_of_bounds() {
2019-02-23 17:42:40 +00:00
use pyo3::types::PyDateTime;
use std::iter;
// 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);
2019-02-23 17:38:00 +00:00
let invalid_dates = INVALID_DATES.iter().zip(iter::repeat(&valid_time));
let invalid_times = iter::repeat(&valid_date).zip(INVALID_TIMES.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,
);
2019-02-23 17:38:00 +00:00
dt.unwrap_err();
}
}