Skip to content

Commit 6962266

Browse files
committed
Optimze performance for heavy processing
1 parent deb3f36 commit 6962266

File tree

17 files changed

+318
-15
lines changed

17 files changed

+318
-15
lines changed

docs/source/changelog.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@ Changelog
33

44
All notable changes to this project will be documented in this file.
55

6+
[0.6.3] - 2025-08-11
7+
--------------------
8+
9+
Added
10+
^^^^^
11+
- Added ``PerformanceConfig`` class for fine-tuning performance characteristics with presets for high-performance, low-memory, and balanced modes.
12+
- Added ``LazyFilterChain`` class for lazy evaluation of filter chains to improve performance with large filter chains.
13+
- Added regex pattern caching in ``RegexValidator`` to improve performance with repeated patterns.
14+
15+
Changed
16+
^^^^^^^
17+
- Updated Dockerfile base image from ``debian:buster-slim`` to ``debian:bookworm-slim`` to fix package repository issues after Debian Buster reached end-of-life.
18+
- Optimized Cython compilation with enhanced compiler flags (``-O3``, ``-march=native``, etc.) for better performance.
19+
- Enhanced ``ValidationMixin`` to support lazy evaluation when filter chains exceed the configured threshold.
20+
21+
622
[0.6.2] - 2025-07-03
723
--------------------
824

docs/source/options/filter.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,12 @@ Available Filters
4141
- `Base64ImageDownscaleFilter <#flask_inputfilter.filters.Base64ImageDownscaleFilter>`_
4242
- `Base64ImageResizeFilter <#flask_inputfilter.filters.Base64ImageResizeFilter>`_
4343
- `BlacklistFilter <#flask_inputfilter.filters.BlacklistFilter>`_
44+
- `LazyFilterChain <#flask_inputfilter.filters.LazyFilterChain>`_
4445
- `StringRemoveEmojisFilter <#flask_inputfilter.filters.StringRemoveEmojisFilter>`_
4546
- `StringSlugifyFilter <#flask_inputfilter.filters.StringSlugifyFilter>`_
4647
- `StringTrimFilter <#flask_inputfilter.filters.StringTrimFilter>`_
4748
- `ToAlphaNumericFilter <#flask_inputfilter.filters.ToAlphaNumericFilter>`_
49+
- `ToBase64ImageFilter <#flask_inputfilter.filters.ToBase64ImageFilter>`_
4850
- `ToBooleanFilter <#flask_inputfilter.filters.ToBooleanFilter>`_
4951
- `ToCamelCaseFilter <#flask_inputfilter.filters.ToCamelCaseFilter>`_
5052
- `ToDataclassFilter <#flask_inputfilter.filters.ToDataclassFilter>`_
@@ -53,6 +55,7 @@ Available Filters
5355
- `ToDigitsFilter <#flask_inputfilter.filters.ToDigitsFilter>`_
5456
- `ToEnumFilter <#flask_inputfilter.filters.ToEnumFilter>`_
5557
- `ToFloatFilter <#flask_inputfilter.filters.ToFloatFilter>`_
58+
- `ToImageFilter <#flask_inputfilter.filters.ToImageFilter>`_
5659
- `ToIntegerFilter <#flask_inputfilter.filters.ToIntegerFilter>`_
5760
- `ToIsoFilter <#flask_inputfilter.filters.ToIsoFilter>`_
5861
- `ToLowerFilter <#flask_inputfilter.filters.ToLowerFilter>`_

