Skip to content

Commit 8bc4350

Browse files
committed
fix borrows with lookup key
1 parent 67a5922 commit 8bc4350

File tree

7 files changed

+70
-60
lines changed

7 files changed

+70
-60
lines changed

src/lookup_key.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ fn py_get_attrs<'py>(obj: &Bound<'py, PyAny>, attr_name: &Py<PyString>) -> PyRes
580580

581581
pub fn get_lookup_key(
582582
py: Python,
583-
validation_alias: &Option<Py<PyAny>>,
583+
validation_alias: Option<&Py<PyAny>>,
584584
validate_by_name: bool,
585585
validate_by_alias: bool,
586586
field_name: &str,

src/validators/arguments.rs

Lines changed: 21 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use pyo3::intern;
44
use pyo3::prelude::*;
55
use pyo3::types::{PyDict, PyList, PyString, PyTuple};
66

7-
use ahash::{AHashSet, AHashMap};
7+
use ahash::AHashSet;
88
use pyo3::IntoPyObjectExt;
99

1010
use crate::build_tools::py_schema_err;
@@ -45,7 +45,7 @@ struct Parameter {
4545
kwarg_key: Option<Py<PyString>>,
4646
validator: CombinedValidator,
4747
alias: Option<Py<PyAny>>,
48-
mode: String
48+
mode: String,
4949
}
5050

5151
#[derive(Debug)]
@@ -98,9 +98,10 @@ impl BuildValidator for ArgumentsValidator {
9898
had_keyword_only = true;
9999
}
100100

101-
let kwarg_key = match mode == "keyword_only" || mode == "positional_or_keyword" {
102-
true => Some(py_name.unbind()),
103-
false => None,
101+
let kwarg_key = if mode == "keyword_only" || mode == "positional_or_keyword" {
102+
Some(py_name.unbind())
103+
} else {
104+
None
104105
};
105106

106107
let schema = arg.get_as_req(intern!(py, "schema"))?;
@@ -130,8 +131,8 @@ impl BuildValidator for ArgumentsValidator {
130131
name,
131132
kwarg_key,
132133
validator,
133-
alias: arg.get_item(intern!(py, "alias"))?.map(|v| v.into()),
134-
mode: mode.to_string()
134+
alias: arg.get_item(intern!(py, "alias"))?.map(std::convert::Into::into),
135+
mode: mode.to_string(),
135136
});
136137
}
137138

