diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bd41b4c2de..53d9d87add3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Default to "m" ABI tag when choosing `libpython` link name for CPython 3.7 on Unix. [#2288](https://github.com/PyO3/pyo3/pull/2288) +### Fixed + +- Fix segfault when calling FFI methods `PyDateTime_DATE_GET_TZINFO` or `PyDateTime_TIME_GET_TZINFO` on `datetime` or `time` without a tzinfo. [#2289](https://github.com/PyO3/pyo3/pull/2289) + ## [0.16.3] - 2022-04-05 ### Packaging diff --git a/pyo3-ffi/src/datetime.rs b/pyo3-ffi/src/datetime.rs index 5e064ea2011..2b1194c56de 100644 --- a/pyo3-ffi/src/datetime.rs +++ b/pyo3-ffi/src/datetime.rs @@ -155,7 +155,11 @@ macro_rules! _PyDateTime_GET_FOLD { #[cfg(not(PyPy))] macro_rules! _PyDateTime_GET_TZINFO { ($o: expr) => { - (*$o).tzinfo + if (*$o).hastzinfo != 0 { + (*$o).tzinfo + } else { + $crate::Py_None() + } }; } diff --git a/src/ffi/tests.rs b/src/ffi/tests.rs index aa5235210c2..0231b0ffcd7 100644 --- a/src/ffi/tests.rs +++ b/src/ffi/tests.rs @@ -180,3 +180,44 @@ fn ucs4() { } }) } + +#[test] +#[cfg(not(PyPy))] +fn test_get_tzinfo() { + crate::Python::with_gil(|py| { + use crate::types::{PyDateTime, PyTime}; + use crate::{AsPyPointer, PyAny, ToPyObject}; + + let datetime = py.import("datetime").map_err(|e| e.print(py)).unwrap(); + let timezone = datetime.getattr("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(); + + assert!( + unsafe { py.from_borrowed_ptr::(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) } + .is(&utc) + ); + + let dt = PyDateTime::new(py, 2018, 1, 1, 0, 0, 0, 0, None).unwrap(); + + assert!( + unsafe { py.from_borrowed_ptr::(PyDateTime_DATE_GET_TZINFO(dt.as_ptr())) } + .is_none() + ); + + let t = PyTime::new(py, 0, 0, 0, 0, Some(&utc)).unwrap(); + + assert!( + unsafe { py.from_borrowed_ptr::(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) } + .is(&utc) + ); + + let t = PyTime::new(py, 0, 0, 0, 0, None).unwrap(); + + assert!( + unsafe { py.from_borrowed_ptr::(PyDateTime_TIME_GET_TZINFO(t.as_ptr())) } + .is_none() + ); + }) +}