Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/validators/date.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use crate::build_tools::{is_strict, py_schema_error_type};
use crate::errors::{ErrorType, ErrorTypeDefaults, ValError, ValResult};
use crate::input::{EitherDate, Input};

use crate::tools::SchemaDict;
use crate::validators::datetime::{NowConstraint, NowOp};

use super::Exactness;
Expand Down Expand Up @@ -177,7 +176,7 @@ impl DateConstraints {
}

fn convert_pydate(schema: &Bound<'_, PyDict>, key: &Bound<'_, PyString>) -> PyResult<Option<Date>> {
match schema.get_as::<Bound<'_, PyAny>>(key)? {
match schema.get_item(key)? {
Some(value) => match value.validate_date(false) {
Ok(v) => Ok(Some(v.into_inner().as_raw()?)),
Err(_) => Err(PyValueError::new_err(format!(
Expand Down
2 changes: 1 addition & 1 deletion src/validators/datetime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl DateTimeConstraints {
}

fn py_datetime_as_datetime(schema: &Bound<'_, PyDict>, key: &Bound<'_, PyString>) -> PyResult<Option<DateTime>> {
match schema.get_as::<Bound<'_, PyAny>>(key)? {
match schema.get_item(key)? {
Some(value) => match value.validate_datetime(false, MicrosecondsPrecisionOverflowBehavior::Truncate) {
Ok(v) => Ok(Some(v.into_inner().as_raw()?)),
Err(_) => Err(PyValueError::new_err(format!(
Expand Down
4 changes: 2 additions & 2 deletions src/validators/decimal.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use pyo3::exceptions::{PyTypeError, PyValueError};
use pyo3::intern;
use pyo3::sync::GILOnceCell;
use pyo3::types::{IntoPyDict, PyDict, PyString, PyTuple, PyType};
use pyo3::types::{IntoPyDict, PyDict, PyTuple, PyType};
use pyo3::{prelude::*, PyTypeInfo};

use crate::build_tools::{is_strict, schema_or_config_same};
Expand Down Expand Up @@ -29,7 +29,7 @@ pub fn get_decimal_type(py: Python) -> &Bound<'_, PyType> {
}

fn validate_as_decimal(py: Python, schema: &Bound<'_, PyDict>, key: &str) -> PyResult<Option<Py<PyAny>>> {
match schema.get_as::<Bound<'_, PyAny>>(&PyString::new(py, key))? {
match schema.get_item(key)? {
Some(value) => match value.validate_decimal(false, py) {
Ok(v) => Ok(Some(v.into_inner().unbind())),
Err(_) => Err(PyValueError::new_err(format!(
Expand Down
18 changes: 8 additions & 10 deletions src/validators/int.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ use num_bigint::BigInt;
use pyo3::exceptions::PyValueError;
use pyo3::intern;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyString};
use pyo3::types::PyDict;
use pyo3::IntoPyObjectExt;

use crate::build_tools::is_strict;
use crate::errors::{ErrorType, ValError, ValResult};
use crate::input::{Input, Int};
use crate::tools::SchemaDict;

use super::{BuildValidator, CombinedValidator, DefinitionsBuilder, ValidationState, Validator};

fn validate_as_int(py: Python, schema: &Bound<'_, PyDict>, key: &str) -> PyResult<Option<Int>> {
match schema.get_as::<Bound<'_, PyAny>>(&PyString::new(py, key))? {
fn validate_as_int(schema: &Bound<'_, PyDict>, key: &str) -> PyResult<Option<Int>> {
match schema.get_item(key)? {
Some(value) => match value.validate_int(false) {
Ok(v) => match v.into_inner().as_int() {
Ok(v) => Ok(Some(v)),
Expand Down Expand Up @@ -90,14 +89,13 @@ pub struct ConstrainedIntValidator {

impl ConstrainedIntValidator {
fn build(schema: &Bound<'_, PyDict>, config: Option<&Bound<'_, PyDict>>) -> PyResult<CombinedValidator> {
let py = schema.py();
Ok(Self {
strict: is_strict(schema, config)?,
multiple_of: validate_as_int(py, schema, "multiple_of")?,
le: validate_as_int(py, schema, "le")?,
lt: validate_as_int(py, schema, "lt")?,
ge: validate_as_int(py, schema, "ge")?,
gt: validate_as_int(py, schema, "gt")?,
multiple_of: validate_as_int(schema, "multiple_of")?,
le: validate_as_int(schema, "le")?,
lt: validate_as_int(schema, "lt")?,
ge: validate_as_int(schema, "ge")?,
gt: validate_as_int(schema, "gt")?,
}
.into())
}
Expand Down
15 changes: 9 additions & 6 deletions src/validators/timedelta.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyDelta, PyDeltaAccess, PyDict};
use speedate::Duration;
use speedate::{Duration, MicrosecondsPrecisionOverflowBehavior};

use crate::build_tools::is_strict;
use crate::errors::{ErrorType, ValError, ValResult};
use crate::input::{duration_as_pytimedelta, EitherTimedelta, Input};
use crate::input::{duration_as_pytimedelta, Input};

use super::datetime::extract_microseconds_precision;
use super::{BuildValidator, CombinedValidator, DefinitionsBuilder, ValidationState, Validator};
Expand All @@ -26,10 +27,12 @@ struct TimedeltaConstraints {

fn get_constraint(schema: &Bound<'_, PyDict>, key: &str) -> PyResult<Option<Duration>> {
match schema.get_item(key)? {
Some(value) => {
let either_timedelta = EitherTimedelta::try_from(&value)?;
Ok(Some(either_timedelta.to_duration()?))
}
Some(value) => match value.validate_timedelta(false, MicrosecondsPrecisionOverflowBehavior::default()) {
Ok(v) => Ok(Some(v.into_inner().to_duration()?)),
Err(_) => Err(PyValueError::new_err(format!(
"'{key}' must be coercible to a timedelta instance"
))),
},
None => Ok(None),
}
}
Expand Down
8 changes: 7 additions & 1 deletion tests/validators/test_date.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
'constraint',
['le', 'lt', 'ge', 'gt'],
)
def test_constraints_schema_validation(constraint: str) -> None:
def test_constraints_schema_validation_error(constraint: str) -> None:
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a date instance"):
SchemaValidator(cs.date_schema(**{constraint: 'bad_value'}))


def test_constraints_schema_validation() -> None:
val = SchemaValidator(cs.date_schema(gt='2020-01-01'))
with pytest.raises(ValidationError):
val.validate_python('2019-01-01')


@pytest.mark.parametrize(
'input_value,expected',
[
Expand Down
8 changes: 7 additions & 1 deletion tests/validators/test_datetime.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@
'constraint',
['le', 'lt', 'ge', 'gt'],
)
def test_constraints_schema_validation(constraint: str) -> None:
def test_constraints_schema_validation_error(constraint: str) -> None:
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a datetime instance"):
SchemaValidator(cs.datetime_schema(**{constraint: 'bad_value'}))


def test_constraints_schema_validation() -> None:
val = SchemaValidator(cs.datetime_schema(gt='2020-01-01T00:00:00'))
with pytest.raises(ValidationError):
val.validate_python('2019-01-01T00:00:00')


@pytest.mark.parametrize(
'input_value,expected',
[
Expand Down
8 changes: 7 additions & 1 deletion tests/validators/test_decimal.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,17 @@ class DecimalSubclass(Decimal):
'constraint',
['multiple_of', 'le', 'lt', 'ge', 'gt'],
)
def test_constraints_schema_validation(constraint: str) -> None:
def test_constraints_schema_validation_error(constraint: str) -> None:
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a Decimal instance"):
SchemaValidator(cs.decimal_schema(**{constraint: 'bad_value'}))


def test_constraints_schema_validation() -> None:
val = SchemaValidator(cs.decimal_schema(gt='1'))
with pytest.raises(ValidationError):
val.validate_python('0')


@pytest.mark.parametrize(
'input_value,expected',
[
Expand Down
8 changes: 7 additions & 1 deletion tests/validators/test_int.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,17 @@
'constraint',
['multiple_of', 'le', 'lt', 'ge', 'gt'],
)
def test_constraints_schema_validation(constraint: str) -> None:
def test_constraints_schema_validation_error(constraint: str) -> None:
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to an integer"):
SchemaValidator(cs.int_schema(**{constraint: 'bad_value'}))


def test_constraints_schema_validation() -> None:
val = SchemaValidator(cs.int_schema(gt='1'))
with pytest.raises(ValidationError):
val.validate_python('0')


@pytest.mark.parametrize(
'input_value,expected',
[
Expand Down
15 changes: 15 additions & 0 deletions tests/validators/test_timedelta.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,21 @@
pandas = None


@pytest.mark.parametrize(
'constraint',
['le', 'lt', 'ge', 'gt'],
)
def test_constraints_schema_validation_error(constraint: str) -> None:
with pytest.raises(SchemaError, match=f"'{constraint}' must be coercible to a timedelta instance"):
SchemaValidator(core_schema.timedelta_schema(**{constraint: 'bad_value'}))


def test_constraints_schema_validation() -> None:
val = SchemaValidator(core_schema.timedelta_schema(gt=3))
with pytest.raises(ValidationError):
val.validate_python(1)


@pytest.mark.parametrize(
'input_value,expected',
[
Expand Down
Loading