@@ -192,20 +193,11 @@ impl Validator for ArgumentsValidator {
192193
let mut output_args: Vec<PyObject> = Vec::with_capacity(self.positional_params_count);
193194
let output_kwargs = PyDict::new(py);
194195
let mut errors: Vec<ValLineError> = Vec::new();
195-
let mut used_kwargs: AHashSet<&str> = AHashSet::with_capacity(self.parameters.len());
196+
let mut used_kwargs: AHashSet<String> = AHashSet::with_capacity(self.parameters.len());
196197

197198
let validate_by_alias = state.validate_by_alias_or(self.validate_by_alias);
198199
let validate_by_name = state.validate_by_name_or(self.validate_by_name);
199200

200-
let mut lookup_keys = AHashMap::with_capacity(self.parameters.len());
201-
for param in &self.parameters {
202-
if param.mode == "keyword_only" || param.mode == "positional_or_keyword" {
203-
let param_name = param.name.as_str();
204-
let lookup_key = get_lookup_key(py, &param.alias, validate_by_name, validate_by_alias, param_name)?;
205-
lookup_keys.insert(param_name, lookup_key);
206-
}
207-
}
208-
209201
// go through arguments getting the value from args or kwargs and validating it
210202
for (index, parameter) in self.parameters.iter().enumerate() {
211203
let mut pos_value = None;
@@ -215,12 +207,21 @@ impl Validator for ArgumentsValidator {
215207
}
216208
}
217209
let mut kw_value = None;
218-
let kw_lookup_key = lookup_keys.get(parameter.name.as_str());
210+
let mut kw_lookup_key = None;
211+
if parameter.mode == "keyword_only" || parameter.mode == "positional_or_keyword" {
212+
kw_lookup_key = Some(get_lookup_key(
213+
py,
214+
parameter.alias.as_ref(),
215+
validate_by_name,
216+
validate_by_alias,
217+
&parameter.name,
218+
)?);
219+
}
219220

220221
if let Some(kwargs) = args.kwargs() {
221-
if let Some(lookup_key) = kw_lookup_key {
222+
if let Some(ref lookup_key) = kw_lookup_key {
222223
if let Some((lookup_path, value)) = kwargs.get_item(lookup_key)? {
223-
used_kwargs.insert(lookup_path.first_key());
224+
used_kwargs.insert(lookup_path.first_key().to_string());
224225
kw_value = Some((lookup_path, value));
225226
}
226227
}

src/validators/dataclass.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pyo3::intern;
33
use pyo3::prelude::*;
44
use pyo3::types::{PyDict, PyList, PyString, PyTuple, PyType};
55

6-
use ahash::{AHashMap, AHashSet};
6+
use ahash::AHashSet;
77
use pyo3::IntoPyObjectExt;
88

99
use crate::build_tools::py_schema_err;
@@ -97,7 +97,9 @@ impl BuildValidator for DataclassArgsValidator {
9797
kw_only,
9898
name,
9999
py_name: py_name.into(),
100-
alias: field.get_item(intern!(py, "validation_alias"))?.map(|a| a.into()),
100+
alias: field
101+
.get_item(intern!(py, "validation_alias"))?
102+
.map(std::convert::Into::into),
101103
validator,
102104
init: field.get_as(intern!(py, "init"))?.unwrap_or(true),
103105
init_only: field.get_as(intern!(py, "init_only"))?.unwrap_or(false),
@@ -149,19 +151,13 @@ impl Validator for DataclassArgsValidator {
149151
let mut init_only_args = self.init_only_count.map(Vec::with_capacity);
150152

151153
let mut errors: Vec<ValLineError> = Vec::new();
152-
let mut used_keys: AHashSet<&str> = AHashSet::with_capacity(self.fields.len());
154+
let mut used_keys: AHashSet<String> = AHashSet::with_capacity(self.fields.len());
153155

154156
let state = &mut state.rebind_extra(|extra| extra.data = Some(output_dict.clone()));
155157

156158
let validate_by_alias = state.validate_by_alias_or(self.validate_by_alias);
157159
let validate_by_name = state.validate_by_name_or(self.validate_by_name);
158160

159-
let mut lookup_keys = AHashMap::with_capacity(self.fields.len());
160-
for field in &self.fields {
161-
let lookup_key = get_lookup_key(py, &field.alias, validate_by_name, validate_by_alias, &field.name)?;
162-
lookup_keys.insert(&field.name, lookup_key);
163-
}
164-
165161
let mut fields_set_count: usize = 0;
166162

167163
macro_rules! set_item {
@@ -204,11 +200,17 @@ impl Validator for DataclassArgsValidator {
204200
}
205201
}
206202

207-
let lookup_key = &lookup_keys[&field.name];
203+
let lookup_key = get_lookup_key(
204+
py,
205+
field.alias.as_ref(),
206+
validate_by_name,
207+
validate_by_alias,
208+
&field.name,
209+
)?;
208210
let mut kw_value = None;
209211
if let Some(kwargs) = args.kwargs() {
210-
if let Some((lookup_path, value)) = kwargs.get_item(lookup_key)? {
211-
used_keys.insert(lookup_path.first_key());
212+
if let Some((lookup_path, value)) = kwargs.get_item(&lookup_key)? {
213+
used_keys.insert(lookup_path.first_key().to_string());
212214
kw_value = Some((lookup_path, value));
213215
}
214216
}

src/validators/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ impl SchemaValidator {
193193
.map_err(|e| self.prepare_validation_err(py, e, InputType::Python))
194194
}
195195

196+
#[allow(clippy::too_many_arguments)]
196197
#[pyo3(signature = (input, *, strict=None, from_attributes=None, context=None, self_instance=None, by_alias=None, by_name=None))]
197198
pub fn isinstance_python(
198199
&self,
@@ -226,6 +227,7 @@ impl SchemaValidator {
226227
}
227228
}
228229

230+
#[allow(clippy::too_many_arguments)]
229231
#[pyo3(signature = (input, *, strict=None, context=None, self_instance=None, allow_partial=PartialMode::Off, by_alias=None, by_name=None))]
230232
pub fn validate_json(
231233
&self,
@@ -256,6 +258,7 @@ impl SchemaValidator {
256258
r.map_err(|e| self.prepare_validation_err(py, e, InputType::Json))
257259
}
258260

261+
#[allow(clippy::too_many_arguments)]
259262
#[pyo3(signature = (input, *, strict=None, context=None, allow_partial=PartialMode::Off, by_alias=None, by_name=None))]
260263
pub fn validate_strings(
261264
&self,
@@ -684,6 +687,7 @@ pub struct Extra<'a, 'py> {
684687
}
685688

686689
impl<'a, 'py> Extra<'a, 'py> {
690+
#[allow(clippy::too_many_arguments)]
687691
pub fn new(
688692
strict: Option<bool>,
689693
from_attributes: Option<bool>,

src/validators/model_fields.rs

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use pyo3::intern;
33
use pyo3::prelude::*;
44
use pyo3::types::{PyDict, PySet, PyString, PyType};
55

6-
use ahash::{AHashMap, AHashSet};
6+
use ahash::AHashSet;
77
use pyo3::IntoPyObjectExt;
88

99
use crate::build_tools::py_schema_err;
@@ -81,7 +81,9 @@ impl BuildValidator for ModelFieldsValidator {
8181

8282
fields.push(Field {
8383
name: field_name.to_string(),
84-
alias: field_info.get_item(intern!(py, "validation_alias"))?.map(|a| a.into()),
84+
alias: field_info
85+
.get_item(intern!(py, "validation_alias"))?
86+
.map(std::convert::Into::into),
8587
name_py: field_name_py.into(),
8688
validator,
8789
frozen: field_info.get_as::<bool>(intern!(py, "frozen"))?.unwrap_or(false),
@@ -153,15 +155,9 @@ impl Validator for ModelFieldsValidator {
153155
let validate_by_alias = state.validate_by_alias_or(self.validate_by_alias);
154156
let validate_by_name = state.validate_by_name_or(self.validate_by_name);
155157

156-
let mut lookup_keys = AHashMap::with_capacity(self.fields.len());
157-
for field in &self.fields {
158-
let lookup_key = get_lookup_key(py, &field.alias, validate_by_name, validate_by_alias, &field.name)?;
159-
lookup_keys.insert(&field.name, lookup_key);
160-
}
161-
162158
// we only care about which keys have been used if we're iterating over the object for extra after
163159
// the first pass
164-
let mut used_keys: Option<AHashSet<&str>> =
160+
let mut used_keys: Option<AHashSet<String>> =
165161
if self.extra_behavior == ExtraBehavior::Ignore || dict.is_py_get_attr() {
166162
None
167163
} else {
@@ -172,8 +168,14 @@ impl Validator for ModelFieldsValidator {
172168
let state = &mut state.rebind_extra(|extra| extra.data = Some(model_dict.clone()));
173169

174170
for field in &self.fields {
175-
let lookup_key = &lookup_keys[&field.name];
176-
let op_key_value = match dict.get_item(lookup_key) {
171+
let lookup_key = get_lookup_key(
172+
py,
173+
field.alias.as_ref(),
174+
validate_by_name,
175+
validate_by_alias,
176+
&field.name,
177+
)?;
178+
let op_key_value = match dict.get_item(&lookup_key) {
177179
Ok(v) => v,
178180
Err(ValError::LineErrors(line_errors)) => {
179181
for err in line_errors {
@@ -187,7 +189,7 @@ impl Validator for ModelFieldsValidator {
187189
if let Some(ref mut used_keys) = used_keys {
188190
// key is "used" whether or not validation passes, since we want to skip this key in
189191
// extra logic either way
190-
used_keys.insert(lookup_path.first_key());
192+
used_keys.insert(lookup_path.first_key().to_string());
191193
}
192194
match field.validator.validate(py, value.borrow_input(), state) {
193195
Ok(value) => {
@@ -238,7 +240,7 @@ impl Validator for ModelFieldsValidator {
238240
if let Some(used_keys) = used_keys {
239241
struct ValidateToModelExtra<'a, 's, 'py> {
240242
py: Python<'py>,
241-
used_keys: AHashSet<&'a str>,
243+
used_keys: AHashSet<String>,
242244
errors: &'a mut Vec<ValLineError>,
243245
fields_set_vec: &'a mut Vec<Py<PyString>>,
244246
extra_behavior: ExtraBehavior,

src/validators/typed_dict.rs

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
use ahash::AHashMap;
21
use pyo3::intern;
32
use pyo3::prelude::*;
43
use pyo3::types::{PyDict, PyString};
@@ -112,7 +111,9 @@ impl BuildValidator for TypedDictValidator {
112111

113112
fields.push(TypedDictField {
114113
name: field_name.to_string(),
115-
alias: field_info.get_item(intern!(py, "validation_alias"))?.map(|a| a.into()),
114+
alias: field_info
115+
.get_item(intern!(py, "validation_alias"))?
116+
.map(std::convert::Into::into),
116117
name_py: field_name_py.into(),
117118
validator,
118119
required,
@@ -159,15 +160,9 @@ impl Validator for TypedDictValidator {
159160
let validate_by_alias = state.validate_by_alias_or(self.validate_by_alias);
160161
let validate_by_name = state.validate_by_name_or(self.validate_by_name);
161162

162-
let mut lookup_keys = AHashMap::with_capacity(self.fields.len());
163-
for field in &self.fields {
164-
let lookup_key = get_lookup_key(py, &field.alias, validate_by_name, validate_by_alias, &field.name)?;
165-
lookup_keys.insert(&field.name, lookup_key);
166-
}
167-
168163
// we only care about which keys have been used if we're iterating over the object for extra after
169164
// the first pass
170-
let mut used_keys: Option<AHashSet<&str>> =
165+
let mut used_keys: Option<AHashSet<String>> =
171166
if self.extra_behavior == ExtraBehavior::Ignore || dict.is_py_get_attr() {
172167
None
173168
} else {
@@ -180,8 +175,14 @@ impl Validator for TypedDictValidator {
180175
let mut fields_set_count: usize = 0;
181176

182177
for field in &self.fields {
183-
let lookup_key = &lookup_keys[&field.name];
184-
let op_key_value = match dict.get_item(lookup_key) {
178+
let lookup_key = get_lookup_key(
179+
py,
180+
field.alias.as_ref(),
181+
validate_by_name,
182+
validate_by_alias,
183+
&field.name,
184+
)?;
185+
let op_key_value = match dict.get_item(&lookup_key) {
185186
Ok(v) => v,
186187
Err(ValError::LineErrors(line_errors)) => {
187188
let field_loc: LocItem = field.name.clone().into();
@@ -198,7 +199,7 @@ impl Validator for TypedDictValidator {
198199
if let Some(ref mut used_keys) = used_keys {
199200
// key is "used" whether or not validation passes, since we want to skip this key in
200201
// extra logic either way
201-
used_keys.insert(lookup_path.first_key());
202+
used_keys.insert(lookup_path.first_key().to_string());
202203
}
203204
let is_last_partial = if let Some(ref last_key) = partial_last_key {
204205
let first_key_loc: LocItem = lookup_path.first_key().into();
@@ -264,7 +265,7 @@ impl Validator for TypedDictValidator {
264265
if let Some(used_keys) = used_keys {
265266
struct ValidateExtras<'a, 's, 'py> {
266267
py: Python<'py>,
267-
used_keys: AHashSet<&'a str>,
268+
used_keys: AHashSet<String>,
268269
errors: &'a mut Vec<ValLineError>,
269270
extras_validator: Option<&'a CombinedValidator>,
270271
output_dict: &'a Bound<'py, PyDict>,

tests/test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ json_input = '{"a": "something"}'
136136
let json_input = locals.get_item("json_input").unwrap().unwrap();
137137
let binding = SchemaValidator::py_new(py, &schema, None)
138138
.unwrap()
139-
.validate_json(py, &json_input, None, None, None, false.into())
139+
.validate_json(py, &json_input, None, None, None, false.into(), None, None)
140140
.unwrap();
141141
let validation_result: Bound<'_, PyAny> = binding.extract(py).unwrap();
142142
let repr = format!("{}", validation_result.repr().unwrap());

0 commit comments

Comments
 (0)