Skip to content

Commit

Permalink
feat(python): add validate
Browse files Browse the repository at this point in the history
  • Loading branch information
gadomski committed Oct 10, 2024
1 parent 433362d commit 1e3928a
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/python.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ pip install cql2

::: cql2.Expr
::: cql2.SqlQuery
::: cql2.ValidationError
15 changes: 15 additions & 0 deletions python/cql2.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,18 @@ class Expr:
>>> expr = Expr({"op":"=","args":[{"property":"landsat:scene_id"},"LC82030282019133LGN00"]})
"""

def validate(self) -> None:
"""Validates this expression using json-schema.
Raises:
ValidationError: Raised if the validation fails
Examples:
>>> from cql2 import Expr
>>> expr = Expr("landsat:scene_id = 'LC82030282019133LGN00'")
>>> expr.validate()
"""

def to_json(self) -> dict[str, Any]:
"""Converts this cql2 expression to a cql2-json dictionary.
Expand Down Expand Up @@ -81,3 +93,6 @@ class Expr:
>>> q.params
['LC82030282019133LGN00']
"""

class ValidationError(Exception):
"""An error raised when cql2 json-schema validation fails."""
15 changes: 14 additions & 1 deletion python/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use pyo3::{
create_exception,
exceptions::{PyException, PyIOError, PyValueError},
prelude::*,
};
use std::path::PathBuf;

create_exception!(cql2, ValidationError, PyException);

/// Crate-specific error enum.
enum Error {
Cql2(::cql2::Error),
Expand Down Expand Up @@ -46,6 +49,15 @@ impl Expr {
}
}

fn validate(&self) -> PyResult<()> {
let validator = ::cql2::Validator::new().map_err(Error::from)?;
if let Err(error) = validator.validate(&self.0.to_value().map_err(Error::from)?) {
Err(ValidationError::new_err(error.to_string()))
} else {
Ok(())
}
}

/// Converts this expression to a cql2-json dictionary.
fn to_json<'py>(&self, py: Python<'py>) -> Result<Bound<'py, PyAny>> {
pythonize::pythonize(py, &self.0).map_err(Error::from)
Expand Down Expand Up @@ -104,8 +116,9 @@ impl From<pythonize::PythonizeError> for Error {

/// A Python module implemented in Rust.
#[pymodule]
fn cql2(m: &Bound<'_, PyModule>) -> PyResult<()> {
fn cql2(py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<Expr>()?;
m.add_class::<SqlQuery>()?;
m.add("ValidationError", py.get_type_bound::<ValidationError>())?;
Ok(())
}
14 changes: 13 additions & 1 deletion python/tests/test_expr.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pathlib import Path

from cql2 import Expr
import pytest
from cql2 import Expr, ValidationError


def test_from_path(fixtures: Path) -> None:
Expand All @@ -26,3 +27,14 @@ def test_to_sql(example01_text: str) -> None:
sql_query = Expr(example01_text).to_sql()
assert sql_query.query == '("landsat:scene_id" = $1)'
assert sql_query.params == ["LC82030282019133LGN00"]


def test_validate() -> None:
expr = Expr(
{
"op": "t_before",
"args": [{"property": "updated_at"}, {"timestamp": "invalid-timestamp"}],
}
)
with pytest.raises(ValidationError):
expr.validate()

0 comments on commit 1e3928a

Please sign in to comment.