Skip to content

Commit 389055c

Browse files
committed
Move complex logic outside of base InputFilter class
1 parent 27b1e0d commit 389055c

File tree

16 files changed

+456
-172
lines changed

16 files changed

+456
-172
lines changed

MANIFEST.in

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
include docs/source/index.rst
22
include LICENSE
33
include docs/source/changelog.rst
4-
recursive-include flask_inputfilter *.py *.pyi *.pyx *.pxd *.c *.cpp
5-
recursive-include flask_inputfilter/include *.h
4+
recursive-include flask_inputfilter *.py *.pyi *.pyx *.pxd *.h
5+
6+
recursive-exclude flask_inputfilter *.cpp
7+
68
prune tests
79
recursive-prune __pycache__

flask_inputfilter/_input_filter.pxd

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,5 +74,3 @@ cdef class InputFilter:
7474
cpdef object serialize(self)
7575
cpdef void add_global_validator(self, BaseValidator validator)
7676
cpdef list[BaseValidator] get_global_validators(self)
77-
78-
cdef inline void _check_all_conditions(self, dict validated_data, dict errors)

flask_inputfilter/_input_filter.pyx

Lines changed: 9 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ from flask import Response, g, request
99

1010
from flask_inputfilter.exceptions import ValidationError
1111

12-
from flask_inputfilter.mixins.cimports cimport FieldMixin
12+
from flask_inputfilter.mixins.cimports cimport DataMixin
1313
from flask_inputfilter.models.cimports cimport BaseCondition, BaseFilter, BaseValidator, ExternalApiConfig, FieldModel
1414

1515
from libcpp.vector cimport vector
@@ -180,14 +180,12 @@ cdef class InputFilter:
180180
"""
181181
data = data or self.data
182182
cdef:
183-
dict[str, str] errors = {},
184-
dict[str, Any] validated_data = {}
183+
dict[str, str] errors
184+
dict[str, Any] validated_data
185185

186-
validated_data, errors = FieldMixin.validate_fields(
187-
self.fields, data, self.global_filters, self.global_validators)
188-
189-
if self.conditions:
190-
self._check_all_conditions(validated_data, errors)
186+
validated_data, errors = DataMixin.validate_with_conditions(
187+
self.fields, data, self.global_filters, self.global_validators, self.conditions
188+
)
191189

192190
if errors:
193191
raise ValidationError(errors)
@@ -230,25 +228,7 @@ cdef class InputFilter:
230228
represent field names and values represent the associated
231229
data to be filtered and stored.
232230
"""
233-
self.data = {}
234-
cdef:
235-
Py_ssize_t i, n = len(data) if data else 0
236-
list keys = list(data.keys()) if n > 0 else []
237-
list values = list(data.values()) if n > 0 else []
238-
str field_name
239-
object field_value
240-
241-
for i in range(n):
242-
field_name = keys[i]
243-
field_value = values[i]
244-
if field_name in self.fields:
245-
field_value = FieldMixin.apply_filters(
246-
self.fields[field_name].filters,
247-
self.global_filters,
248-
field_value,
249-
)
250-
251-
self.data[field_name] = field_value
231+
self.data = DataMixin.filter_data(data, self.fields, self.global_filters)
252232

253233
cpdef object get_value(self, str name):
254234
"""
@@ -371,21 +351,7 @@ cdef class InputFilter:
371351
Returns:
372352
bool: True if there are any unknown fields; False otherwise.
373353
"""
374-
if not self.data and self.fields:
375-
return True
376-
cdef set field_set
377-
378-
if len(self.fields) > 10:
379-
field_set = set(self.fields.keys())
380-
for field_name in self.data.keys():
381-
if field_name not in field_set:
382-
return True
383-
else:
384-
for field_name in self.data.keys():
385-
if field_name not in self.fields:
386-
return True
387-
388-
return False
354+
return DataMixin.has_unknown_fields(self.data, self.fields)
389355

