Skip to content

Commit 7b3904e

Browse files
committed
Coerce date and datetime
1 parent b23925e commit 7b3904e

File tree

4 files changed

+38
-8
lines changed

4 files changed

+38
-8
lines changed

src/validators/date.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
use pyo3::exceptions::PyValueError;
12
use pyo3::intern;
23
use pyo3::prelude::*;
3-
use pyo3::types::{PyDate, PyDict, PyString};
4+
use pyo3::types::{PyDict, PyString};
45
use speedate::{Date, Time};
56
use strum::EnumMessage;
67

@@ -175,9 +176,14 @@ impl DateConstraints {
175176
}
176177
}
177178

178-
fn convert_pydate(schema: &Bound<'_, PyDict>, field: &Bound<'_, PyString>) -> PyResult<Option<Date>> {
179-
match schema.get_as::<Bound<'_, PyDate>>(field)? {
180-
Some(date) => Ok(Some(EitherDate::Py(date).as_raw()?)),
179+
fn convert_pydate(schema: &Bound<'_, PyDict>, key: &Bound<'_, PyString>) -> PyResult<Option<Date>> {
180+
match schema.get_as::<Bound<'_, PyAny>>(key)? {
181+
Some(value) => match value.validate_date(false) {
182+
Ok(v) => Ok(Some(v.into_inner().as_raw()?)),
183+
Err(_) => Err(PyValueError::new_err(format!(
184+
"'{key}' must be coercible to a date instance",
185+
))),
186+
},
181187
None => Ok(None),
182188
}
183189
}

src/validators/datetime.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1+
use pyo3::exceptions::PyValueError;
12
use pyo3::intern;
23
use pyo3::prelude::*;
34
use pyo3::sync::GILOnceCell;
45
use pyo3::types::{PyDict, PyString};
5-
use speedate::{DateTime, Time};
6+
use speedate::{DateTime, MicrosecondsPrecisionOverflowBehavior, Time};
67
use std::cmp::Ordering;
78
use strum::EnumMessage;
89

@@ -208,9 +209,14 @@ impl DateTimeConstraints {
208209
}
209210
}
210211

211-
fn py_datetime_as_datetime(schema: &Bound<'_, PyDict>, field: &Bound<'_, PyString>) -> PyResult<Option<DateTime>> {
212-
match schema.get_as(field)? {
213-
Some(dt) => Ok(Some(EitherDateTime::Py(dt).as_raw()?)),
212+
fn py_datetime_as_datetime(schema: &Bound<'_, PyDict>, key: &Bound<'_, PyString>) -> PyResult<Option<DateTime>> {
213+
match schema.get_as::<Bound<'_, PyAny>>(key)? {
214+
Some(value) => match value.validate_datetime(false, MicrosecondsPrecisionOverflowBehavior::Truncate) {
215+
Ok(v) => Ok(Some(v.into_inner().as_raw()?)),
216+
Err(_) => Err(PyValueError::new_err(format!(
217+
"'{key}' must be coercible to a datetime instance",
218+
))),
219+
},
214220
None => Ok(None),
215221
}
216222
}

tests/validators/test_date.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,15 @@
1313
from ..conftest import Err, PyAndJson
1414

1515

16+
@pytest.mark.parametrize(
17+
'constraint',
18+
['le', 'lt', 'ge', 'gt'],
19+
)
20+
def test_constraints_schema_validation(constraint: str) -> None:
21+
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a date instance"):
22+
SchemaValidator(cs.date_schema(**{constraint: 'bad_value'}))
23+
24+
1625
@pytest.mark.parametrize(
1726
'input_value,expected',
1827
[

tests/validators/test_datetime.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,15 @@
1414
from ..conftest import Err, PyAndJson
1515

1616

17+
@pytest.mark.parametrize(
18+
'constraint',
19+
['le', 'lt', 'ge', 'gt'],
20+
)
21+
def test_constraints_schema_validation(constraint: str) -> None:
22+
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a datetime instance"):
23+
SchemaValidator(cs.datetime_schema(**{constraint: 'bad_value'}))
24+
25+
1726
@pytest.mark.parametrize(
1827
'input_value,expected',
1928
[

0 commit comments

Comments
 (0)