diff --git a/Cargo.lock b/Cargo.lock index b74074c8d..667d15b1b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -148,32 +148,19 @@ checksum = "62b02a5381cc465bd3041d84623d0fa3b66738b52b8e2fc3bab8ad63ab032f4a" [[package]] name = "jiter" -version = "0.0.6" +version = "0.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87db066a99f69382be06d02313f8ce989996b53a04a8a70cfd1a6483a56227f7" +checksum = "c2a1b6e316923afd3087ec73829f646a67c18f3a5bd61624247b05e652e4a99d" dependencies = [ "ahash", "hashbrown", - "lexical-core", + "lexical-parse-float", "num-bigint", "num-traits", "pyo3", "smallvec", ] -[[package]] -name = "lexical-core" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" -dependencies = [ - "lexical-parse-float", - "lexical-parse-integer", - "lexical-util", - "lexical-write-float", - "lexical-write-integer", -] - [[package]] name = "lexical-parse-float" version = "0.8.5" @@ -204,27 +191,6 @@ dependencies = [ "static_assertions", ] -[[package]] -name = "lexical-write-float" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" -dependencies = [ - "lexical-util", - "lexical-write-integer", - "static_assertions", -] - -[[package]] -name = "lexical-write-integer" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" -dependencies = [ - "lexical-util", - "static_assertions", -] - [[package]] name = "libc" version = "0.2.147" @@ -363,9 +329,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.20.3" +version = "0.21.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233" +checksum = "5d0c41d899f822e5f39186d6da130a822a0a43edb19992b51bf4ef6cd0b4cfd1" dependencies = [ "cfg-if", "indoc", @@ -382,9 +348,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.20.3" +version = "0.21.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7" +checksum = "5509c2aa78c7e770077e41ba86f806e60dcee812e924ccb2d6fe78c0a0128ce2" dependencies = [ "once_cell", "python3-dll-a", @@ -393,9 +359,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.20.3" +version = "0.21.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" +checksum = "e6bb234a86ed619a661f3bb3c2493aaff9cb937e33e198d17f5f20a15881e155" dependencies = [ "libc", "pyo3-build-config", @@ -403,9 +369,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.20.3" +version = "0.21.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158" +checksum = "f0b787de2c6832eb1eb393c9f82f976a5a87bda979780d9b853878846a8d2e4b" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -415,9 +381,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.20.3" +version = "0.21.0-beta.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" +checksum = "5e3b7beed357786d2afe845871964e824ad8af0df38a403f7d01cdc81aadb211" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index ea3e3d73c..f74976967 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ include = [ rust-version = "1.70" [dependencies] -pyo3 = { version = "0.20.3", features = ["generate-import-lib", "num-bigint"] } +pyo3 = { version = "0.21.0-beta.0", features = ["generate-import-lib", "num-bigint"] } regex = "1.10.3" strum = { version = "0.25.0", features = ["derive"] } strum_macros = "0.26.1" @@ -44,7 +44,7 @@ base64 = "0.21.7" num-bigint = "0.4.4" python3-dll-a = "0.2.7" uuid = "1.7.0" -jiter = {version = "0.0.6", features = ["python"]} +jiter = {version = "0.0.7", features = ["python"]} [lib] name = "_pydantic_core" @@ -71,12 +71,12 @@ debug = true strip = false [dev-dependencies] -pyo3 = { version = "0.20.3", features = ["auto-initialize"] } +pyo3 = { version = "0.21.0-beta.0", features = ["auto-initialize"] } [build-dependencies] version_check = "0.9.4" # used where logic has to be version/distribution specific, e.g. pypy -pyo3-build-config = { version = "0.20.2" } +pyo3-build-config = { version = "0.21.0-beta.0" } [lints.clippy] dbg_macro = "warn" diff --git a/benches/main.rs b/benches/main.rs index 4b8a2b106..d1ef27a93 100644 --- a/benches/main.rs +++ b/benches/main.rs @@ -10,17 +10,17 @@ use pyo3::types::{PyDict, PyString}; use _pydantic_core::{validate_core_schema, SchemaValidator}; fn build_schema_validator_with_globals(py: Python, code: &str, globals: Option<&PyDict>) -> SchemaValidator { - let mut schema: &PyDict = py.eval(code, globals, None).unwrap().extract().unwrap(); - schema = validate_core_schema(py, schema, None).unwrap().extract().unwrap(); - SchemaValidator::py_new(py, schema, None).unwrap() + let mut schema = py.eval(code, globals, None).unwrap().extract().unwrap(); + schema = validate_core_schema(&schema, None).unwrap().extract().unwrap(); + SchemaValidator::py_new(py, &schema, None).unwrap() } fn build_schema_validator(py: Python, code: &str) -> SchemaValidator { build_schema_validator_with_globals(py, code, None) } -fn json<'a>(py: Python<'a>, code: &'a str) -> &'a PyAny { - black_box(PyString::new(py, code)) +fn json<'a>(py: Python<'a>, code: &'a str) -> Bound<'a, PyAny> { + black_box(PyString::new_bound(py, code).into_any()) } #[bench] @@ -28,11 +28,11 @@ fn ints_json(bench: &mut Bencher) { Python::with_gil(|py| { let validator = build_schema_validator(py, "{'type': 'int'}"); - let result = validator.validate_json(py, json(py, "123"), None, None, None).unwrap(); + let result = validator.validate_json(py, &json(py, "123"), None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, 123); - bench.iter(|| black_box(validator.validate_json(py, json(py, "123"), None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &json(py, "123"), None, None, None).unwrap())) }) } @@ -41,14 +41,13 @@ fn ints_python(bench: &mut Bencher) { Python::with_gil(|py| { let validator = build_schema_validator(py, "{'type': 'int'}"); - let input = 123_i64.into_py(py); - let input = input.as_ref(py); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = 123_i64.into_py(py).into_bound(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, 123); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -61,7 +60,7 @@ fn list_int_json(bench: &mut Bencher) { (0..100).map(|x| x.to_string()).collect::>().join(",") ); - bench.iter(|| black_box(validator.validate_json(py, json(py, &code), None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &json(py, &code), None, None, None).unwrap())) }) } @@ -80,9 +79,9 @@ fn list_int_input(py: Python<'_>) -> (SchemaValidator, PyObject) { fn list_int_python(bench: &mut Bencher) { Python::with_gil(|py| { let (validator, input) = list_int_input(py); - let input = black_box(input.as_ref(py)); + let input = black_box(input.bind(py)); bench.iter(|| { - let v = validator.validate_python(py, input, None, None, None, None).unwrap(); + let v = validator.validate_python(py, &input, None, None, None, None).unwrap(); black_box(v) }) }) @@ -92,12 +91,12 @@ fn list_int_python(bench: &mut Bencher) { fn list_int_python_isinstance(bench: &mut Bencher) { Python::with_gil(|py| { let (validator, input) = list_int_input(py); - let input = black_box(input.as_ref(py)); - let v = validator.isinstance_python(py, input, None, None, None, None).unwrap(); + let input = black_box(input.bind(py)); + let v = validator.isinstance_python(py, &input, None, None, None, None).unwrap(); assert!(v); bench.iter(|| { - let v = validator.isinstance_python(py, input, None, None, None, None).unwrap(); + let v = validator.isinstance_python(py, &input, None, None, None, None).unwrap(); black_box(v) }) }) @@ -115,7 +114,7 @@ fn list_error_json(bench: &mut Bencher) { .join(", ") ); - match validator.validate_json(py, json(py, &code), None, None, None) { + match validator.validate_json(py, &json(py, &code), None, None, None) { Ok(_) => panic!("unexpectedly valid"), Err(e) => { let v = e.value(py); @@ -127,7 +126,7 @@ fn list_error_json(bench: &mut Bencher) { }; bench.iter( - || match validator.validate_json(py, json(py, &code), None, None, None) { + || match validator.validate_json(py, &json(py, &code), None, None, None) { Ok(_) => panic!("unexpectedly valid"), Err(e) => black_box(e), }, @@ -145,9 +144,9 @@ fn list_error_python_input(py: Python<'_>) -> (SchemaValidator, PyObject) { .join(", ") ); - let input = py.eval(&code, None, None).unwrap(); + let input = py.eval(&code, None, None).unwrap().extract().unwrap(); - match validator.validate_python(py, input, None, None, None, None) { + match validator.validate_python(py, &input, None, None, None, None) { Ok(_) => panic!("unexpectedly valid"), Err(e) => { let v = e.value(py); @@ -165,9 +164,9 @@ fn list_error_python(bench: &mut Bencher) { Python::with_gil(|py| { let (validator, input) = list_error_python_input(py); - let input = black_box(input.as_ref(py)); + let input = black_box(input.bind(py)); bench.iter(|| { - let result = validator.validate_python(py, input, None, None, None, None); + let result = validator.validate_python(py, &input, None, None, None, None); match result { Ok(_) => panic!("unexpectedly valid"), @@ -181,14 +180,12 @@ fn list_error_python(bench: &mut Bencher) { fn list_error_python_isinstance(bench: &mut Bencher) { Python::with_gil(|py| { let (validator, input) = list_error_python_input(py); - let r = validator - .isinstance_python(py, black_box(input.as_ref(py)), None, None, None, None) - .unwrap(); + let input = black_box(input.bind(py)); + let r = validator.isinstance_python(py, &input, None, None, None, None).unwrap(); assert!(!r); - let input = black_box(input.as_ref(py)); bench.iter(|| { - black_box(validator.isinstance_python(py, input, None, None, None, None).unwrap()); + black_box(validator.isinstance_python(py, &input, None, None, None, None).unwrap()); }) }) } @@ -202,7 +199,7 @@ fn list_any_json(bench: &mut Bencher) { (0..100).map(|x| x.to_string()).collect::>().join(",") ); - bench.iter(|| black_box(validator.validate_json(py, json(py, &code), None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &json(py, &code), None, None, None).unwrap())) }) } @@ -214,10 +211,10 @@ fn list_any_python(bench: &mut Bencher) { "[{}]", (0..100).map(|x| x.to_string()).collect::>().join(",") ); - let input = py.eval(&code, None, None).unwrap(); - let input = black_box(input); + let input = py.eval(&code, None, None).unwrap().to_object(py); + let input = black_box(input.bind(py)); bench.iter(|| { - let v = validator.validate_python(py, input, None, None, None, None).unwrap(); + let v = validator.validate_python(py, &input, None, None, None, None).unwrap(); black_box(v) }) }) @@ -247,7 +244,7 @@ fn dict_json(bench: &mut Bencher) { .join(", ") ); - bench.iter(|| black_box(validator.validate_json(py, json(py, &code), None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &json(py, &code), None, None, None).unwrap())) }) } @@ -266,10 +263,10 @@ fn dict_python(bench: &mut Bencher) { .collect::>() .join(", ") ); - let input = py.eval(&code, None, None).unwrap(); - let input = black_box(input); + let input = py.eval(&code, None, None).unwrap().to_object(py); + let input = black_box(input.bind(py)); bench.iter(|| { - let v = validator.validate_python(py, input, None, None, None, None).unwrap(); + let v = validator.validate_python(py, &input, None, None, None, None).unwrap(); black_box(v) }) }) @@ -295,9 +292,9 @@ fn dict_value_error(bench: &mut Bencher) { .join(", ") ); - let input = py.eval(&code, None, None).unwrap(); + let input = py.eval(&code, None, None).unwrap().to_object(py).into_bound(py); - match validator.validate_python(py, input, None, None, None, None) { + match validator.validate_python(py, &input, None, None, None, None) { Ok(_) => panic!("unexpectedly valid"), Err(e) => { let v = e.value(py); @@ -310,7 +307,7 @@ fn dict_value_error(bench: &mut Bencher) { let input = black_box(input); bench.iter(|| { - let result = validator.validate_python(py, input, None, None, None, None); + let result = validator.validate_python(py, &input, None, None, None, None); match result { Ok(_) => panic!("unexpectedly valid"), @@ -345,7 +342,7 @@ fn typed_dict_json(bench: &mut Bencher) { let code = r#"{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7, "h": 8, "i": 9, "j": 0}"#.to_string(); - bench.iter(|| black_box(validator.validate_json(py, json(py, &code), None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &json(py, &code), None, None, None).unwrap())) }) } @@ -373,10 +370,10 @@ fn typed_dict_python(bench: &mut Bencher) { ); let code = r#"{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5, "f": 6, "g": 7, "h": 8, "i": 9, "j": 0}"#.to_string(); - let input = py.eval(&code, None, None).unwrap(); - let input = black_box(input); + let input = py.eval(&code, None, None).unwrap().to_object(py); + let input = black_box(input.bind(py)); bench.iter(|| { - let v = validator.validate_python(py, input, None, None, None, None).unwrap(); + let v = validator.validate_python(py, &input, None, None, None, None).unwrap(); black_box(v) }) }) @@ -413,10 +410,10 @@ fn typed_dict_deep_error(bench: &mut Bencher) { let code = "{'field_a': '1', 'field_b': {'field_c': '2', 'field_d': {'field_e': '4', 'field_f': 'xx'}}}"; - let input = py.eval(code, None, None).unwrap(); - let input = black_box(input); + let input = py.eval(code, None, None).unwrap().to_object(py); + let input = black_box(input.bind(py)); - match validator.validate_python(py, input, None, None, None, None) { + match validator.validate_python(py, &input, None, None, None, None) { Ok(_) => panic!("unexpectedly valid"), Err(e) => { let v = e.value(py); @@ -428,7 +425,7 @@ fn typed_dict_deep_error(bench: &mut Bencher) { }; bench.iter(|| { - let result = validator.validate_python(py, input, None, None, None, None); + let result = validator.validate_python(py, &input, None, None, None, None); match result { Ok(_) => panic!("unexpectedly valid"), @@ -444,16 +441,16 @@ fn complete_model(bench: &mut Bencher) { let sys_path = py.import("sys").unwrap().getattr("path").unwrap(); sys_path.call_method1("append", ("./tests/benchmarks/",)).unwrap(); - let complete_schema = py.import("complete_schema").unwrap(); + let complete_schema = py.import_bound("complete_schema").unwrap(); let mut schema = complete_schema.call_method0("schema").unwrap(); - schema = validate_core_schema(py, schema, None).unwrap().extract().unwrap(); - let validator = SchemaValidator::py_new(py, schema, None).unwrap(); + schema = validate_core_schema(&schema, None).unwrap().extract().unwrap(); + let validator = SchemaValidator::py_new(py, &schema, None).unwrap(); let input = complete_schema.call_method0("input_data_lax").unwrap(); let input = black_box(input); bench.iter(|| { - black_box(validator.validate_python(py, input, None, None, None, None).unwrap()); + black_box(validator.validate_python(py, &input, None, None, None, None).unwrap()); }) }) } @@ -464,18 +461,18 @@ fn nested_model_using_definitions(bench: &mut Bencher) { let sys_path = py.import("sys").unwrap().getattr("path").unwrap(); sys_path.call_method1("append", ("./tests/benchmarks/",)).unwrap(); - let complete_schema = py.import("nested_schema").unwrap(); + let complete_schema = py.import_bound("nested_schema").unwrap(); let mut schema = complete_schema.call_method0("schema_using_defs").unwrap(); - schema = validate_core_schema(py, schema, None).unwrap().extract().unwrap(); - let validator = SchemaValidator::py_new(py, schema, None).unwrap(); + schema = validate_core_schema(&schema, None).unwrap().extract().unwrap(); + let validator = SchemaValidator::py_new(py, &schema, None).unwrap(); let input = complete_schema.call_method0("input_data_valid").unwrap(); let input = black_box(input); - validator.validate_python(py, input, None, None, None, None).unwrap(); + validator.validate_python(py, &input, None, None, None, None).unwrap(); bench.iter(|| { - black_box(validator.validate_python(py, input, None, None, None, None).unwrap()); + black_box(validator.validate_python(py, &input, None, None, None, None).unwrap()); }) }) } @@ -486,18 +483,18 @@ fn nested_model_inlined(bench: &mut Bencher) { let sys_path = py.import("sys").unwrap().getattr("path").unwrap(); sys_path.call_method1("append", ("./tests/benchmarks/",)).unwrap(); - let complete_schema = py.import("nested_schema").unwrap(); + let complete_schema = py.import_bound("nested_schema").unwrap(); let mut schema = complete_schema.call_method0("inlined_schema").unwrap(); - schema = validate_core_schema(py, schema, None).unwrap().extract().unwrap(); - let validator = SchemaValidator::py_new(py, schema, None).unwrap(); + schema = validate_core_schema(&schema, None).unwrap().extract().unwrap(); + let validator = SchemaValidator::py_new(py, &schema, None).unwrap(); let input = complete_schema.call_method0("input_data_valid").unwrap(); let input = black_box(input); - validator.validate_python(py, input, None, None, None, None).unwrap(); + validator.validate_python(py, &input, None, None, None, None).unwrap(); bench.iter(|| { - black_box(validator.validate_python(py, input, None, None, None, None).unwrap()); + black_box(validator.validate_python(py, &input, None, None, None, None).unwrap()); }) }) } @@ -508,13 +505,13 @@ fn literal_ints_few_python(bench: &mut Bencher) { let validator = build_schema_validator(py, "{'type': 'literal', 'expected': list(range(5))}"); let input = 4_i64.into_py(py); - let input = input.as_ref(py); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = input.bind(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, 4); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -524,13 +521,14 @@ fn literal_strings_few_small_python(bench: &mut Bencher) { let validator = build_schema_validator(py, "{'type': 'literal', 'expected': [f'{idx}' for idx in range(5)]}"); let input = py.eval("'4'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -543,20 +541,21 @@ fn literal_strings_few_large_python(bench: &mut Bencher) { ); let input = py.eval("'a' * 25 + '4'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } #[bench] fn literal_enums_few_python(bench: &mut Bencher) { Python::with_gil(|py| { - let globals = PyDict::new(py); + let globals = PyDict::new_bound(py); py.run( r#" from enum import Enum @@ -567,7 +566,7 @@ class Foo(Enum): v3 = object() v4 = object() "#, - Some(globals), + Some(globals.as_gil_ref()), None, ) .unwrap(); @@ -575,15 +574,16 @@ class Foo(Enum): let validator = build_schema_validator_with_globals( py, "{'type': 'literal', 'expected': [Foo.v1, Foo.v2, Foo.v3, Foo.v4]}", - Some(globals), + Some(globals.as_gil_ref()), ); - let input = py.eval("Foo.v4", Some(globals), None).unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = py.eval("Foo.v4", Some(globals.as_gil_ref()), None).unwrap(); + let input = input.to_object(py).into_bound(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); assert!(input.eq(result).unwrap()); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -592,14 +592,13 @@ fn literal_ints_many_python(bench: &mut Bencher) { Python::with_gil(|py| { let validator = build_schema_validator(py, "{'type': 'literal', 'expected': list(range(100))}"); - let input = 99_i64.into_py(py); - let input = input.as_ref(py); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = 99_i64.into_py(py).into_bound(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, 99); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -609,13 +608,14 @@ fn literal_strings_many_small_python(bench: &mut Bencher) { let validator = build_schema_validator(py, "{'type': 'literal', 'expected': [f'{idx}' for idx in range(100)]}"); let input = py.eval("'99'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -628,13 +628,14 @@ fn literal_strings_many_large_python(bench: &mut Bencher) { ); let input = py.eval("'a' * 25 + '99'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) }) } @@ -644,12 +645,13 @@ fn literal_ints_many_json(bench: &mut Bencher) { let validator = build_schema_validator(py, "{'type': 'literal', 'expected': list(range(100))}"); let input_json = py.eval("'99'", None, None).unwrap(); - let result = validator.validate_json(py, input_json, None, None, None).unwrap(); + let input_json = input_json.to_object(py).into_bound(py); + let result = validator.validate_json(py, &input_json, None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, 99); let input_json = black_box(input_json); - bench.iter(|| black_box(validator.validate_json(py, input_json, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &input_json, None, None, None).unwrap())) }) } @@ -662,21 +664,23 @@ fn literal_strings_many_large_json(bench: &mut Bencher) { ); let input = py.eval("'a' * 25 + '99'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_json = py.eval("'\"' + 'a' * 25 + '99' + '\"'", None, None).unwrap(); + let input_json = input_json.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_json(py, input_json, None, None, None).unwrap(); + let result = validator.validate_json(py, &input_json, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input_json = black_box(input_json); - bench.iter(|| black_box(validator.validate_json(py, input_json, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_json(py, &input_json, None, None, None).unwrap())) }) } #[bench] fn literal_mixed_few_python(bench: &mut Bencher) { Python::with_gil(|py| { - let globals = PyDict::new(py); + let globals = PyDict::new_bound(py); py.run( r#" from enum import Enum @@ -687,58 +691,62 @@ class Foo(Enum): v3 = object() v4 = object() "#, - Some(globals), + Some(globals.as_gil_ref()), None, ) .unwrap(); let validator = build_schema_validator_with_globals( py, "{'type': 'literal', 'expected': [None, 'null', -1, Foo.v4]}", - Some(globals), + Some(globals.as_gil_ref()), ); // String { let input = py.eval("'null'", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_str: String = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_str: String = result.extract(py).unwrap(); assert_eq!(result_str, input_str); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) } // Int { let input = py.eval("-1", None, None).unwrap(); + let input = input.to_object(py).into_bound(py); let input_int: i64 = input.extract().unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); let result_int: i64 = result.extract(py).unwrap(); assert_eq!(result_int, input_int); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) } // None { let input = py.eval("None", None, None).unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = input.to_object(py).into_bound(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); assert!(input.eq(result).unwrap()); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) } // Enum { - let input = py.eval("Foo.v4", Some(globals), None).unwrap(); - let result = validator.validate_python(py, input, None, None, None, None).unwrap(); + let input = py.eval("Foo.v4", Some(globals.as_gil_ref()), None).unwrap(); + let input = input.to_object(py).into_bound(py); + let result = validator.validate_python(py, &input, None, None, None, None).unwrap(); assert!(input.eq(result).unwrap()); let input = black_box(input); - bench.iter(|| black_box(validator.validate_python(py, input, None, None, None, None).unwrap())) + bench.iter(|| black_box(validator.validate_python(py, &input, None, None, None, None).unwrap())) } }) } diff --git a/src/argument_markers.rs b/src/argument_markers.rs index 3e73fb629..748dd0ae2 100644 --- a/src/argument_markers.rs +++ b/src/argument_markers.rs @@ -15,9 +15,9 @@ pub struct ArgsKwargs { impl ArgsKwargs { fn eq(&self, py: Python, other: &Self) -> PyResult { - if self.args.as_ref(py).eq(other.args.as_ref(py))? { + if self.args.bind(py).eq(other.args.bind(py))? { match (&self.kwargs, &other.kwargs) { - (Some(d1), Some(d2)) => d1.as_ref(py).eq(d2.as_ref(py)), + (Some(d1), Some(d2)) => d1.bind(py).eq(d2.bind(py)), (None, None) => Ok(true), _ => Ok(false), } @@ -55,9 +55,9 @@ impl ArgsKwargs { } pub fn __repr__(&self, py: Python) -> String { - let args = safe_repr(self.args.as_ref(py)); + let args = safe_repr(self.args.bind(py)); match self.kwargs { - Some(ref d) => format!("ArgsKwargs({args}, {})", safe_repr(d.as_ref(py))), + Some(ref d) => format!("ArgsKwargs({args}, {})", safe_repr(d.bind(py))), None => format!("ArgsKwargs({args})"), } } diff --git a/src/build_tools.rs b/src/build_tools.rs index c242f97a3..23c88970b 100644 --- a/src/build_tools.rs +++ b/src/build_tools.rs @@ -12,10 +12,10 @@ use crate::tools::SchemaDict; use crate::ValidationError; pub fn schema_or_config<'py, T>( - schema: &'py PyDict, - config: Option<&'py PyDict>, - schema_key: &PyString, - config_key: &PyString, + schema: &Bound<'py, PyDict>, + config: Option<&Bound<'py, PyDict>>, + schema_key: &Bound<'py, PyString>, + config_key: &Bound<'py, PyString>, ) -> PyResult> where T: FromPyObject<'py>, @@ -30,9 +30,9 @@ where } pub fn schema_or_config_same<'py, T>( - schema: &'py PyDict, - config: Option<&'py PyDict>, - key: &PyString, + schema: &Bound<'py, PyDict>, + config: Option<&Bound<'py, PyDict>>, + key: &Bound<'py, PyString>, ) -> PyResult> where T: FromPyObject<'py>, @@ -40,7 +40,7 @@ where schema_or_config(schema, config, key, key) } -pub fn is_strict(schema: &PyDict, config: Option<&PyDict>) -> PyResult { +pub fn is_strict(schema: &Bound<'_, PyDict>, config: Option<&Bound<'_, PyDict>>) -> PyResult { let py = schema.py(); Ok(schema_or_config_same(schema, config, intern!(py, "strict"))?.unwrap_or(false)) } @@ -90,7 +90,7 @@ impl SchemaError { ValidationError::new(line_errors, "Schema".to_object(py), InputType::Python, false); let schema_error = SchemaError(SchemaErrorEnum::ValidationError(validation_error)); match Py::new(py, schema_error) { - Ok(err) => PyErr::from_value(err.into_ref(py)), + Ok(err) => PyErr::from_value_bound(err.into_bound(py).into_any()), Err(err) => err, } } @@ -124,7 +124,7 @@ impl SchemaError { fn errors(&self, py: Python) -> PyResult> { match &self.0 { - SchemaErrorEnum::Message(_) => Ok(PyList::empty(py).into_py(py)), + SchemaErrorEnum::Message(_) => Ok(PyList::empty_bound(py).unbind()), SchemaErrorEnum::ValidationError(error) => error.errors(py, false, false, true), } } @@ -174,18 +174,18 @@ pub(crate) enum ExtraBehavior { impl ExtraBehavior { pub fn from_schema_or_config( py: Python, - schema: &PyDict, - config: Option<&PyDict>, + schema: &Bound<'_, PyDict>, + config: Option<&Bound<'_, PyDict>>, default: Self, ) -> PyResult { - let extra_behavior = schema_or_config::>( + let extra_behavior = schema_or_config::>>( schema, config, intern!(py, "extra_behavior"), intern!(py, "extra_fields_behavior"), )? .flatten(); - let res = match extra_behavior { + let res = match extra_behavior.as_ref().map(|s| s.to_str()).transpose()? { Some("allow") => Self::Allow, Some("ignore") => Self::Ignore, Some("forbid") => Self::Forbid, diff --git a/src/errors/line_error.rs b/src/errors/line_error.rs index 2a48bfaf4..ecd429409 100644 --- a/src/errors/line_error.rs +++ b/src/errors/line_error.rs @@ -1,6 +1,7 @@ use pyo3::exceptions::PyTypeError; use pyo3::prelude::*; -use pyo3::PyDowncastError; +use pyo3::DowncastError; +use pyo3::DowncastIntoError; use jiter::JsonValue; @@ -35,8 +36,14 @@ impl From for ValError { } } -impl From> for ValError { - fn from(py_downcast: PyDowncastError) -> Self { +impl From> for ValError { + fn from(py_downcast: DowncastError) -> Self { + Self::InternalErr(PyTypeError::new_err(py_downcast.to_string())) + } +} + +impl From> for ValError { + fn from(py_downcast: DowncastIntoError) -> Self { Self::InternalErr(PyTypeError::new_err(py_downcast.to_string())) } } diff --git a/src/errors/location.rs b/src/errors/location.rs index 55bab0017..138d327ce 100644 --- a/src/errors/location.rs +++ b/src/errors/location.rs @@ -1,5 +1,5 @@ use pyo3::exceptions::PyTypeError; -use pyo3::once_cell::GILOnceCell; +use pyo3::sync::GILOnceCell; use std::fmt; use pyo3::prelude::*; @@ -126,9 +126,9 @@ static EMPTY_TUPLE: GILOnceCell = GILOnceCell::new(); impl ToPyObject for Location { fn to_object(&self, py: Python<'_>) -> PyObject { match self { - Self::List(loc) => PyTuple::new(py, loc.iter().rev()).to_object(py), + Self::List(loc) => PyTuple::new_bound(py, loc.iter().rev()).to_object(py), Self::Empty => EMPTY_TUPLE - .get_or_init(py, || PyTuple::empty(py).to_object(py)) + .get_or_init(py, || PyTuple::empty_bound(py).to_object(py)) .clone_ref(py), } } @@ -193,12 +193,12 @@ impl Serialize for Location { } } -impl TryFrom> for Location { +impl TryFrom>> for Location { type Error = PyErr; /// Only ever called by ValidationError -> PyLineError to convert user input to our internal Location /// Thus this expects the location to *not* be reversed and reverses it before storing it. - fn try_from(location: Option<&PyAny>) -> PyResult { + fn try_from(location: Option<&Bound<'_, PyAny>>) -> PyResult { if let Some(location) = location { let mut loc_vec: Vec = if let Ok(tuple) = location.downcast::() { tuple.iter().map(Into::into).collect() diff --git a/src/errors/mod.rs b/src/errors/mod.rs index de6650527..bee5f7225 100644 --- a/src/errors/mod.rs +++ b/src/errors/mod.rs @@ -13,8 +13,8 @@ pub use self::validation_exception::ValidationError; pub use self::value_exception::{PydanticCustomError, PydanticKnownError, PydanticOmit, PydanticUseDefault}; pub fn py_err_string(py: Python, err: PyErr) -> String { - let value = err.value(py); - match value.get_type().name() { + let value = err.value_bound(py); + match value.get_type().qualname() { Ok(type_name) => match value.str() { Ok(py_str) => { let str_cow = py_str.to_string_lossy(); diff --git a/src/errors/types.rs b/src/errors/types.rs index eddd7dbaa..a77ed6a8e 100644 --- a/src/errors/types.rs +++ b/src/errors/types.rs @@ -3,8 +3,8 @@ use std::borrow::Cow; use std::fmt; use pyo3::exceptions::{PyKeyError, PyTypeError}; -use pyo3::once_cell::GILOnceCell; use pyo3::prelude::*; +use pyo3::sync::GILOnceCell; use pyo3::types::{PyDict, PyList}; use ahash::AHashMap; @@ -18,11 +18,11 @@ use crate::tools::{extract_i64, py_err, py_error_type}; use super::PydanticCustomError; #[pyfunction] -pub fn list_all_errors(py: Python) -> PyResult<&PyList> { - let mut errors: Vec<&PyDict> = Vec::with_capacity(100); +pub fn list_all_errors(py: Python) -> PyResult> { + let mut errors: Vec> = Vec::with_capacity(100); for error_type in ErrorType::iter() { if !matches!(error_type, ErrorType::CustomError { .. }) { - let d = PyDict::new(py); + let d = PyDict::new_bound(py); d.set_item("type", error_type.to_string())?; let message_template_python = error_type.message_template_python(); d.set_item("message_template_python", message_template_python)?; @@ -39,7 +39,7 @@ pub fn list_all_errors(py: Python) -> PyResult<&PyList> { errors.push(d); } } - Ok(PyList::new(py, errors)) + Ok(PyList::new_bound(py, errors)) } fn field_from_context<'py, T: FromPyObject<'py>>( @@ -121,7 +121,8 @@ macro_rules! error_types { } } - fn py_dict_update_ctx(&self, py: Python, dict: &PyDict) -> PyResult { + fn py_dict_update_ctx(&self, py: Python, dict: &Bound<'_, PyDict>) -> PyResult { + use pyo3::types::PyMapping; match self { $( Self::$item { context, $($key,)* } => { @@ -129,7 +130,7 @@ macro_rules! error_types { dict.set_item::<&str, Py>(stringify!($key), $key.to_object(py))?; )* if let Some(ctx) = context { - dict.update(ctx.as_ref(py).downcast()?)?; + dict.update(ctx.bind(py).downcast::()?)?; Ok(true) } else { Ok(false) @@ -669,20 +670,20 @@ impl ErrorType { Self::ValueError { error, .. } => { let error = &error .as_ref() - .map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.as_ref(py).to_string())); + .map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.bind(py).to_string())); render!(tmpl, error) } Self::AssertionError { error, .. } => { let error = &error .as_ref() - .map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.as_ref(py).to_string())); + .map_or(Cow::Borrowed("None"), |v| Cow::Owned(v.bind(py).to_string())); render!(tmpl, error) } Self::CustomError { message_template, context, .. - } => PydanticCustomError::format_message(message_template, context.as_ref().map(|c| c.as_ref(py))), + } => PydanticCustomError::format_message(message_template, context.as_ref().map(|c| c.bind(py))), Self::LiteralError { expected, .. } => render!(tmpl, expected), Self::DateParsing { error, .. } => render!(tmpl, error), Self::DateFromDatetimeParsing { error, .. } => render!(tmpl, error), @@ -729,8 +730,8 @@ impl ErrorType { } pub fn py_dict(&self, py: Python) -> PyResult>> { - let dict = PyDict::new(py); - let custom_ctx_used = self.py_dict_update_ctx(py, dict)?; + let dict = PyDict::new_bound(py); + let custom_ctx_used = self.py_dict_update_ctx(py, &dict)?; if let Self::CustomError { .. } = self { if custom_ctx_used { @@ -785,7 +786,7 @@ impl From for Number { } impl FromPyObject<'_> for Number { - fn extract(obj: &PyAny) -> PyResult { + fn extract_bound(obj: &Bound<'_, PyAny>) -> PyResult { if let Some(int) = extract_i64(obj) { Ok(Number::Int(int)) } else if let Ok(float) = obj.extract::() { diff --git a/src/errors/validation_exception.rs b/src/errors/validation_exception.rs index e87f4b0d9..059763a21 100644 --- a/src/errors/validation_exception.rs +++ b/src/errors/validation_exception.rs @@ -5,8 +5,8 @@ use std::str::from_utf8; use pyo3::exceptions::{PyKeyError, PyTypeError, PyValueError}; use pyo3::ffi; use pyo3::intern; -use pyo3::once_cell::GILOnceCell; use pyo3::prelude::*; +use pyo3::sync::GILOnceCell; use pyo3::types::{PyDict, PyList, PyString}; use serde::ser::{Error, SerializeMap, SerializeSeq}; use serde::{Serialize, Serializer}; @@ -73,7 +73,7 @@ impl ValidationError { return cause_problem; } } - PyErr::from_value(err.as_ref(py)) + PyErr::from_value_bound(err.into_bound(py).into_any()) } Err(err) => err, } @@ -202,9 +202,9 @@ fn include_url_env(py: Python) -> bool { match std::env::var_os("PYDANTIC_ERRORS_OMIT_URL") { Some(val) => { // We don't care whether warning succeeded or not, hence the assignment - let _ = PyErr::warn( + let _ = PyErr::warn_bound( py, - py.get_type::(), + &py.get_type_bound::(), "PYDANTIC_ERRORS_OMIT_URL is deprecated, use PYDANTIC_ERRORS_INCLUDE_URL instead", 1, ); @@ -253,14 +253,17 @@ impl ValidationError { fn from_exception_data( py: Python, title: PyObject, - line_errors: &PyList, + line_errors: Bound<'_, PyList>, input_type: &str, hide_input: bool, ) -> PyResult> { Py::new( py, Self { - line_errors: line_errors.iter().map(PyLineError::try_from).collect::>()?, + line_errors: line_errors + .iter() + .map(|error| PyLineError::try_from(&error)) + .collect::>()?, title, input_type: InputType::try_from(input_type)?, hide_input, @@ -287,7 +290,7 @@ impl ValidationError { ) -> PyResult> { let url_prefix = get_url_prefix(py, include_url); let mut iteration_error = None; - let list = PyList::new( + let list = PyList::new_bound( py, // PyList::new takes ExactSizeIterator, so if an error occurs during iteration we // fill the list with None before returning the error; the list will then be thrown @@ -318,7 +321,7 @@ impl ValidationError { include_url: bool, include_context: bool, include_input: bool, - ) -> PyResult<&'py PyString> { + ) -> PyResult> { let state = SerializationState::new("iso8601", "utf8", "constants")?; let extra = state.extra(py, &SerMode::Json, true, false, false, true, None, None); let serializer = ValidationErrorSerializer { @@ -347,7 +350,7 @@ impl ValidationError { } }; let s = from_utf8(&bytes).map_err(json_py_err)?; - Ok(PyString::new(py, s)) + Ok(PyString::new_bound(py, s)) } fn __repr__(&self, py: Python) -> String { @@ -358,12 +361,12 @@ impl ValidationError { self.__repr__(py) } - fn __reduce__(slf: &PyCell) -> PyResult<(&PyAny, PyObject)> { + fn __reduce__<'py>(slf: &Bound<'py, Self>) -> PyResult<(Bound<'py, PyAny>, PyObject)> { let py = slf.py(); let callable = slf.getattr("from_exception_data")?; let borrow = slf.try_borrow()?; let args = ( - borrow.title.as_ref(py), + borrow.title.bind(py), borrow.errors(py, include_url_env(py), true, true)?, borrow.input_type.into_py(py), borrow.hide_input, @@ -463,11 +466,11 @@ impl From for ValLineError { } } -impl TryFrom<&PyAny> for PyLineError { +impl TryFrom<&Bound<'_, PyAny>> for PyLineError { type Error = PyErr; - fn try_from(value: &PyAny) -> PyResult { - let dict: &PyDict = value.downcast()?; + fn try_from(value: &Bound<'_, PyAny>) -> PyResult { + let dict = value.downcast::()?; let py = value.py(); let type_raw = dict @@ -485,7 +488,7 @@ impl TryFrom<&PyAny> for PyLineError { )); }; - let location = Location::try_from(dict.get_item("loc")?)?; + let location = Location::try_from(dict.get_item("loc")?.as_ref())?; let input_value = match dict.get_item("input")? { Some(i) => i.into_py(py), @@ -513,7 +516,7 @@ impl PyLineError { input_type: InputType, include_input: bool, ) -> PyResult { - let dict = PyDict::new(py); + let dict = PyDict::new_bound(py); dict.set_item("type", self.error_type.type_string())?; dict.set_item("loc", self.location.to_object(py))?; dict.set_item("msg", self.error_type.render_message(py, input_type)?)?; @@ -555,11 +558,11 @@ impl PyLineError { write!(output, " {message} [type={}", self.error_type.type_string())?; if !hide_input { - let input_value = self.input_value.as_ref(py); + let input_value = self.input_value.bind(py); let input_str = safe_repr(input_value); - truncate_input_value!(output, &input_str); + truncate_input_value!(output, &input_str.to_cow()); - if let Ok(type_) = input_value.get_type().name() { + if let Ok(type_) = input_value.get_type().qualname() { write!(output, ", input_type={type_}")?; } } @@ -663,13 +666,13 @@ impl<'py> Serialize for PyLineErrorSerializer<'py> { if self.include_input { map.serialize_entry( "input", - &self.extra.serialize_infer(self.line_error.input_value.as_ref(py)), + &self.extra.serialize_infer(self.line_error.input_value.bind(py)), )?; } if self.include_context { if let Some(context) = self.line_error.error_type.py_dict(py).map_err(py_err_json::)? { - map.serialize_entry("ctx", &self.extra.serialize_infer(context.as_ref(py)))?; + map.serialize_entry("ctx", &self.extra.serialize_infer(context.bind(py)))?; } } if let Some(url_prefix) = self.url_prefix { diff --git a/src/errors/value_exception.rs b/src/errors/value_exception.rs index 3152044d1..e69fc0a9c 100644 --- a/src/errors/value_exception.rs +++ b/src/errors/value_exception.rs @@ -89,7 +89,7 @@ impl PydanticCustomError { } pub fn message(&self, py: Python) -> PyResult { - Self::format_message(&self.message_template, self.context.as_ref().map(|c| c.as_ref(py))) + Self::format_message(&self.message_template, self.context.as_ref().map(|c| c.bind(py))) } fn __str__(&self, py: Python) -> PyResult { @@ -99,7 +99,7 @@ impl PydanticCustomError { fn __repr__(&self, py: Python) -> PyResult { let msg = self.message(py)?; match self.context.as_ref() { - Some(ctx) => Ok(format!("{msg} [type={}, context={}]", self.error_type, ctx.as_ref(py))), + Some(ctx) => Ok(format!("{msg} [type={}, context={}]", self.error_type, ctx.bind(py))), None => Ok(format!("{msg} [type={}, context=None]", self.error_type)), } } @@ -115,14 +115,14 @@ impl PydanticCustomError { ValError::new(error_type, input) } - pub fn format_message(message_template: &str, context: Option<&PyDict>) -> PyResult { + pub fn format_message(message_template: &str, context: Option<&Bound<'_, PyDict>>) -> PyResult { let mut message = message_template.to_string(); if let Some(ctx) = context { - for (key, value) in ctx { - let key: &PyString = key.downcast()?; + for (key, value) in ctx.iter() { + let key = key.downcast::()?; if let Ok(py_str) = value.downcast::() { message = message.replace(&format!("{{{}}}", key.to_str()?), py_str.to_str()?); - } else if let Some(value_int) = extract_i64(value) { + } else if let Some(value_int) = extract_i64(&value) { message = message.replace(&format!("{{{}}}", key.to_str()?), &value_int.to_string()); } else { // fallback for anything else just in case @@ -174,11 +174,7 @@ impl PydanticKnownError { fn __repr__(&self, py: Python) -> PyResult { let msg = self.message(py)?; match self.context(py)?.as_ref() { - Some(ctx) => Ok(format!( - "{msg} [type={}, context={}]", - self.error_type(), - ctx.as_ref(py) - )), + Some(ctx) => Ok(format!("{msg} [type={}, context={}]", self.error_type(), ctx.bind(py))), None => Ok(format!("{msg} [type={}, context=None]", self.error_type())), } } diff --git a/src/input/datetime.rs b/src/input/datetime.rs index ebb5675f2..9dcf9006e 100644 --- a/src/input/datetime.rs +++ b/src/input/datetime.rs @@ -20,7 +20,7 @@ use crate::tools::py_err; #[cfg_attr(debug_assertions, derive(Debug))] pub enum EitherDate<'a> { Raw(Date), - Py(&'a PyDate), + Py(Bound<'a, PyDate>), } impl<'a> From for EitherDate<'a> { @@ -29,13 +29,13 @@ impl<'a> From for EitherDate<'a> { } } -impl<'a> From<&'a PyDate> for EitherDate<'a> { - fn from(date: &'a PyDate) -> Self { +impl<'a> From> for EitherDate<'a> { + fn from(date: Bound<'a, PyDate>) -> Self { Self::Py(date) } } -pub fn pydate_as_date(py_date: &PyAny) -> PyResult { +pub fn pydate_as_date(py_date: &Bound<'_, PyAny>) -> PyResult { let py = py_date.py(); Ok(Date { year: py_date.getattr(intern!(py, "year"))?.extract()?, @@ -55,7 +55,7 @@ impl<'a> EitherDate<'a> { pub fn try_into_py(self, py: Python<'_>) -> PyResult { let date = match self { Self::Py(date) => Ok(date), - Self::Raw(date) => PyDate::new(py, date.year.into(), date.month, date.day), + Self::Raw(date) => PyDate::new_bound(py, date.year.into(), date.month, date.day), }?; Ok(date.into_py(py)) } @@ -64,7 +64,7 @@ impl<'a> EitherDate<'a> { #[cfg_attr(debug_assertions, derive(Debug))] pub enum EitherTime<'a> { Raw(Time), - Py(&'a PyTime), + Py(Bound<'a, PyTime>), } impl<'a> From