390356
cpdef str get_error_message(self, str field_name):
391357
"""
@@ -658,41 +624,7 @@ cdef class InputFilter:
658624
"Can only merge with another InputFilter instance."
659625
)
660626

661-
cdef:
662-
Py_ssize_t i, n
663-
dict other_inputs = other.get_inputs()
664-
list keys = list(other_inputs.keys()) if other_inputs else []
665-
list new_fields = list(other_inputs.values()) if other_inputs else []
666-
667-
# Merge fields
668-
n = len(keys)
669-
for i in range(n):
670-
self.fields[keys[i]] = new_fields[i]
671-
672-
# Merge conditions
673-
self.conditions += other.conditions
674-
675-
# Merge global filters (avoid duplicates by type)
676-
for filter in other.global_filters:
677-
existing_type_map = {
678-
type(v): i for i, v in enumerate(self.global_filters)
679-
}
680-
if type(filter) in existing_type_map:
681-
self.global_filters[existing_type_map[type(filter)]] = filter
682-
else:
683-
self.global_filters.append(filter)
684-
685-
# Merge global validators (avoid duplicates by type)
686-
for validator in other.global_validators:
687-
existing_type_map = {
688-
type(v): i for i, v in enumerate(self.global_validators)
689-
}
690-
if type(validator) in existing_type_map:
691-
self.global_validators[
692-
existing_type_map[type(validator)]
693-
] = validator
694-
else:
695-
self.global_validators.append(validator)
627+
DataMixin.merge_input_filters(self, other)
696628

697629
cpdef void set_model(self, model_class: Type[T]):
698630
"""
@@ -739,16 +671,3 @@ cdef class InputFilter:
739671
list[BaseValidator]: A list of global validators.
740672
"""
741673
return self.global_validators
742-
743-
cdef inline void _check_all_conditions(self, dict[str, Any] validated_data, dict[str, str] errors):
744-
"""
745-
Check all conditions against the validated data.
746-
747-
Args:
748-
validated_data (dict[str, Any]): The data that has been validated.
749-
errors (dict[str, str]): A dictionary to store any validation errors.
750-
"""
751-
try:
752-
FieldMixin.check_conditions(self.conditions, validated_data)
753-
except ValidationError as e:
754-
errors[_INTERNED_STRINGS["_condition"]] = str(e)

flask_inputfilter/input_filter.py

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from flask import Response, g, request
99

1010
from flask_inputfilter.exceptions import ValidationError
11-
from flask_inputfilter.mixins import FieldMixin
11+
from flask_inputfilter.mixins import DataMixin
1212
from flask_inputfilter.models import BaseFilter, ExternalApiConfig, FieldModel
1313

1414
if TYPE_CHECKING:
@@ -185,13 +185,14 @@ def validate_data(
185185
"""
186186
data = data or self.data
187187