docs/source/options/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ Options
1212
copy
1313
external_api
1414
deserialization
15+
performance
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
Performance Configuration
2+
=========================
3+
4+
The Flask-InputFilter library provides various performance optimization settings that can be tuned based on your specific use case.
5+
6+
Overview
7+
--------
8+
9+
The ``PerformanceConfig`` class allows you to control various performance-related settings such as lazy evaluation, caching, and string interning.
10+
11+
Configuration Options
12+
---------------------
13+
14+
The following options are available:
15+
16+
- **USE_LAZY_EVALUATION**: Enable lazy evaluation for filter chains (default: ``True``)
17+
- **LAZY_EVALUATION_THRESHOLD**: Minimum number of filters to trigger lazy evaluation (default: ``5``)
18+
- **REGEX_CACHE_SIZE**: Maximum number of compiled regex patterns to cache (default: ``128``)
19+
- **USE_STRING_INTERNING**: Enable automatic string interning for field names (default: ``True``)
20+
- **LARGE_DATASET_THRESHOLD**: Number of fields to consider as "large" for optimization (default: ``10``)
21+
- **USE_CYTHON**: Use Cython-compiled modules when available (default: ``True``)
22+
23+
Usage Examples
24+
--------------
25+
26+
Basic Configuration
27+
~~~~~~~~~~~~~~~~~~~
28+
29+
.. code-block:: python
30+
31+
from flask_inputfilter.performance_config import PerformanceConfig
32+
33+
# Set individual options
34+
PerformanceConfig.USE_LAZY_EVALUATION = True
35+
PerformanceConfig.REGEX_CACHE_SIZE = 256
36+
37+
Preset Configurations
38+
~~~~~~~~~~~~~~~~~~~~~
39+
40+
The library provides preset configurations for common scenarios:
41+
42+
**High Performance Mode**
43+
44+
Optimized for maximum speed, may use more memory:
45+
46+
.. code-block:: python
47+
48+
from flask_inputfilter.performance_config import PerformanceConfig
49+
50+
PerformanceConfig.set_high_performance()
51+
52+
**Low Memory Mode**
53+
54+
Optimized for minimal memory usage, may be slower:
55+
56+
.. code-block:: python
57+
58+
from flask_inputfilter.performance_config import PerformanceConfig
59+
60+
PerformanceConfig.set_low_memory()
61+
62+
**Balanced Mode**
63+
64+
Reset to default balanced configuration:
65+
66+
.. code-block:: python
67+
68+
from flask_inputfilter.performance_config import PerformanceConfig
69+
70+
PerformanceConfig.set_balanced()
71+
72+
Lazy Filter Chain
73+
-----------------
74+
75+
The ``LazyFilterChain`` class provides lazy evaluation for filter chains, which can significantly improve performance when dealing with large filter chains or when early filters might invalidate the need for later processing.
76+
77+
.. autoclass:: flask_inputfilter.filters.LazyFilterChain
78+
:members:
79+
:undoc-members:
80+
:show-inheritance:
81+
82+
Performance Config API
83+
----------------------
84+
85+
.. autoclass:: flask_inputfilter.performance_config.PerformanceConfig
86+
:members:
87+
:undoc-members:
88+
:show-inheritance:
89+
90+
Best Practices
91+
--------------
92+
93+
1. **For High-Volume APIs**: Use ``set_high_performance()`` to maximize throughput
94+
2. **For Memory-Constrained Environments**: Use ``set_low_memory()`` to minimize memory footprint
95+
3. **For Complex Validation Chains**: Keep ``USE_LAZY_EVALUATION`` enabled to avoid unnecessary processing
96+
4. **For Regex-Heavy Validation**: Increase ``REGEX_CACHE_SIZE`` if you have many unique regex patterns
97+
98+
Performance Tips
99+
----------------
100+
101+
- String interning is most effective when you have many repeated field names across requests
102+
- Lazy evaluation provides the most benefit when you have long filter chains (>5 filters)
103+
- The regex cache is shared across all validator instances, so increasing its size helps with diverse patterns
104+
- Cython compilation provides 30-50% performance improvement for core validation logic

env_configs/env.Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM debian:buster-slim
1+
FROM debian:bookworm-slim
22

33
WORKDIR /app
44

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import warnings
24

35
warnings.warn(
@@ -7,5 +9,3 @@
79
DeprecationWarning,
810
stacklevel=2,
911
)
10-
11-
from flask_inputfilter.models import BaseCondition

