diff --git a/src/conversions/time.rs b/src/conversions/time.rs index 8d723982efd..33dca2d318b 100644 --- a/src/conversions/time.rs +++ b/src/conversions/time.rs @@ -48,12 +48,12 @@ use crate::sync::GILOnceCell; use crate::types::any::PyAnyMethods; #[cfg(not(Py_LIMITED_API))] use crate::types::datetime::timezone_from_offset; -use crate::types::PyNone; #[cfg(not(Py_LIMITED_API))] use crate::types::{ PyDate, PyDateAccess, PyDateTime, PyDelta, PyDeltaAccess, PyTime, PyTimeAccess, PyTzInfo, PyTzInfoAccess, }; +use crate::types::{PyInt, PyNone}; use crate::{ffi, Bound, FromPyObject, PyAny, PyErr, PyObject, PyResult, Python}; #[cfg(Py_LIMITED_API)] use crate::{intern, DowncastError}; @@ -154,6 +154,24 @@ impl FromPyObject<'_> for Month { } } +impl<'py> IntoPyObject<'py> for Month { + #[cfg(Py_LIMITED_API)] + type Target = PyInt; + #[cfg(not(Py_LIMITED_API))] + type Target = PyInt; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + (self as u8) + .into_pyobject(py) + .or_else(|_| Err(PyValueError::new_err("invalid month"))) + + // .map(|month| PyInt::::new(py, month.into())) + // .ok_or_else(|| PyValueError::new_err("invalid month")) + } +} + impl FromPyObject<'_> for Duration { fn extract_bound(ob: &Bound<'_, PyAny>) -> PyResult { // Python size are much lower than rust size so we do not need bound checks. @@ -852,733 +870,732 @@ fn timezone_utc(py: Python<'_>) -> Bound<'_, PyAny> { DatetimeTypes::get(py).timezone_utc.bind(py).clone() } -// #[cfg(test)] -// mod tests { -// use super::*; -// use crate::{types::PyTuple, BoundObject}; -// use std::{cmp::Ordering, panic}; - -// #[test] -// // Only Python>=3.9 has the zoneinfo package -// // We skip the test on windows too since we'd need to install -// // tzdata there to make this work. -// #[cfg(all(Py_3_9, not(target_os = "windows")))] -// fn test_zoneinfo_is_not_fixed_offset() { -// use crate::ffi; -// use crate::types::any::PyAnyMethods; -// use crate::types::dict::PyDictMethods; - -// Python::with_gil(|py| { -// let locals = crate::types::PyDict::new(py); -// py.run( -// ffi::c_str!("import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')"), -// None, -// Some(&locals), -// ) -// .unwrap(); -// let result: PyResult = locals.get_item("zi").unwrap().unwrap().extract(); -// assert!(result.is_err()); -// let res = result.err().unwrap(); -// // Also check the error message is what we expect -// let msg = res.value(py).repr().unwrap().to_string(); -// assert_eq!(msg, "TypeError(\"zoneinfo.ZoneInfo(key='Europe/London') is not a fixed offset timezone\")"); -// }); -// } - -// #[test] -// fn test_timezone_aware_to_naive_fails() { -// // Test that if a user tries to convert a python's timezone aware datetime into a naive -// // one, the conversion fails. -// Python::with_gil(|py| { -// let py_datetime = -// new_py_datetime_ob(py, "datetime", (2022, 1, 1, 1, 0, 0, 0, python_utc(py))); -// // Now test that converting a PyDateTime with tzinfo to a PrimitiveDateTime fails -// let res: PyResult = py_datetime.extract(); -// assert_eq!( -// res.unwrap_err().value(py).repr().unwrap().to_string(), -// "TypeError('expected a datetime without tzinfo')" -// ); -// }); -// } - -// #[test] -// fn test_naive_to_timezone_aware_fails() { -// // Test that if a user tries to convert a python's timezone aware datetime into a naive -// // one, the conversion fails. -// Python::with_gil(|py| { -// let py_datetime = new_py_datetime_ob(py, "datetime", (2022, 1, 1, 1, 0, 0, 0)); -// // Now test that converting a PyDateTime with tzinfo to a PrimitiveDateTime fails -// let res: PyResult> = py_datetime.extract(); -// assert_eq!( -// res.unwrap_err().value(py).repr().unwrap().to_string(), -// "TypeError('expected a datetime with non-None tzinfo')" -// ); - -// // Now test that converting a PyDateTime with tzinfo to a PrimitiveDateTime fails -// let res: PyResult> = py_datetime.extract(); -// assert_eq!( -// res.unwrap_err().value(py).repr().unwrap().to_string(), -// "TypeError('expected a datetime with non-None tzinfo')" -// ); -// }); -// } +#[cfg(test)] +mod tests_time { + use super::*; + use crate::{types::PyTuple, BoundObject}; + use std::{cmp::Ordering, panic}; + + #[test] + // Only Python>=3.9 has the zoneinfo package + // We skip the test on windows too since we'd need to install + // tzdata there to make this work. + #[cfg(all(Py_3_9, not(target_os = "windows")))] + fn test_zoneinfo_is_not_fixed_offset() { + use crate::ffi; + use crate::types::any::PyAnyMethods; + use crate::types::dict::PyDictMethods; + + Python::with_gil(|py| { + let locals = crate::types::PyDict::new(py); + py.run( + ffi::c_str!("import zoneinfo; zi = zoneinfo.ZoneInfo('Europe/London')"), + None, + Some(&locals), + ) + .unwrap(); + let result: PyResult = locals.get_item("zi").unwrap().unwrap().extract(); + assert!(result.is_err()); + let res = result.err().unwrap(); + // Also check the error message is what we expect + let msg = res.value(py).repr().unwrap().to_string(); + assert_eq!(msg, "TypeError(\"zoneinfo.ZoneInfo(key='Europe/London') is not a fixed offset timezone\")"); + }); + } -// #[test] -// fn test_invalid_types_fail() { -// // Test that if a user tries to convert a python's timezone aware datetime into a naive -// // one, the conversion fails. -// Python::with_gil(|py| { -// let none = py.None().into_bound(py); -// assert_eq!( -// none.extract::().unwrap_err().to_string(), -// "TypeError: 'NoneType' object cannot be converted to 'PyDelta'" -// ); -// assert_eq!( -// none.extract::().unwrap_err().to_string(), -// "TypeError: 'NoneType' object cannot be converted to 'PyTzInfo'" -// ); -// assert_eq!( -// none.extract::().unwrap_err().to_string(), -// "ValueError: expected datetime.timezone.utc" -// ); -// assert_eq!( -// none.extract::