188-
validated_data, errors = FieldMixin.validate_fields(
189-
self.fields, data, self.global_filters, self.global_validators
188+
validated_data, errors = DataMixin.validate_with_conditions(
189+
self.fields,
190+
data,
191+
self.global_filters,
192+
self.global_validators,
193+
self.conditions,
190194
)
191195

192-
if self.conditions:
193-
self._check_all_conditions(validated_data, errors)
194-
195196
if errors:
196197
raise ValidationError(errors)
197198

@@ -233,16 +234,9 @@ def set_data(self, data: dict[str, Any]) -> None:
233234
represent field names and values represent the associated
234235
data to be filtered and stored.
235236
"""
236-
self.data = {}
237-
for field_name, field_value in data.items():
238-
if field_name in self.fields:
239-
field_value = FieldMixin.apply_filters(
240-
self.fields[field_name].filters,
241-
self.global_filters,
242-
field_value,
243-
)
244-
245-
self.data[field_name] = field_value
237+
self.data = DataMixin.filter_data(
238+
data, self.fields, self.global_filters
239+
)
246240

247241
def get_value(self, name: str) -> Any:
248242
"""
@@ -315,6 +309,12 @@ def get_raw_values(self) -> dict[str, Any]:
315309
if not self.fields:
316310
return {}
317311

312+
# Use optimized intersection for larger datasets
313+
if len(self.fields) > 10:
314+
field_set = set(self.fields.keys())
315+
data_set = set(self.data.keys())
316+
common_fields = field_set & data_set
317+
return {field: self.data[field] for field in common_fields}
318318
return {
319319
field: self.data[field]
320320
for field in self.fields
@@ -358,10 +358,7 @@ def has_unknown(self) -> bool:
358358
Returns:
359359
bool: True if there are any unknown fields; False otherwise.
360360
"""
361-
if not self.data and self.fields:
362-
return True
363-
364-
return any(field_name not in self.fields for field_name in self.data)
361+
return DataMixin.has_unknown_fields(self.data, self.fields)
365362

366363
def get_error_message(self, field_name: str) -> Optional[str]:
367364
"""
@@ -634,30 +631,7 @@ def merge(self, other: InputFilter) -> None:
634631
"Can only merge with another InputFilter instance."
635632
)
636633

637-
for key, new_field in other.get_inputs().items():
638-
self.fields[key] = new_field
639-
640-
self.conditions += other.conditions
641-
642-
for filter in other.global_filters:
643-
existing_type_map = {
644-
type(v): i for i, v in enumerate(self.global_filters)
645-
}
646-
if type(filter) in existing_type_map:
647-
self.global_filters[existing_type_map[type(filter)]] = filter
648-
else:
649-
self.global_filters.append(filter)
650-
651-
for validator in other.global_validators:
652-
existing_type_map = {
653-
type(v): i for i, v in enumerate(self.global_validators)
654-
}
655-
if type(validator) in existing_type_map:
656-
self.global_validators[existing_type_map[type(validator)]] = (
657-
validator
658-
)
659-
else:
660-
self.global_validators.append(validator)
634+
DataMixin.merge_input_filters(self, other)
661635

662636
def set_model(self, model_class: Type[T]) -> None:
663637
"""
@@ -703,10 +677,3 @@ def get_global_validators(self) -> list[BaseValidator]:
703677
list[BaseValidator]: A list of global validators.
704678
"""
705679
return self.global_validators
706-
707-
def _check_all_conditions(self, validated_data, errors) -> None:
708-
"""Check all conditions against validated data."""
709-
try:
710-
FieldMixin.check_conditions(self.conditions, validated_data)
711-
except ValidationError as e:
712-
errors["_condition"] = str(e)

flask_inputfilter/input_filter.pyi

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,3 @@ class InputFilter:
9090
def serialize(self) -> Union[dict[str, Any], T]: ...
9191
def add_global_validator(self, validator: BaseValidator) -> None: ...
9292
def get_global_validators(self) -> list[BaseValidator]: ...
93-
def _check_all_conditions(self, validated_data, errors) -> None: ...
Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import shutil
22

33
if shutil.which("g++") is not None:
4+
from .data_mixin._data_mixin import DataMixin
45
from .external_api_mixin._external_api_mixin import ExternalApiMixin
5-
from .field_mixin._field_mixin import FieldMixin
6+
from .validation_mixin._validation_mixin import ValidationMixin
67

78
else:
9+
from .data_mixin.data_mixin import DataMixin
810
from .external_api_mixin.external_api_mixin import ExternalApiMixin
9-
from .field_mixin.field_mixin import FieldMixin
11+
from .validation_mixin.validation_mixin import ValidationMixin
1012

1113
__all__ = [
14+
"DataMixin",
1215
"ExternalApiMixin",
13-
"FieldMixin",
16+
"ValidationMixin",
1417
]
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .external_api_mixin._external_api_mixin cimport ExternalApiMixin
2-
from .field_mixin._field_mixin cimport FieldMixin
2+
from .validation_mixin._validation_mixin cimport ValidationMixin
3+
from .data_mixin._data_mixin cimport DataMixin
File renamed without changes.
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from typing import Any
2+
3+
from flask_inputfilter.models.cimports cimport BaseFilter, BaseValidator, FieldModel, BaseCondition, InputFilter
4+
5+
6+
cdef class DataMixin:
7+
8+
@staticmethod
9+
cdef bint has_unknown_fields(dict[str, Any] data, dict[str, FieldModel] fields)
10+
11+
@staticmethod
12+
cdef dict[str, Any] filter_data(dict[str, Any] data, dict[str, FieldModel] fields, list[BaseFilter] global_filters)
13+
14+
@staticmethod
15+
cdef tuple validate_with_conditions(
16+
dict[str, FieldModel] fields,
17+
dict[str, Any] data,
18+
list[BaseFilter] global_filters,
19+
list[BaseValidator] global_validators,
20+
list[BaseCondition] conditions
21+
)
22+
23+
@staticmethod
24+
cdef void merge_input_filters(InputFilter target_filter, InputFilter source_filter) except *
25+
26+
@staticmethod
27+
cdef void _merge_component_list(list target_list, list source_list)

0 commit comments

Comments
 (0)