flask_inputfilter/filters/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from .base_64_image_downscale_filter import Base64ImageDownscaleFilter
66
from .base_64_image_resize_filter import Base64ImageResizeFilter
77
from .blacklist_filter import BlacklistFilter
8+
from .lazy_filter_chain import LazyFilterChain
89
from .string_remove_emojis_filter import StringRemoveEmojisFilter
910
from .string_slugify_filter import StringSlugifyFilter
1011
from .string_trim_filter import StringTrimFilter
@@ -40,6 +41,7 @@
4041
"Base64ImageResizeFilter",
4142
"BaseFilter",
4243
"BlacklistFilter",
44+
"LazyFilterChain",
4345
"StringRemoveEmojisFilter",
4446
"StringSlugifyFilter",
4547
"StringTrimFilter",
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
import warnings
24

35
warnings.warn(
@@ -7,5 +9,3 @@
79
DeprecationWarning,
810
stacklevel=2,
911
)
10-
11-
from flask_inputfilter.models import BaseFilter
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING, Any, Generator
4+
5+
if TYPE_CHECKING:
6+
from flask_inputfilter.models import BaseFilter
7+
8+
9+
class LazyFilterChain:
10+
"""
11+
A lazy evaluation wrapper for filter chains that delays processing until
12+
needed.
13+
14+
This can significantly improve performance when dealing with large filter
15+
chains or when early filters might invalidate the need for later
16+
processing.
17+
"""
18+
19+
__slots__ = ("_filters", "_processed", "_result", "_value")
20+
21+
def __init__(self, filters: list[BaseFilter], value: Any) -> None:
22+
self._filters = filters
23+
self._value = value
24+
self._processed = False
25+
self._result = None
26+
27+
def _process(self) -> Generator[Any, None, None]:
28+
"""Generator that lazily applies filters."""
29+
current_value = self._value
30+
for filter in self._filters:
31+
if current_value is None:
32+
yield None
33+
return
34+
current_value = filter.apply(current_value)
35+
yield current_value
36+
37+
def get_result(self) -> Any:
38+
"""Get the final result, processing filters only when needed."""
39+
if not self._processed:
40+
# Process all filters and get the final result
41+
for result in self._process():
42+
self._result = result
43+
self._processed = True
44+
return self._result
45+
46+
def apply_until(self, condition: callable) -> Any:
47+
"""
48+
Apply filters until a condition is met.
49+
50+
This allows for early termination of filter processing.
51+
"""
52+
current_value = self._value
53+
for filter in self._filters:
54+
if current_value is None or condition(current_value):
55+
return current_value
56+
current_value = filter.apply(current_value)
57+
return current_value

flask_inputfilter/input_filter.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from flask_inputfilter.exceptions import ValidationError
1111
from flask_inputfilter.mixins import DataMixin
1212
from flask_inputfilter.models import BaseFilter, ExternalApiConfig, FieldModel
13+
from flask_inputfilter.performance_config import PerformanceConfig
1314

1415
if TYPE_CHECKING:
1516
from collections.abc import Callable
@@ -34,6 +35,22 @@
3435
"required": sys.intern("required"),
3536
"steps": sys.intern("steps"),
3637
"validators": sys.intern("validators"),
38+
# Common field names
39+
"id": sys.intern("id"),
40+
"name": sys.intern("name"),
41+
"email": sys.intern("email"),
42+
"password": sys.intern("password"),
43+
"username": sys.intern("username"),
44+
"user_id": sys.intern("user_id"),
45+
"created_at": sys.intern("created_at"),
46+
"updated_at": sys.intern("updated_at"),
47+
"status": sys.intern("status"),
48+
"type": sys.intern("type"),
49+
"value": sys.intern("value"),
50+
"data": sys.intern("data"),
51+
"message": sys.intern("message"),
52+
"error": sys.intern("error"),
53+
"result": sys.intern("result"),
3754
}
3855

3956

@@ -437,6 +454,9 @@ def add(
437454
copy (Optional[str]): The name of the field to copy the value
438455
from.
439456
"""
457+
if PerformanceConfig.USE_STRING_INTERNING:
458+
name = sys.intern(name)
459+
440460
if name in self.fields:
441461
raise ValueError(f"Field '{name}' already exists.")
442462

0 commit comments

Comments
 (0)