Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix validation of Literal from JSON keys when used as dict key #1075

Merged
merged 2 commits into from Nov 16, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 12 additions & 3 deletions src/validators/literal.rs
Expand Up @@ -9,7 +9,7 @@ use pyo3::{intern, PyTraverseError, PyVisit};

use crate::build_tools::{py_schema_err, py_schema_error_type};
use crate::errors::{ErrorType, ValError, ValResult};
use crate::input::Input;
use crate::input::{Input, ValidationMatch};
use crate::py_gc::PyGcTraverse;
use crate::tools::SchemaDict;

Expand Down Expand Up @@ -116,8 +116,17 @@ impl<T: Debug> LiteralLookup<T> {
}
}
if let Some(expected_strings) = &self.expected_str {
// dbg!(expected_strings);
if let Ok(either_str) = input.exact_str() {
let validation_result = if input.is_python() {
input.exact_str()
} else {
// Strings coming from JSON are treated as "strict" but not "exact" for reasons
// of parsing types like UUID; see the implementation of `validate_str` for Json
// inputs for justification. We might change that eventually, but for now we need
// to work around this when loading from JSON
input.validate_str(true, false).map(ValidationMatch::into_inner)
sydney-runkle marked this conversation as resolved.
Show resolved Hide resolved
};

if let Ok(either_str) = validation_result {
let cow = either_str.as_cow()?;
if let Some(id) = expected_strings.get(cow.as_ref()) {
return Ok(Some((input, &self.values[*id])));
Expand Down
33 changes: 32 additions & 1 deletion tests/test.rs
@@ -1,6 +1,6 @@
#[cfg(test)]
mod tests {
use _pydantic_core::SchemaSerializer;
use _pydantic_core::{SchemaSerializer, SchemaValidator};
use pyo3::prelude::*;
use pyo3::types::PyDict;

Expand Down Expand Up @@ -86,4 +86,35 @@ a = A()
assert_eq!(serialized, b"{\"b\":\"b\"}");
});
}

#[test]
fn test_literal_schema() {
Python::with_gil(|py| {
let code = r#"
schema = {
"type": "dict",
"keys_schema": {
"type": "literal",
"expected": ["a", "b"],
},
"values_schema": {
"type": "str",
},
"strict": False,
}
json_input = '{"a": "something"}'
"#;
let locals = PyDict::new(py);
py.run(code, None, Some(locals)).unwrap();
let schema: &PyDict = locals.get_item("schema").unwrap().unwrap().extract().unwrap();
let json_input: &PyAny = locals.get_item("json_input").unwrap().unwrap().extract().unwrap();
let binding = SchemaValidator::py_new(py, schema, None)
.unwrap()
.validate_json(py, json_input, None, None, None)
.unwrap();
let validation_result: &PyAny = binding.extract(py).unwrap();
let repr = format!("{}", validation_result.repr().unwrap());
assert_eq!(repr, "{'a': 'something'}");
});
}
}