• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

LeanderCS / flask-inputfilter / #405

30 Jun 2025 05:23PM UTC coverage: 92.131% (-1.3%) from 93.409%
#405

push

coveralls-python

web-flow
Merge pull request #57 from LeanderCS/optimize

Optimize InputFilter

304 of 336 new or added lines in 71 files covered. (90.48%)

36 existing lines in 3 files now uncovered.

1885 of 2046 relevant lines covered (92.13%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

76.33
/flask_inputfilter/input_filter.py
1
from __future__ import annotations
1✔
2

3
import json
1✔
4
import logging
1✔
5
import sys
1✔
6
from collections.abc import Callable
1✔
7
from typing import Any, Optional, Type, TypeVar, Union
1✔
8

9
from flask import Response, g, request
1✔
10

11
from flask_inputfilter.conditions import BaseCondition
1✔
12
from flask_inputfilter.exceptions import ValidationError
1✔
13
from flask_inputfilter.filters import BaseFilter
1✔
14
from flask_inputfilter.mixins import ExternalApiMixin, FieldMixin
1✔
15
from flask_inputfilter.models import ExternalApiConfig, FieldModel
1✔
16
from flask_inputfilter.validators import BaseValidator
1✔
17

18
T = TypeVar("T")
1✔
19

20
_INTERNED_STRINGS = {
1✔
21
    "required": sys.intern("required"),
22
    "default": sys.intern("default"),
23
    "fallback": sys.intern("fallback"),
24
    "filters": sys.intern("filters"),
25
    "validators": sys.intern("validators"),
26
    "steps": sys.intern("steps"),
27
    "external_api": sys.intern("external_api"),
28
    "copy": sys.intern("copy"),
29
    "GET": sys.intern("GET"),
30
    "POST": sys.intern("POST"),
31
    "PUT": sys.intern("PUT"),
32
    "PATCH": sys.intern("PATCH"),
33
    "DELETE": sys.intern("DELETE"),
34
    "_condition": sys.intern("_condition"),
35
    "_error": sys.intern("_error"),
36
}
37

38

39
class InputFilter:
1✔
40
    """Base class for all input filters."""
41

42
    def __init__(self, methods: Optional[list[str]] = None) -> None:
1✔
43
        self.methods: list[str] = methods or [
1✔
44
            "GET",
45
            "POST",
46
            "PATCH",
47
            "PUT",
48
            "DELETE",
49
        ]
50
        self.fields: dict[str, FieldModel] = {}
1✔
51
        self.conditions: list[BaseCondition] = []
1✔
52
        self.global_filters: list[BaseFilter] = []
1✔
53
        self.global_validators: list[BaseValidator] = []
1✔
54
        self.data: dict[str, Any] = {}
1✔
55
        self.validated_data: dict[str, Any] = {}
1✔
56
        self.errors: dict[str, str] = {}
1✔
57
        self.model_class: Optional[Type[T]] = None
1✔
58

59
    def isValid(self) -> bool:
1✔
UNCOV
60
        import warnings
×
61

62
        warnings.warn(
×
63
            "isValid() is deprecated, use is_valid() instead",
64
            DeprecationWarning,
65
            stacklevel=2,
66
        )
67
        return self.is_valid()
×
68

69
    def is_valid(self) -> bool:
1✔
70
        """
71
        Checks if the object's state or its attributes meet certain conditions
72
        to be considered valid. This function is typically used to ensure that
73
        the current state complies with specific requirements or rules.
74

75
        Returns:
76
            bool: Returns True if the state or attributes of the object fulfill
77
                all required conditions; otherwise, returns False.
78
        """
79
        try:
1✔
80
            self.validate_data()
1✔
81

82
        except ValidationError as e:
1✔
83
            self.errors = e.args[0]
1✔
84
            return False
1✔
85

86
        return True
1✔
87

88
    @classmethod
1✔
89
    def validate(
90
        cls,
91
    ) -> Callable:
92
        """
93
        Decorator for validating input data in routes.
94

95
        Args:
96
            cls
97

98
        Returns:
99
            Callable
100
        """
101

102
        def decorator(
1✔
103
            f: Callable,
104
        ) -> Callable:
105
            """
106
            Decorator function to validate input data for a Flask route.
107

108
            Args:
109
                f (Callable): The Flask route function to be decorated.
110

111
            Returns:
112
                Callable: The wrapped function with input validation.
113
            """
114

115
            def wrapper(
1✔
116
                *args, **kwargs
117
            ) -> Union[Response, tuple[Any, dict[str, Any]]]:
118
                """
119
                Wrapper function to handle input validation and error handling
120
                for the decorated route function.
121

122
                Args:
123
                    *args: Positional arguments for the route function.
124
                    **kwargs: Keyword arguments for the route function.
125

126
                Returns:
127
                    Union[Response, tuple[Any, dict[str, Any]]]: The response
128
                        from the route function or an error response.
129
                """
130
                input_filter = cls()
1✔
131
                if request.method not in input_filter.methods:
1✔
132
                    return Response(status=405)
1✔
133

134
                if request.is_json:
1✔
135
                    data = request.get_json(cache=True)
1✔
136
                    if not isinstance(data, dict):
1✔
137
                        data = {}
1✔
138
                else:
139
                    data = dict(request.args)
1✔
140

141
                try:
1✔
142
                    if kwargs:
1✔
143
                        data.update(kwargs)
1✔
144

145
                    input_filter.data = data
1✔
146
                    input_filter.validated_data = {}
1✔
147
                    input_filter.errors = {}
1✔
148

149
                    g.validated_data = input_filter.validate_data()
1✔
150

151
                except ValidationError as e:
1✔
152
                    return Response(
1✔
153
                        status=400,
154
                        response=json.dumps(e.args[0]),
155
                        mimetype="application/json",
156
                    )
157

158
                except Exception:
×
NEW
159
                    logging.exception(
×
160
                        "An unexpected exception occurred while "
161
                        "validating input data.",
162
                    )
163
                    return Response(status=500)
×
164

165
                return f(*args, **kwargs)
1✔
166

167
            return wrapper
1✔
168

169
        return decorator
1✔
170

171
    def validateData(
1✔
172
        self, data: Optional[dict[str, Any]] = None
173
    ) -> Union[dict[str, Any], Type[T]]:
UNCOV
174
        import warnings
×
175

UNCOV
176
        warnings.warn(
×
177
            "validateData() is deprecated, use validate_data() instead",
178
            DeprecationWarning,
179
            stacklevel=2,
180
        )
UNCOV
181
        return self.validate_data(data)
×
182

183
    def validate_data(
1✔
184
        self, data: Optional[dict[str, Any]] = None
185
    ) -> Union[dict[str, Any], Type[T]]:
186
        """
187
        Validates input data against defined field rules, including applying
188
        filters, validators, custom logic steps, and fallback mechanisms. The
189
        validation process also ensures the required fields are handled
190
        appropriately and conditions are checked after processing.
191

192
        Args:
193
            data (dict[str, Any]): A dictionary containing the input data to
194
                be validated where keys represent field names and values
195
                represent the corresponding data.
196

197
        Returns:
198
            Union[dict[str, Any], Type[T]]: A dictionary containing the
199
                validated data with any modifications, default values,
200
                or processed values as per the defined validation rules.
201

202
        Raises:
203
            Any errors raised during external API calls, validation, or
204
                logical steps execution of the respective fields or conditions
205
                will propagate without explicit handling here.
206
        """
207
        data = data or self.data
1✔
208
        errors = {}
1✔
209
        validated_data = {}
1✔
210

211
        global_filters = self.global_filters
1✔
212
        global_validators = self.global_validators
1✔
213
        has_global_filters = bool(global_filters)
1✔
214
        has_global_validators = bool(global_validators)
1✔
215

216
        for field_name, field_info in self.fields.items():
1✔
217
            try:
1✔
218
                if field_info.copy:
1✔
219
                    value = validated_data.get(field_info.copy)
1✔
220
                elif field_info.external_api:
1✔
221
                    value = ExternalApiMixin.call_external_api(
1✔
222
                        field_info.external_api,
223
                        field_info.fallback,
224
                        validated_data,
225
                    )
226
                else:
227
                    value = data.get(field_name)
1✔
228

229
                if field_info.filters or has_global_filters:
1✔
230
                    filters = field_info.filters
1✔
231
                    if has_global_filters:
1✔
232
                        filters = filters + global_filters
1✔
233
                    value = FieldMixin.apply_filters(filters, value)
1✔
234

235
                if field_info.validators or has_global_validators:
1✔
236
                    validators = field_info.validators
1✔
237
                    if has_global_validators:
1✔
238
                        validators = validators + global_validators
1✔
239
                    result = FieldMixin.validate_field(
1✔
240
                        validators, field_info.fallback, value
241
                    )
242
                    if result is not None:
1✔
243
                        value = result
1✔
244

245
                if field_info.steps:
1✔
246
                    result = FieldMixin.apply_steps(
1✔
247
                        field_info.steps, field_info.fallback, value
248
                    )
249
                    if result is not None:
1✔
250
                        value = result
1✔
251

252
                if value is None:
1✔
253
                    if field_info.required:
1✔
254
                        if field_info.fallback is not None:
1✔
255
                            value = field_info.fallback
1✔
256
                        elif field_info.default is not None:
1✔
NEW
257
                            value = field_info.default
×
258
                        else:
259
                            raise ValidationError(
1✔
260
                                f"Field '{field_name}' is required."
261
                            )
262
                    elif field_info.default is not None:
1✔
263
                        value = field_info.default
1✔
264

265
                validated_data[field_name] = value
1✔
266

267
            except ValidationError as e:
1✔
268
                errors[field_name] = str(e)
1✔
269

270
        if self.conditions:
1✔
271
            try:
1✔
272
                FieldMixin.check_conditions(self.conditions, validated_data)
1✔
273
            except ValidationError as e:
1✔
274
                errors["_condition"] = str(e)
1✔
275

276
        if errors:
1✔
277
            raise ValidationError(errors)
1✔
278

279
        self.validated_data = validated_data
1✔
280

281
        if self.model_class is not None:
1✔
282
            return self.model_class(**validated_data)
1✔
283

284
        return validated_data
1✔
285

286
    def addCondition(self, condition: BaseCondition) -> None:
1✔
UNCOV
287
        import warnings
×
288

289
        warnings.warn(
×
290
            "addCondition() is deprecated, use add_condition() instead",
291
            DeprecationWarning,
292
            stacklevel=2,
293
        )
294
        self.add_condition(condition)
×
295

296
    def add_condition(self, condition: BaseCondition) -> None:
1✔
297
        """
298
        Add a condition to the input filter.
299

300
        Args:
301
            condition (BaseCondition): The condition to add.
302
        """
303
        self.conditions.append(condition)
1✔
304

305
    def getConditions(self) -> list[BaseCondition]:
1✔
UNCOV
306
        import warnings
×
307

308
        warnings.warn(
×
309
            "getConditions() is deprecated, use get_conditions() instead",
310
            DeprecationWarning,
311
            stacklevel=2,
312
        )
313
        return self.get_conditions()
×
314

315
    def get_conditions(self) -> list[BaseCondition]:
1✔
316
        """
317
        Retrieve the list of all registered conditions.
318

319
        This function provides access to the conditions that have been
320
        registered and stored. Each condition in the returned list
321
        is represented as an instance of the BaseCondition type.
322

323
        Returns:
324
            list[BaseCondition]: A list containing all currently registered
325
                instances of BaseCondition.
326
        """
327
        return self.conditions
1✔
328

329
    def setData(self, data: dict[str, Any]) -> None:
1✔
UNCOV
330
        import warnings
×
331

332
        warnings.warn(
×
333
            "setData() is deprecated, use set_data() instead",
334
            DeprecationWarning,
335
            stacklevel=2,
336
        )
337
        self.set_data(data)
×
338

339
    def set_data(self, data: dict[str, Any]) -> None:
1✔
340
        """
341
        Filters and sets the provided data into the object's internal storage,
342
        ensuring that only the specified fields are considered and their values
343
        are processed through defined filters.
344

345
        Parameters:
346
            data (dict[str, Any]):
347
                The input dictionary containing key-value pairs where keys
348
                represent field names and values represent the associated
349
                data to be filtered and stored.
350
        """
351
        self.data = {}
1✔
352
        for field_name, field_value in data.items():
1✔
353
            if field_name in self.fields:
1✔
354
                field_value = FieldMixin.apply_filters(
1✔
355
                    filters=self.fields[field_name].filters,
356
                    value=field_value,
357
                )
358

359
            self.data[field_name] = field_value
1✔
360

361
    def getValue(self, name: str) -> Any:
1✔
UNCOV
362
        import warnings
×
363

364
        warnings.warn(
×
365
            "getValue() is deprecated, use get_value() instead",
366
            DeprecationWarning,
367
            stacklevel=2,
368
        )
369
        return self.get_value(name)
×
370

371
    def get_value(self, name: str) -> Any:
1✔
372
        """
373
        This method retrieves a value associated with the provided name. It
374
        searches for the value based on the given identifier and returns the
375
        corresponding result. If no value is found, it typically returns a
376
        default or fallback output. The method aims to provide flexibility in
377
        retrieving data without explicitly specifying the details of the
378
        underlying implementation.
379

380
        Args:
381
            name (str): A string that represents the identifier for which the
382
                 corresponding value is being retrieved. It is used to perform
383
                 the lookup.
384

385
        Returns:
386
            Any: The retrieved value associated with the given name. The
387
                 specific type of this value is dependent on the
388
                 implementation and the data being accessed.
389
        """
390
        return self.validated_data.get(name)
1✔
391

392
    def getValues(self) -> dict[str, Any]:
1✔
UNCOV
393
        import warnings
×
394

395
        warnings.warn(
×
396
            "getValues() is deprecated, use get_values() instead",
397
            DeprecationWarning,
398
            stacklevel=2,
399
        )
400
        return self.get_values()
×
401

402
    def get_values(self) -> dict[str, Any]:
1✔
403
        """
404
        Retrieves a dictionary of key-value pairs from the current object. This
405
        method provides access to the internal state or configuration of the
406
        object in a dictionary format, where keys are strings and values can be
407
        of various types depending on the object's design.
408

409
        Returns:
410
            dict[str, Any]: A dictionary containing string keys and their
411
                            corresponding values of any data type.
412
        """
413
        return self.validated_data
1✔
414

415
    def getRawValue(self, name: str) -> Any:
1✔
UNCOV
416
        import warnings
×
417

418
        warnings.warn(
×
419
            "getRawValue() is deprecated, use get_raw_value() instead",
420
            DeprecationWarning,
421
            stacklevel=2,
422
        )
423
        return self.get_raw_value(name)
×
424

425
    def get_raw_value(self, name: str) -> Any:
1✔
426
        """
427
        Fetches the raw value associated with the provided key.
428

429
        This method is used to retrieve the underlying value linked to the
430
        given key without applying any transformations or validations. It
431
        directly fetches the raw stored value and is typically used in
432
        scenarios where the raw data is needed for processing or debugging
433
        purposes.
434

435
        Args:
436
            name (str): The name of the key whose raw value is to be
437
                retrieved.
438

439
        Returns:
440
            Any: The raw value associated with the provided key.
441
        """
442
        return self.data.get(name)
1✔
443

444
    def getRawValues(self) -> dict[str, Any]:
1✔
UNCOV
445
        import warnings
×
446

447
        warnings.warn(
×
448
            "getRawValues() is deprecated, use get_raw_values() instead",
449
            DeprecationWarning,
450
            stacklevel=2,
451
        )
452
        return self.get_raw_values()
×
453

454
    def get_raw_values(self) -> dict[str, Any]:
1✔
455
        """
456
        Retrieves raw values from a given source and returns them as a
457
        dictionary.
458

459
        This method is used to fetch and return unprocessed or raw data in
460
        the form of a dictionary where the keys are strings, representing
461
        the identifiers, and the values are of any data type.
462

463
        Returns:
464
            dict[str, Any]: A dictionary containing the raw values retrieved.
465
               The keys are strings representing the identifiers, and the
466
               values can be of any type, depending on the source
467
               being accessed.
468
        """
469
        if not self.fields:
1✔
470
            return {}
1✔
471

472
        return {
1✔
473
            field: self.data[field]
474
            for field in self.fields
475
            if field in self.data
476
        }
477

478
    def getUnfilteredData(self) -> dict[str, Any]:
1✔
UNCOV
479
        import warnings
×
480

481
        warnings.warn(
×
482
            "getUnfilteredData() is deprecated, use "
483
            "get_unfiltered_data() instead",
484
            DeprecationWarning,
485
            stacklevel=2,
486
        )
487
        return self.get_unfiltered_data()
×
488

489
    def get_unfiltered_data(self) -> dict[str, Any]:
1✔
490
        """
491
        Fetches unfiltered data from the data source.
492

493
        This method retrieves data without any filtering, processing, or
494
        manipulations applied. It is intended to provide raw data that has
495
        not been altered since being retrieved from its source. The usage
496
        of this method should be limited to scenarios where unprocessed data
497
        is required, as it does not perform any validations or checks.
498

499
        Returns:
500
            dict[str, Any]: The unfiltered, raw data retrieved from the
501
                 data source. The return type may vary based on the
502
                 specific implementation of the data source.
503
        """
504
        return self.data
1✔
505

506
    def setUnfilteredData(self, data: dict[str, Any]) -> None:
1✔
UNCOV
507
        import warnings
×
508

509
        warnings.warn(
×
510
            "setUnfilteredData() is deprecated, use "
511
            "set_unfiltered_data() instead",
512
            DeprecationWarning,
513
            stacklevel=2,
514
        )
515
        self.set_unfiltered_data(data)
×
516

517
    def set_unfiltered_data(self, data: dict[str, Any]) -> None:
1✔
518
        """
519
        Sets unfiltered data for the current instance. This method assigns a
520
        given dictionary of data to the instance for further processing. It
521
        updates the internal state using the provided data.
522

523
        Parameters:
524
            data (dict[str, Any]): A dictionary containing the unfiltered
525
                data to be associated with the instance.
526
        """
527
        self.data = data
1✔
528

529
    def hasUnknown(self) -> bool:
1✔
UNCOV
530
        import warnings
×
531

532
        warnings.warn(
×
533
            "hasUnknown() is deprecated, use has_unknown() instead",
534
            DeprecationWarning,
535
            stacklevel=2,
536
        )
537
        return self.has_unknown()
×
538

539
    def has_unknown(self) -> bool:
1✔
540
        """
541
        Checks whether any values in the current data do not have corresponding
542
        configurations in the defined fields.
543

544
        Returns:
545
            bool: True if there are any unknown fields; False otherwise.
546
        """
547
        if not self.data and self.fields:
1✔
548
            return True
1✔
549

550
        return any(
1✔
551
            field_name not in self.fields.keys()
552
            for field_name in self.data.keys()
553
        )
554

555
    def getErrorMessage(self, field_name: str) -> Optional[str]:
1✔
UNCOV
556
        import warnings
×
557

558
        warnings.warn(
×
559
            "getErrorMessage() is deprecated, use get_error_message() instead",
560
            DeprecationWarning,
561
            stacklevel=2,
562
        )
563
        return self.get_error_message(field_name)
×
564

565
    def get_error_message(self, field_name: str) -> Optional[str]:
1✔
566
        """
567
        Retrieves and returns a predefined error message.
568

569
        This method is intended to provide a consistent error message
570
        to be used across the application when an error occurs. The
571
        message is predefined and does not accept any parameters.
572
        The exact content of the error message may vary based on
573
        specific implementation, but it is designed to convey meaningful
574
        information about the nature of an error.
575

576
        Args:
577
            field_name (str): The name of the field for which the error
578
                message is being retrieved.
579

580
        Returns:
581
            Optional[str]: A string representing the predefined error message.
582
        """
583
        return self.errors.get(field_name)
1✔
584

585
    def getErrorMessages(self) -> dict[str, str]:
1✔
UNCOV
586
        import warnings
×
587

588
        warnings.warn(
×
589
            "getErrorMessages() is deprecated, use "
590
            "get_error_messages() instead",
591
            DeprecationWarning,
592
            stacklevel=2,
593
        )
594
        return self.get_error_messages()
×
595

596
    def get_error_messages(self) -> dict[str, str]:
1✔
597
        """
598
        Retrieves all error messages associated with the fields in the input
599
        filter.
600

601
        This method aggregates and returns a dictionary of error messages
602
        where the keys represent field names, and the values are their
603
        respective error messages.
604

605
        Returns:
606
            dict[str, str]: A dictionary containing field names as keys and
607
                            their corresponding error messages as values.
608
        """
609
        return self.errors
1✔
610

611
    def add(
1✔
612
        self,
613
        name: str,
614
        required: bool = False,
615
        default: Any = None,
616
        fallback: Any = None,
617
        filters: Optional[list[BaseFilter]] = None,
618
        validators: Optional[list[BaseValidator]] = None,
619
        steps: Optional[list[Union[BaseFilter, BaseValidator]]] = None,
620
        external_api: Optional[ExternalApiConfig] = None,
621
        copy: Optional[str] = None,
622
    ) -> None:
623
        """
624
        Add the field to the input filter.
625

626
        Args:
627
            name (str): The name of the field.
628

629
            required (Optional[bool]): Whether the field is required.
630

631
            default (Optional[Any]): The default value of the field.
632

633
            fallback (Optional[Any]): The fallback value of the field, if
634
                validations fails or field None, although it is required.
635

636
            filters (Optional[list[BaseFilter]]): The filters to apply to
637
                the field value.
638

639
            validators (Optional[list[BaseValidator]]): The validators to
640
                apply to the field value.
641

642
            steps (Optional[list[Union[BaseFilter, BaseValidator]]]): Allows
643
                to apply multiple filters and validators in a specific order.
644

645
            external_api (Optional[ExternalApiConfig]): Configuration for an
646
                external API call.
647

648
            copy (Optional[str]): The name of the field to copy the value
649
                from.
650
        """
651
        if name in self.fields:
1✔
652
            raise ValueError(f"Field '{name}' already exists.")
1✔
653

654
        self.fields[name] = FieldModel(
1✔
655
            required=required,
656
            default=default,
657
            fallback=fallback,
658
            filters=filters or [],
659
            validators=validators or [],
660
            steps=steps or [],
661
            external_api=external_api,
662
            copy=copy,
663
        )
664

665
    def has(self, field_name: str) -> bool:
1✔
666
        """
667
        This method checks the existence of a specific field within the input
668
        filter values, identified by its field name. It does not return a
669
        value, serving purely as a validation or existence check mechanism.
670

671
        Args:
672
            field_name (str): The name of the field to check for existence.
673

674
        Returns:
675
            bool: True if the field exists in the input filter,
676
                otherwise False.
677
        """
678
        return field_name in self.fields
1✔
679

680
    def getInput(self, field_name: str) -> Optional[FieldModel]:
1✔
UNCOV
681
        import warnings
×
682

683
        warnings.warn(
×
684
            "getInput() is deprecated, use get_input() instead",
685
            DeprecationWarning,
686
            stacklevel=2,
687
        )
688
        return self.get_input(field_name)
×
689

690
    def get_input(self, field_name: str) -> Optional[FieldModel]:
1✔
691
        """
692
        Represents a method to retrieve a field by its name.
693

694
        This method allows fetching the configuration of a specific field
695
        within the object, using its name as a string. It ensures
696
        compatibility with various field names and provides a generic
697
        return type to accommodate different data types for the fields.
698

699
        Args:
700
            field_name (str): A string representing the name of the field who
701
                        needs to be retrieved.
702

703
        Returns:
704
            Optional[FieldModel]: The field corresponding to the
705
                specified name.
706
        """
707
        return self.fields.get(field_name)
1✔
708

709
    def getInputs(self) -> dict[str, FieldModel]:
1✔
UNCOV
710
        import warnings
×
711

UNCOV
712
        warnings.warn(
×
713
            "getInputs() is deprecated, use get_inputs() instead",
714
            DeprecationWarning,
715
            stacklevel=2,
716
        )
UNCOV
717
        return self.get_inputs()
×
718

719
    def get_inputs(self) -> dict[str, FieldModel]:
1✔
720
        """
721
        Retrieve the dictionary of input fields associated with the object.
722

723
        Returns:
724
            dict[str, FieldModel]: Dictionary containing field names as
725
                keys and their corresponding FieldModel instances as values
726
        """
727
        return self.fields
1✔
728

729
    def remove(self, field_name: str) -> Optional[FieldModel]:
1✔
730
        """
731
        Removes the specified field from the instance or collection.
732

733
        This method is used to delete a specific field identified by
734
        its name. It ensures the designated field is removed entirely
735
        from the relevant data structure. No value is returned upon
736
        successful execution.
737

738
        Args:
739
            field_name (str): The name of the field to be removed.
740

741
        Returns:
742
            Any: The value of the removed field, if any.
743
        """
744
        return self.fields.pop(field_name, None)
1✔
745

746
    def count(self) -> int:
1✔
747
        """
748
        Counts the total number of elements in the collection.
749

750
        This method returns the total count of elements stored within the
751
        underlying data structure, providing a quick way to ascertain the
752
        size or number of entries available.
753

754
        Returns:
755
            int: The total number of elements in the collection.
756
        """
757
        return len(self.fields)
1✔
758

759
    def replace(
1✔
760
        self,
761
        name: str,
762
        required: bool = False,
763
        default: Any = None,
764
        fallback: Any = None,
765
        filters: Optional[list[BaseFilter]] = None,
766
        validators: Optional[list[BaseValidator]] = None,
767
        steps: Optional[list[Union[BaseFilter, BaseValidator]]] = None,
768
        external_api: Optional[ExternalApiConfig] = None,
769
        copy: Optional[str] = None,
770
    ) -> None:
771
        """
772
        Replaces a field in the input filter.
773

774
        Args:
775
             name (str): The name of the field.
776

777
            required (Optional[bool]): Whether the field is required.
778

779
            default (Optional[Any]): The default value of the field.
780

781
            fallback (Optional[Any]): The fallback value of the field, if
782
                validations fails or field None, although it is required.
783

784
            filters (Optional[list[BaseFilter]]): The filters to apply to
785
                the field value.
786

787
            validators (Optional[list[BaseValidator]]): The validators to
788
                apply to the field value.
789

790
            steps (Optional[list[Union[BaseFilter, BaseValidator]]]): Allows
791
                to apply multiple filters and validators in a specific order.
792

793
            external_api (Optional[ExternalApiConfig]): Configuration for an
794
                external API call.
795

796
            copy (Optional[str]): The name of the field to copy the value
797
                from.
798
        """
799
        self.fields[name] = FieldModel(
1✔
800
            required=required,
801
            default=default,
802
            fallback=fallback,
803
            filters=filters or [],
804
            validators=validators or [],
805
            steps=steps or [],
806
            external_api=external_api,
807
            copy=copy,
808
        )
809

810
    def addGlobalFilter(self, filter: BaseFilter) -> None:
1✔
UNCOV
811
        import warnings
×
812

813
        warnings.warn(
×
814
            "addGlobalFilter() is deprecated, use add_global_filter() instead",
815
            DeprecationWarning,
816
            stacklevel=2,
817
        )
818
        self.add_global_filter(filter)
×
819

820
    def add_global_filter(self, filter: BaseFilter) -> None:
1✔
821
        """
822
        Add a global filter to be applied to all fields.
823

824
        Args:
825
            filter: The filter to add.
826
        """
827
        self.global_filters.append(filter)
1✔
828

829
    def getGlobalFilters(self) -> list[BaseFilter]:
1✔
UNCOV
830
        import warnings
×
831

832
        warnings.warn(
×
833
            "getGlobalFilters() is deprecated, use "
834
            "get_global_filters() instead",
835
            DeprecationWarning,
836
            stacklevel=2,
837
        )
838
        return self.get_global_filters()
×
839

840
    def get_global_filters(self) -> list[BaseFilter]:
1✔
841
        """
842
        Retrieve all global filters associated with this InputFilter instance.
843

844
        This method returns a list of BaseFilter instances that have been
845
        added as global filters. These filters are applied universally to
846
        all fields during data processing.
847

848
        Returns:
849
            list[BaseFilter]: A list of global filters.
850
        """
851
        return self.global_filters
1✔
852

853
    def clear(self) -> None:
1✔
854
        """
855
        Resets all fields of the InputFilter instance to their initial empty
856
        state.
857

858
        This method clears the internal storage of fields, conditions, filters,
859
        validators, and data, effectively resetting the object as if it were
860
        newly initialized.
861
        """
862
        self.fields.clear()
1✔
863
        self.conditions.clear()
1✔
864
        self.global_filters.clear()
1✔
865
        self.global_validators.clear()
1✔
866
        self.data.clear()
1✔
867
        self.validated_data.clear()
1✔
868
        self.errors.clear()
1✔
869

870
    def merge(self, other: "InputFilter") -> None:
1✔
871
        """
872
        Merges another InputFilter instance intelligently into the current
873
        instance.
874

875
        - Fields with the same name are merged recursively if possible,
876
            otherwise overwritten.
877
        - Conditions, are combined and duplicated.
878
        - Global filters and validators are merged without duplicates.
879

880
        Args:
881
            other (InputFilter): The InputFilter instance to merge.
882
        """
883
        if not hasattr(other, "validate_data"):
1✔
884
            raise TypeError(
1✔
885
                "Can only merge with another InputFilter instance."
886
            )
887

888
        for key, new_field in other.get_inputs().items():
1✔
889
            self.fields[key] = new_field
1✔
890

891
        self.conditions += other.conditions
1✔
892

893
        for filter in other.global_filters:
1✔
894
            existing_type_map = {
1✔
895
                type(v): i for i, v in enumerate(self.global_filters)
896
            }
897
            if type(filter) in existing_type_map:
1✔
898
                self.global_filters[existing_type_map[type(filter)]] = filter
1✔
899
            else:
900
                self.global_filters.append(filter)
1✔
901

902
        for validator in other.global_validators:
1✔
903
            existing_type_map = {
1✔
904
                type(v): i for i, v in enumerate(self.global_validators)
905
            }
906
            if type(validator) in existing_type_map:
1✔
907
                self.global_validators[
1✔
908
                    existing_type_map[type(validator)]
909
                ] = validator
910
            else:
911
                self.global_validators.append(validator)
1✔
912

913
    def setModel(self, model_class: Type[T]) -> None:
1✔
UNCOV
914
        import warnings
×
915

916
        warnings.warn(
×
917
            "setModel() is deprecated, use set_model() instead",
918
            DeprecationWarning,
919
            stacklevel=2,
920
        )
921
        self.set_model(model_class)
×
922

923
    def set_model(self, model_class: Type[T]) -> None:
1✔
924
        """
925
        Set the model class for serialization.
926

927
        Args:
928
            model_class (Type[T]): The class to use for serialization.
929
        """
930
        self.model_class = model_class
1✔
931

932
    def serialize(self) -> Union[dict[str, Any], T]:
1✔
933
        """
934
        Serialize the validated data. If a model class is set, returns an
935
        instance of that class, otherwise returns the raw validated data.
936

937
        Returns:
938
            Union[dict[str, Any], T]: The serialized data.
939
        """
940
        if self.model_class is None:
1✔
941
            return self.validated_data
1✔
942

943
        return self.model_class(**self.validated_data)
1✔
944

945
    def addGlobalValidator(self, validator: BaseValidator) -> None:
1✔
UNCOV
946
        import warnings
×
947

948
        warnings.warn(
×
949
            "addGlobalValidator() is deprecated, use "
950
            "add_global_validator() instead",
951
            DeprecationWarning,
952
            stacklevel=2,
953
        )
954
        self.add_global_validator(validator)
×
955

956
    def add_global_validator(self, validator: BaseValidator) -> None:
1✔
957
        """
958
        Add a global validator to be applied to all fields.
959

960
        Args:
961
            validator (BaseValidator): The validator to add.
962
        """
963
        self.global_validators.append(validator)
1✔
964

965
    def getGlobalValidators(self) -> list[BaseValidator]:
1✔
UNCOV
966
        import warnings
×
967

968
        warnings.warn(
×
969
            "getGlobalValidators() is deprecated, use "
970
            "get_global_validators() instead",
971
            DeprecationWarning,
972
            stacklevel=2,
973
        )
974
        return self.get_global_validators()
×
975

976
    def get_global_validators(self) -> list[BaseValidator]:
1✔
977
        """
978
        Retrieve all global validators associated with this InputFilter
979
        instance.
980

981
        This method returns a list of BaseValidator instances that have been
982
        added as global validators. These validators are applied universally
983
        to all fields during validation.
984

985
        Returns:
986
            list[BaseValidator]: A list of global validators.
987
        """
988
        return self.global_validators
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc