Skip to content

Commit de6002b

Browse files
committed
Add more compiler directives
1 parent 2086e74 commit de6002b

File tree

9 files changed

+155
-95
lines changed

9 files changed

+155
-95
lines changed

flask_inputfilter/_input_filter.pyx

Lines changed: 67 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
# cython: language=c++
2-
# cython: language_level=3
3-
# cython: binding=True
4-
# cython: cdivision=True
5-
# cython: boundscheck=False
6-
# cython: initializedcheck=False
72
import json
83
import logging
94
from typing import Any, Optional, Type, TypeVar, Union
@@ -190,19 +185,29 @@ cdef class InputFilter:
190185
cdef dict errors = {}
191186
cdef dict validated_data = {}
192187

193-
cdef object default
194-
cdef object fallback
195-
cdef list filters
196-
cdef list validators
197-
cdef object external_api
198-
cdef str copy
199-
200-
cdef list global_filters = self.global_filters
201-
cdef list global_validators = self.global_validators
202-
cdef bint has_global_filters = bool(global_filters)
203-
cdef bint has_global_validators = bool(global_validators)
204-
205-
for field_name, field_info in self.fields.items():
188+
cdef:
189+
list global_filters = self.global_filters
190+
list global_validators = self.global_validators
191+
bint has_global_filters = bool(global_filters)
192+
bint has_global_validators = bool(global_validators)
193+
194+
cdef:
195+
int i = 0
196+
int n = len(self.fields)
197+
list field_names = list(self.fields.keys())
198+
list field_infos = list(self.fields.values())
199+
200+
cdef:
201+
object default
202+
object fallback
203+
list filters
204+
list validators
205+
object external_api
206+
str copy
207+
208+
for i in range(n):
209+
field_name = field_names[i]
210+
field_info = field_infos[i]
206211
try:
207212
if field_info.copy:
208213
value = validated_data.get(field_info.copy)
@@ -216,27 +221,27 @@ cdef class InputFilter:
216221
value = data.get(field_name)
217222

218223
if field_info.filters or has_global_filters:
219-
filters = field_info.filters
220-
if has_global_filters:
221-
filters = filters + global_filters
222-
value = FieldMixin.apply_filters(filters, value)
224+
value = FieldMixin.apply_filters(
225+
field_info.filters + global_filters
226+
if has_global_filters
227+
else field_info.filters,
228+
value
229+
)
223230

224231
if field_info.validators or has_global_validators:
225-
validators = field_info.validators
226-
if has_global_validators:
227-
validators = validators + global_validators
228-
result = FieldMixin.validate_field(
229-
validators, field_info.fallback, value
230-
)
231-
if result is not None:
232-
value = result
232+
value = FieldMixin.validate_field(
233+
field_info.validators + global_validators
234+
if has_global_validators
235+
else field_info.validators,
236+
field_info.fallback,
237+
value
238+
) or value
233239

234240
if field_info.steps:
235-
result = FieldMixin.apply_steps(
236-
field_info.steps, field_info.fallback, value
237-
)
238-
if result is not None:
239-
value = result
241+
value = FieldMixin.apply_steps(
242+
field_info.steps,
243+
field_info.fallback, value
244+
) or value
240245

241246
if value is None:
242247
if field_info.required:
@@ -308,7 +313,15 @@ cdef class InputFilter:
308313
data to be filtered and stored.
309314
"""
310315
self.data = {}
311-
for field_name, field_value in data.items():
316+
cdef:
317+
int i = 0
318+
int n = len(data)
319+
list keys = list(data.keys())
320+
list values = list(data.values())
321+
322+
for i in range(n):
323+
field_name = keys[i]
324+
field_value = values[i]
312325
if field_name in self.fields:
313326
field_value = FieldMixin.apply_filters(
314327
filters=self.fields[field_name].filters + self.global_filters,
@@ -388,11 +401,17 @@ cdef class InputFilter:
388401
if not self.fields:
389402
return {}
390403

391-
return {
392-
field: self.data[field]
393-
for field in self.fields
394-
if field in self.data
395-
}
404+
cdef:
405+
int i = 0
406+
int n = len(self.fields)
407+
dict result = {}
408+
list field_names = list(self.fields.keys())
409+
410+
for i in range(n):
411+
field = field_names[i]
412+
if field in self.data:
413+
result[field] = self.data[field]
414+
return result
396415

397416
cpdef dict get_unfiltered_data(self):
398417
"""
@@ -711,9 +730,14 @@ cdef class InputFilter:
711730
"Can only merge with another InputFilter instance."
712731
)
713732

