Skip to content

Commit cee3654

Browse files
committed
handle None values
1 parent d87b779 commit cee3654

File tree

6 files changed

+53
-10
lines changed

6 files changed

+53
-10
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "cel"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
edition = "2021"
55

66
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,13 @@ evaluate("is_adult(age)", context)
7070
```
7171

7272

73+
## Testing
74+
75+
```shell
76+
uv run pytest --log-cli-level=debug
77+
```
78+
79+
7380
## Future work
7481

7582

pyproject.toml

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
[build-system]
2-
requires = ["maturin>=1.5,<2.0"]
3-
build-backend = "maturin"
41

52
[project]
63
name = "common-expression-language"
@@ -14,11 +11,17 @@ dynamic = ["version"]
1411
dependencies = [
1512
]
1613

14+
[build-system]
15+
requires = ["maturin>=1.5,<2.0"]
16+
build-backend = "maturin"
17+
18+
1719
[tool.maturin]
1820
features = ["pyo3/extension-module"]
1921

20-
[dependency-groups]
21-
dev = [
22+
[tool.uv]
23+
dev-dependencies = [
2224
"pytest>=8.3.3",
2325
"maturin>=1.7.4",
26+
"pip>=24.3.1",
2427
]

src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ impl Context {
2525
for (k, v) in variables {
2626
let key = k
2727
.extract::<String>()
28-
.map_err(|_| PyValueError::new_err("Keys must be strings"));
28+
.map_err(|_| PyValueError::new_err("Variable name must be strings"));
2929
key.map(|key| context.add_variable(key, v))??;
3030
}
3131
};

src/lib.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use pyo3::exceptions::PyValueError;
77
use pyo3::prelude::*;
88

99
use chrono::{DateTime, Duration as ChronoDuration, Offset, TimeZone, Utc};
10-
use pyo3::types::{PyBytes, PyDateTime, PyDict, PyList, PyTuple};
10+
use pyo3::types::{PyBytes, PyDateTime, PyDict, PyList, PyNone, PyTuple};
1111
use pyo3::types::{PyDelta, PyFunction};
1212
use std::time::{Duration, SystemTime, UNIX_EPOCH};
1313

@@ -98,7 +98,9 @@ impl TryIntoValue for RustyPyType<'_> {
9898
fn try_into_value(self) -> Result<Value, Self::Error> {
9999
let val = match self {
100100
RustyPyType(pyobject) => {
101-
if let Ok(value) = pyobject.extract::<bool>() {
101+
if pyobject.is_none() {
102+
Ok(Value::Null)
103+
} else if let Ok(value) = pyobject.extract::<bool>() {
102104
Ok(Value::Bool(value))
103105
} else if let Ok(value) = pyobject.extract::<i64>() {
104106
Ok(Value::Int(value))
@@ -140,7 +142,11 @@ impl TryIntoValue for RustyPyType<'_> {
140142
} else if let Ok(value) = pyobject.downcast::<PyDict>() {
141143
let mut map: HashMap<Key, Value> = HashMap::new();
142144
for (key, value) in value.into_iter() {
143-
let key = if let Ok(k) = key.extract::<i64>() {
145+
let key = if key.is_none() {
146+
return Err(CelError::ConversionError(
147+
"None cannot be used as a key in dictionaries".to_string(),
148+
));
149+
} else if let Ok(k) = key.extract::<i64>() {
144150
Key::Int(k)
145151
} else if let Ok(k) = key.extract::<u64>() {
146152
Key::Uint(k)

tests/test_context.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ def test_context_vars_implicit():
1616
context = cel.Context({'a': 10})
1717
assert cel.evaluate("a", context) == 10
1818

19+
def test_context_vars_none_value():
20+
context = cel.Context({'a': None})
21+
assert cel.evaluate("a", context) == None
22+
1923

2024
def test_adding_to_context():
2125
context = cel.Context()
@@ -73,3 +77,26 @@ def custom_function(a, b):
7377
'b': 2,
7478
})
7579
assert cel.evaluate("custom_function(a, b)", context) == 42
80+
81+
def test_nested_context_none():
82+
83+
context = {
84+
'spec': {
85+
'type': 'dns',
86+
'nameserver': None,
87+
'host': 'github.com',
88+
'timeout': 30.0,
89+
'pattern': "\ndata['response-code'] == 'NOERROR' &&\nsize(data['A']) >= 1 && \n(timestamp(data["
90+
},
91+
'data': {
92+
'canonical_name': 'github.com.',
93+
'expiration': 1732097106.7902246,
94+
'response': 'id 25\nopcode QUERY\nrcode NOERROR\nflags QR RD RA\nedns 0\npayload 65494\n;QUESTION\ng',
95+
'A': ['4.237.22.38'],
96+
'response-code': 'NOERROR',
97+
'startTimestamp': '2024-11-20T10:04:59.789017+00:00',
98+
'endTimestamp': '2024-11-20T10:04:59.790298+00:00'
99+
}
100+
}
101+
102+
context = cel.Context(variables=context)

0 commit comments

Comments
 (0)