Skip to content

Commit d2a40fa

Browse files
committed
Added python 3.14 support and new FieldModel
1 parent 9e0b710 commit d2a40fa

File tree

13 files changed

+103
-86
lines changed

13 files changed

+103
-86
lines changed

README.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ and type requirements before being processed.
88
Thank you for using ``flask-inputfilter``!
99
==========================================
1010

11-
If you have any questions or suggestions, please feel free to open an issue on GitHub `here <https://github.com/LeanderCS/flask-inputfilter>`__.
11+
If you have any questions or suggestions, please feel free to open an issue on `GitHub <https://github.com/LeanderCS/flask-inputfilter>`__.
1212

1313
If you don't want to miss any updates, please star the repository.
1414
This will help me to understand how many people are interested in this project.
1515

16-
For information about the usage you can view the documentation `here <https://leandercs.github.io/flask-inputfilter>`__.
16+
For information about the usage you can view the `documentation <https://leandercs.github.io/flask-inputfilter>`__.
1717

1818
:Test Status:
1919

@@ -34,7 +34,7 @@ For information about the usage you can view the documentation `here <https://le
3434

3535
:Downloads:
3636

37-
.. image:: https://img.shields.io/pypi/dm/flask-inputfilter?style=flat-square&label=PyPI
37+
.. image:: https://static.pepy.tech/badge/flask-inputfilter/month
3838
:target: https://pypi.org/project/flask-inputfilter/
3939
.. image:: https://static.pepy.tech/badge/flask-inputfilter
4040
:target: https://pypi.org/project/flask-inputfilter/

docs/changelog.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ Changelog
44
All notable changes to this project will be documented in this file.
55

66

7+
[0.0.10] - 2025-03-06
8+
--------------------
9+
10+
Added
11+
^^^^^
12+
- Added python 3.14 support.
13+
14+
Changed
15+
^^^^^^^
16+
- Use ``FieldModel`` vor field definition. (Only internal change, no impact on the user)
17+
18+
719
[0.0.9.1] - 2025-02-09
820
----------------------
921

docs/conf.py

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,12 @@
1-
# Configuration file for the Sphinx documentation builder.
2-
#
3-
# For the full list of built-in configuration values, see the documentation:
4-
# https://www.sphinx-doc.org/en/master/usage/configuration.html
5-
6-
# -- Project information -----------------------------------------------------
7-
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
8-
91
project = "flask-inputfilter"
102
copyright = "2025, Leander Cain Slotosch"
113
author = "Leander Cain Slotosch"
12-
release = "0.0.9.1"
13-
14-
# -- General configuration ---------------------------------------------------
15-
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
4+
release = "0.0.10"
165

176
extensions = []
187

198
templates_path = []
209
exclude_patterns = []
2110

22-
23-
# -- Options for HTML output -------------------------------------------------
24-
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
25-
2611
html_theme = "alabaster"
2712
html_static_path = []

docs/index.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ Overview
1616
Available functions:
1717
--------------------
1818

19-
- :doc:`Creating your own Conditions, Filters and Validators <guides/create_own>`
19+
- :doc:`InputFilter <options/inputfilter>`
2020
- :doc:`Conditions <options/condition>`
2121
- :doc:`Filter <options/filter>`
2222
- :doc:`Validator <options/validator>`
23+
- :doc:`Creating your own Conditions, Filters and Validators <guides/create_own>`
2324
- :doc:`ExternalApi <options/external_api>`
2425

2526

docs/options/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ Options
55
:maxdepth: 2
66

77
condition
8+
filter
89
validator
910
external_api
10-
filter
1111

1212
The `add` method supports several options:
1313

env_configs/Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,9 @@ RUN /root/.pyenv/bin/pyenv install 3.10.2
3838
RUN /root/.pyenv/bin/pyenv install 3.11.0
3939
RUN /root/.pyenv/bin/pyenv install 3.12.0
4040
RUN /root/.pyenv/bin/pyenv install 3.13.0
41+
RUN /root/.pyenv/bin/pyenv install 3.14-dev
4142

42-
RUN /root/.pyenv/bin/pyenv global 3.7.12 3.8.12 3.9.7 3.10.2 3.11.0 3.12.0 3.13.0
43+
RUN /root/.pyenv/bin/pyenv global 3.7.12 3.8.12 3.9.7 3.10.2 3.11.0 3.12.0 3.13.0 3.14-dev
4344

4445
RUN pip install --upgrade pip
4546

env_configs/requirements-py314.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
flask
2+
pillow
3+
pytest
4+
requests
5+
typing_extensions

flask_inputfilter/InputFilter.py

Lines changed: 49 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,29 @@
11
import re
22
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
33

4-
import requests
54
from flask import Response, g, request
65
from typing_extensions import final
76

87
from flask_inputfilter.Condition.BaseCondition import BaseCondition
98
from flask_inputfilter.Exception import ValidationError
109
from flask_inputfilter.Filter import BaseFilter
11-
from flask_inputfilter.Model import ExternalApiConfig
10+
from flask_inputfilter.Model import ExternalApiConfig, FieldModel
1211
from flask_inputfilter.Validator import BaseValidator
1312

13+
API_PLACEHOLDER_PATTERN = re.compile(r"{{(.*?)}}")
14+
1415

1516
class InputFilter:
1617
"""
1718
Base class for input filters.
1819
"""
1920

2021
def __init__(self, methods: Optional[List[str]] = None) -> None:
21-
self.methods = methods or ["GET", "POST", "PATCH", "PUT", "DELETE"]
22-
self.fields = {}
23-
self.conditions = []
24-
self.global_filters = []
25-
self.global_validators = []
22+
self._methods = methods or ["GET", "POST", "PATCH", "PUT", "DELETE"]
23+
self._fields: Dict[str, FieldModel] = {}
24+
self._conditions: List[BaseCondition] = []
25+
self._global_filters: List[BaseFilter] = []
26+
self._global_validators: List[BaseValidator] = []
2627

2728
@final
2829
def add(
@@ -40,96 +41,90 @@ def add(
4041
"""
4142
Add the field to the input filter.
4243
43-
:param name: The name of the field.
44-
:param required: Whether the field is required.
45-
:param default: The default value of the field.
46-
:param fallback: The fallback value of the field, if validations fails
47-
or field None, although it is required .
48-
:param filters: The filters to apply to the field value.
49-
:param validators: The validators to apply to the field value.
50-
:param steps: Allows to apply multiple filters and validators
51-
in a specific order.
52-
:param external_api: Configuration for an external API call.
53-
:param copy: The name of the field to copy the value from.
44+
Args:
45+
name: The name of the field.
46+
required: Whether the field is required.
47+
default: The default value of the field.
48+
fallback: The fallback value of the field, if validations fails
49+
or field None, although it is required .
50+
filters: The filters to apply to the field value.
51+
validators: The validators to apply to the field value.
52+
steps: Allows to apply multiple filters and validators
53+
in a specific order.
54+
external_api: Configuration for an external API call.
55+
copy: The name of the field to copy the value from.
5456
"""
55-
56-
self.fields[name] = {
57-
"required": required,
58-
"default": default,
59-
"fallback": fallback,
60-
"filters": filters or [],
61-
"validators": validators or [],
62-
"steps": steps or [],
63-
"external_api": external_api,
64-
"copy": copy,
65-
}
57+
self._fields[name] = FieldModel(
58+
required=required,
59+
default=default,
60+
fallback=fallback,
61+
filters=filters or [],
62+
validators=validators or [],
63+
steps=steps or [],
64+
external_api=external_api,
65+
copy=copy,
66+
)
6667

6768
@final
6869
def addCondition(self, condition: BaseCondition) -> None:
6970
"""
7071
Add a condition to the input filter.
7172
"""
72-
self.conditions.append(condition)
73+
self._conditions.append(condition)
7374

7475
@final
7576
def addGlobalFilter(self, filter_: BaseFilter) -> None:
7677
"""
7778
Add a global filter to be applied to all fields.
7879
"""
79-
self.global_filters.append(filter_)
80+
self._global_filters.append(filter_)
8081

8182
@final
8283
def addGlobalValidator(self, validator: BaseValidator) -> None:
8384
"""
8485
Add a global validator to be applied to all fields.
8586
"""
86-
self.global_validators.append(validator)
87+
self._global_validators.append(validator)
8788

88-
@final
8989
def __applyFilters(self, filters: List[BaseFilter], value: Any) -> Any:
9090
"""
9191
Apply filters to the field value.
9292
"""
93-
9493
if value is None:
9594
return value
9695

97-
for filter_ in self.global_filters + filters:
96+
for filter_ in self._global_filters + filters:
9897
value = filter_.apply(value)
9998

10099
return value
101100

102-
@final
103101
def __validateField(
104102
self, validators: List[BaseValidator], fallback: Any, value: Any
105103
) -> None:
106104
"""
107105
Validate the field value.
108106
"""
109-
110107
if value is None:
111108
return
112109

113110
try:
114-
for validator in self.global_validators + validators:
111+
for validator in self._global_validators + validators:
115112
validator.validate(value)
116113
except ValidationError:
117114
if fallback is None:
118115
raise
119116

120117
return fallback
121118

122-
@final
119+
@staticmethod
123120
def __applySteps(
124-
self,
125121
steps: List[Union[BaseFilter, BaseValidator]],
126122
fallback: Any,
127123
value: Any,
128124
) -> Any:
129125
"""
130126
Apply multiple filters and validators in a specific order.
131127
"""
132-
133128
if value is None:
134129
return
135130

@@ -143,17 +138,16 @@ def __applySteps(
143138
if fallback is None:
144139
raise
145140
return fallback
146-
147141
return value
148142

149-
@final
150143
def __callExternalApi(
151144
self, config: ExternalApiConfig, fallback: Any, validated_data: dict
152145
) -> Optional[Any]:
153146
"""
154147
Führt den API-Aufruf durch und gibt den Wert zurück,
155148
der im Antwortkörper zu finden ist.
156149
"""
150+
import requests
157151

158152
requestData = {
159153
"headers": {},
@@ -204,20 +198,16 @@ def __callExternalApi(
204198
return fallback
205199

206200
@staticmethod
207-
@final
208201
def __replacePlaceholders(value: str, validated_data: dict) -> str:
209202
"""
210203
Replace all placeholders, marked with '{{ }}' in value
211204
with the corresponding values from validated_data.
212205
"""
213-
214-
return re.sub(
215-
r"{{(.*?)}}",
206+
return API_PLACEHOLDER_PATTERN.sub(
216207
lambda match: str(validated_data.get(match.group(1))),
217208
value,
218209
)
219210

220-
@final
221211
def __replacePlaceholdersInParams(
222212
self, params: dict, validated_data: dict
223213
) -> dict:
@@ -233,7 +223,6 @@ def __replacePlaceholdersInParams(
233223
}
234224

235225
@staticmethod
236-
@final
237226
def __checkForRequired(
238227
field_name: str,
239228
required: bool,
@@ -251,7 +240,6 @@ def __checkForRequired(
251240
value is returned.
252241
If no of the above conditions are met, a ValidationError is raised.
253242
"""
254-
255243
if value is not None:
256244
return value
257245

@@ -264,7 +252,7 @@ def __checkForRequired(
264252
raise ValidationError(f"Field '{field_name}' is required.")
265253

266254
def __checkConditions(self, validated_data: dict) -> None:
267-
for condition in self.conditions:
255+
for condition in self._conditions:
268256
if not condition.check(validated_data):
269257
raise ValidationError(f"Condition '{condition}' not met.")
270258

@@ -276,24 +264,23 @@ def validateData(
276264
Validate the input data, considering both request data and
277265
URL parameters (kwargs).
278266
"""
279-
280267
if kwargs is None:
281268
kwargs = {}
282269

283270
validated_data = {}
284271
combined_data = {**data, **kwargs}
285272

286-
for field_name, field_info in self.fields.items():
273+
for field_name, field_info in self._fields.items():
287274
value = combined_data.get(field_name)
288275

289-
required = field_info["required"]
290-
default = field_info["default"]
291-
fallback = field_info["fallback"]
292-
filters = field_info["filters"]
293-
validators = field_info["validators"]
294-
steps = field_info["steps"]
295-
external_api = field_info["external_api"]
296-
copy = field_info["copy"]
276+
required = field_info.required
277+
default = field_info.default
278+
fallback = field_info.fallback
279+
filters = field_info.filters
280+
validators = field_info.validators
281+
steps = field_info.steps
282+
external_api = field_info.external_api
283+
copy = field_info.copy
297284

298285
if copy:
299286
value = validated_data.get(copy)
@@ -344,7 +331,7 @@ def wrapper(
344331
*args, **kwargs
345332
) -> Union[Response, Tuple[Any, Dict[str, Any]]]:
346333
input_filter = cls()
347-
if request.method not in input_filter.methods:
334+
if request.method not in input_filter._methods:
348335
return Response(status=405, response="Method Not Allowed")
349336

350337
data = request.json if request.is_json else request.args

flask_inputfilter/Model/ExternalApiConfig.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ class ExternalApiConfig:
1111
:param method: The HTTP method to use.
1212
:param params: The parameters to send to the API.
1313
:param data_key: The key in the response JSON to use
14+
:param api_key: The API key to use.
15+
:param headers: The headers to send to the API.
1416
"""
1517

1618
url: str

0 commit comments

Comments
 (0)