714-
for key, new_field in other.get_inputs().items():
715-
self.fields[key] = new_field
733+
cdef:
734+
int i = 0
735+
int n = len(other.get_inputs())
736+
list keys = list(other.get_inputs().keys())
737+
list new_fields = list(other.get_inputs().values())
716738

739+
for i in range(n):
740+
self.fields[keys[i]] = new_fields[i]
717741
self.conditions += other.conditions
718742

719743
for filter in other.global_filters:

flask_inputfilter/filters/array_element_filter.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,12 @@ def apply(self, value: Any) -> list[Any]:
4949

5050
result = []
5151
for element in value:
52-
if hasattr(self.element_filter, "apply"):
52+
if isinstance(self.element_filter, BaseFilter):
5353
result.append(self.element_filter.apply(element))
5454
continue
5555

5656
elif isinstance(self.element_filter, list) and all(
57-
hasattr(v, "apply") for v in self.element_filter
57+
isinstance(v, BaseFilter) for v in self.element_filter
5858
):
5959
for filter_instance in self.element_filter:
6060
element = filter_instance.apply(element)

flask_inputfilter/input_filter.py

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -205,27 +205,32 @@ def validate_data(
205205
value = data.get(field_name)
206206

207207
if field_info.filters or has_global_filters:
208-
filters = field_info.filters
209-
if has_global_filters:
210-
filters = filters + global_filters
211-
value = FieldMixin.apply_filters(filters, value)
208+
value = FieldMixin.apply_filters(
209+
field_info.filters + global_filters
210+
if has_global_filters
211+
else field_info.filters,
212+
value,
213+
)
212214

213215
if field_info.validators or has_global_validators:
214-
validators = field_info.validators
215-
if has_global_validators:
216-
validators = validators + global_validators
217-
result = FieldMixin.validate_field(
218-
validators, field_info.fallback, value
216+
value = (
217+
FieldMixin.validate_field(
218+
field_info.validators + global_validators
219+
if has_global_validators
220+
else field_info.validators,
221+
field_info.fallback,
222+
value,
223+
)
224+
or value
219225
)
220-
if result is not None:
221-
value = result
222226

223227
if field_info.steps:
224-
result = FieldMixin.apply_steps(
225-
field_info.steps, field_info.fallback, value
228+
value = (
229+
FieldMixin.apply_steps(
230+
field_info.steps, field_info.fallback, value
231+
)
232+
or value
226233
)
227-
if result is not None:
228-
value = result
229234

230235
if value is None:
231236
if field_info.required:
@@ -694,7 +699,7 @@ def merge(self, other: "InputFilter") -> None:
694699
Args:
695700
other (InputFilter): The InputFilter instance to merge.
696701
"""
697-
if not hasattr(other, "validate_data"):
702+
if not isinstance(other, InputFilter):
698703
raise TypeError(
699704
"Can only merge with another InputFilter instance."
700705
)

flask_inputfilter/mixins/_external_api_mixin.pyx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
# cython: language=c++
2-
# cython: language_level=3
3-
# cython: binding=True
4-
# cython: cdivision=True
5-
# cython: boundscheck=False
6-
# cython: initializedcheck=False
72
import re
8-
from typing import Any, Dict, Optional
3+
from typing import Optional
94

105
from flask_inputfilter.exceptions import ValidationError
116
from flask_inputfilter.models import ExternalApiConfig
@@ -14,7 +9,7 @@ from flask_inputfilter.models import ExternalApiConfig
149
cdef class ExternalApiMixin:
1510

1611
@staticmethod
17-
cdef str replace_placeholders(
12+
cdef inline str replace_placeholders(
1813
str value,
1914
dict validated_data
2015
):

flask_inputfilter/mixins/_field_mixin.pyx

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from typing import Any, Union
1+
# cython: language=c++
2+
from typing import Union
3+
4+
import cython
25

36
from flask_inputfilter.conditions import BaseCondition
47
from flask_inputfilter.exceptions import ValidationError
@@ -8,6 +11,7 @@ from flask_inputfilter.validators import BaseValidator
811

912
cdef class FieldMixin:
1013
@staticmethod
14+
@cython.exceptval(check=False)
1115
cdef object apply_filters(list filters, object value):
1216
"""
1317
Apply filters to the field value.
@@ -24,8 +28,14 @@ cdef class FieldMixin:
2428
if value is None:
2529
return None
2630

27-
for filter in filters:
28-
value = filter.apply(value)
31+
cdef:
32+
int i
33+
int n = len(filters)
34+
object current_filter
35+
36+
for i in range(n):
37+
current_filter = filters[i]
38+
value = current_filter.apply(value)
2939

3040
return value
3141

@@ -64,12 +74,18 @@ cdef class FieldMixin:
6474
if value is None:
6575
return None
6676

77+
cdef:
78+
int i
79+
int n = len(steps)
80+
object current_step
81+
6782
try:
68-
for step in steps:
69-
if hasattr(step, 'apply'):
70-
value = step.apply(value)
71-
elif hasattr(step, 'validate'):
72-
step.validate(value)
83+
for i in range(n):
84+
current_step = steps[i]
85+
if isinstance(current_step, BaseFilter):
86+
value = current_step.apply(value)
87+
elif isinstance(current_step, BaseValidator):
88+
current_step.validate(value)
7389
except ValidationError:
7490
if fallback is None:
7591
raise
@@ -92,14 +108,20 @@ cdef class FieldMixin:
92108
validated_data (dict[str, Any]):
93109
The validated data to check against the conditions.
94110
"""
95-
for condition in conditions:
96-
if not condition.check(validated_data):
111+
cdef:
112+
int i
113+
int n = len(conditions)
114+
object current_condition
115+
116+
for i in range(n):
117+
current_condition = conditions[i]
118+
if not current_condition.check(validated_data):
97119
raise ValidationError(
98-
f"Condition '{condition.__class__.__name__}' not met."
120+
f"Condition '{current_condition.__class__.__name__}' not met."
99121
)
100122

101123
@staticmethod
102-
cdef object check_for_required(
124+
cdef inline object check_for_required(
103125
str field_name,
104126
bint required,
105127
object default,
@@ -160,9 +182,15 @@ cdef class FieldMixin:
160182
if value is None:
161183
return None
162184

185+
cdef:
186+
int i
187+
int n = len(validators)
188+
object current_validator
189+
163190
try:
164-
for validator in validators:
165-
validator.validate(value)
191+
for i in range(n):
192+
current_validator = validators[i]
193+
current_validator.validate(value)
166194
except ValidationError:
167195
if fallback is None:
168196
raise

flask_inputfilter/mixins/field_mixin.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,9 @@ def apply_steps(
9898

9999
try:
100100
for step in steps:
101-
if hasattr(step, "apply"):
101+
if isinstance(step, BaseFilter):
102102
value = step.apply(value)
103-
elif hasattr(step, "validate"):
103+
elif isinstance(step, BaseValidator):
104104
step.validate(value)
105105
except ValidationError:
106106
if fallback is None:

flask_inputfilter/models/_field_model.pyx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,4 @@
11
# cython: language=c++
2-
# cython: language_level=3
3-
# cython: binding=True
4-
# cython: cdivision=True
5-
# cython: boundscheck=False
6-
# cython: initializedcheck=False
72
from __future__ import annotations
83

94
from typing import Any, Optional, Union

flask_inputfilter/models/external_api_config.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,14 @@ class ExternalApiConfig:
99
"""
1010
Configuration for an external API call.
1111
12-
:param url: The URL of the external API.
13-
:param method: The HTTP method to use.
14-
:param params: The parameters to send to the API.
15-
:param data_key: The key in the response JSON to use
16-
:param api_key: The API key to use.
17-
:param headers: The headers to send to the API.
12+
**Parameters:**
13+
14+
- **url** (*str*): The URL of the external API.
15+
- **method** (*str*): The HTTP method to use.
16+
- **params** (*Optional[dict[str, str]]*): The parameters to send to the API.
17+
- **data_key** (*Optional[str]*): The key in the response JSON to use
18+
- **api_key** (*Optional[str]*): The API key to use.
19+
- **headers** (*Optional[dict[str, str]]*): The headers to send to the API.
1820
"""
1921

2022
url: str

0 commit comments

Comments
 (0)