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

LeanderCS / flask-inputfilter / #478

28 Sep 2025 09:11PM UTC coverage: 92.193% (-0.08%) from 92.276%
#478

push

coveralls-python

web-flow
Merge pull request #65 from LeanderCS/more-declaratives

Add more declaratives to prevent possible errors and make construction more direct

98 of 113 new or added lines in 14 files covered. (86.73%)

3 existing lines in 3 files now uncovered.

2102 of 2280 relevant lines covered (92.19%)

0.92 hits per line

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

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

3
import dataclasses
1✔
4
import inspect
1✔
5
import json
1✔
6
import logging
1✔
7
import sys
1✔
8
from typing import TYPE_CHECKING, Any, Optional, Type, TypeVar, Union
1✔
9

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

12
from flask_inputfilter.declarative import FieldDescriptor
1✔
13
from flask_inputfilter.exceptions import ValidationError
1✔
14
from flask_inputfilter.mixins import DataMixin
1✔
15
from flask_inputfilter.models import BaseFilter, ExternalApiConfig, FieldModel
1✔
16

17
if TYPE_CHECKING:
1✔
18
    from collections.abc import Callable
×
19

20
    from flask_inputfilter.models import BaseCondition, BaseValidator
×
21

22
T = TypeVar("T")
1✔
23

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

42

43
class InputFilter:
1✔
44
    """Base class for all input filters."""
45

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

63
        self._register_decorator_components()
1✔
64

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

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

78
        except ValidationError as e:
1✔
79
            self.errors = e.args[0]
1✔
80
            return False
1✔
81

82
        return True
1✔
83

84
    @classmethod
1✔
85
    def validate(
86
        cls,
87
    ) -> Callable:
88
        """
89
        Decorator for validating input data in routes.
90

91
        Args:
92
            cls
93

94
        Returns:
95
            Callable
96
        """
97

98
        def decorator(
1✔
99
            f: Callable,
100
        ) -> Callable:
101
            """
102
            Decorator function to validate input data for a Flask route.
103

104
            Args:
105
                f (Callable): The Flask route function to be decorated.
106

107
            Returns:
108
                Callable: The wrapped function with input validation.
109
            """
110

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

118
                Args:
119
                    *args: Positional arguments for the route function.
120
                    **kwargs: Keyword arguments for the route function.
121

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

130
                if request.is_json:
1✔
131
                    data = request.get_json(cache=True)
1✔
132
                    if not isinstance(data, dict):
1✔
133
                        data = {}
1✔
134
                else:
135
                    data = dict(request.args)
1✔
136

137
                try:
1✔
138
                    if kwargs:
1✔
139
                        data.update(kwargs)
1✔
140

141
                    input_filter.data = data
1✔
142
                    input_filter.validated_data = {}
1✔
143
                    input_filter.errors = {}
1✔
144

145
                    g.validated_data = input_filter.validate_data()
1✔
146

147
                except ValidationError as e:
1✔
148
                    return Response(
1✔
149
                        status=400,
150
                        response=json.dumps(e.args[0]),
151
                        mimetype="application/json",
152
                    )
153

154
                except Exception:
×
155
                    logging.exception(
×
156
                        "An unexpected exception occurred while "
157
                        "validating input data.",
158
                    )
159
                    return Response(status=500)
×
160

161
                return f(*args, **kwargs)
1✔
162

163
            return wrapper
1✔
164

165
        return decorator
1✔
166

167
    def validate_data(
1✔
168
        self, data: Optional[dict[str, Any]] = None
169
    ) -> Union[dict[str, Any], Type[T]]:
170
        """
171
        Validates input data against defined field rules, including applying
172
        filters, validators, custom logic steps, and fallback mechanisms. The
173
        validation process also ensures the required fields are handled
174
        appropriately and conditions are checked after processing.
175

176
        Args:
177
            data (dict[str, Any]): A dictionary containing the input data to
178
                be validated where keys represent field names and values
179
                represent the corresponding data.
180

181
        Returns:
182
            Union[dict[str, Any], Type[T]]: A dictionary containing the
183
                validated data with any modifications, default values,
184
                or processed values as per the defined validation rules.
185

186
        Raises:
187
            Any errors raised during external API calls, validation, or
188
                logical steps execution of the respective fields or conditions
189
                will propagate without explicit handling here.
190
        """
191
        data = data or self.data
1✔
192

193
        validated_data, errors = DataMixin.validate_with_conditions(
1✔
194
            self.fields,
195
            data,
196
            self.global_filters,
197
            self.global_validators,
198
            self.conditions,
199
        )
200

201
        if errors:
1✔
202
            raise ValidationError(errors)
1✔
203

204
        self.validated_data = validated_data
1✔
205
        return self._serialize()
1✔
206

207
    def add_condition(self, condition: BaseCondition) -> None:
1✔
208
        """
209
        Add a condition to the input filter.
210

211
        Args:
212
            condition (BaseCondition): The condition to add.
213
        """
214
        self.conditions.append(condition)
1✔
215

216
    def _register_decorator_components(self) -> None:
1✔
217
        """Register decorator-based components from the current class and
218
        inheritance chain."""
219
        cls = self.__class__
1✔
220
        dir_attrs = dir(cls)
1✔
221

222
        for attr_name in dir_attrs:
1✔
223
            if attr_name.startswith("_"):
1✔
224
                continue
1✔
225
            if hasattr(cls, attr_name):
1✔
226
                attr_value = getattr(cls, attr_name)
1✔
227
                if isinstance(attr_value, FieldDescriptor):
1✔
228
                    self.fields[attr_name] = FieldModel(
1✔
229
                        attr_value.required,
230
                        attr_value.default,
231
                        attr_value.fallback,
232
                        attr_value.filters,
233
                        attr_value.validators,
234
                        attr_value.steps,
235
                        attr_value.external_api,
236
                        attr_value.copy,
237
                    )
238

239
        added_conditions = set()
1✔
240
        added_global_validators = set()
1✔
241
        added_global_filters = set()
1✔
242

243
        for base_cls in reversed(cls.__mro__):
1✔
244
            conditions = getattr(base_cls, "_conditions", None)
1✔
245
            if conditions is not None:
1✔
246
                for condition in conditions:
1✔
247
                    condition_id = id(condition)
1✔
248
                    if condition_id not in added_conditions:
1✔
249
                        self.conditions.append(condition)
1✔
250
                        added_conditions.add(condition_id)
1✔
251

252
            validators = getattr(base_cls, "_global_validators", None)
1✔
253
            if validators is not None:
1✔
254
                for validator in validators:
1✔
255
                    validator_id = id(validator)
1✔
256
                    if validator_id not in added_global_validators:
1✔
257
                        self.global_validators.append(validator)
1✔
258
                        added_global_validators.add(validator_id)
1✔
259

260
            filters = getattr(base_cls, "_global_filters", None)
1✔
261
            if filters is not None:
1✔
262
                for filter_instance in filters:
1✔
263
                    filter_id = id(filter_instance)
1✔
264
                    if filter_id not in added_global_filters:
1✔
265
                        self.global_filters.append(filter_instance)
1✔
266
                        added_global_filters.add(filter_id)
1✔
267

268
        if hasattr(cls, "_model"):
1✔
269
            self.model_class = cls._model
1✔
270

271
    def get_conditions(self) -> list[BaseCondition]:
1✔
272
        """
273
        Retrieve the list of all registered conditions.
274

275
        This function provides access to the conditions that have been
276
        registered and stored. Each condition in the returned list
277
        is represented as an instance of the BaseCondition type.
278

279
        Returns:
280
            list[BaseCondition]: A list containing all currently registered
281
                instances of BaseCondition.
282
        """
283
        return self.conditions
1✔
284

285
    def set_data(self, data: dict[str, Any]) -> None:
1✔
286
        """
287
        Filters and sets the provided data into the object's internal storage,
288
        ensuring that only the specified fields are considered and their values
289
        are processed through defined filters.
290

291
        Parameters:
292
            data (dict[str, Any]):
293
                The input dictionary containing key-value pairs where keys
294
                represent field names and values represent the associated
295
                data to be filtered and stored.
296
        """
297
        self.data = DataMixin.filter_data(
1✔
298
            data,
299
            self.fields,
300
            self.global_filters,
301
        )
302

303
    def get_value(self, name: str) -> Any:
1✔
304
        """
305
        This method retrieves a value associated with the provided name. It
306
        searches for the value based on the given identifier and returns the
307
        corresponding result. If no value is found, it typically returns a
308
        default or fallback output. The method aims to provide flexibility in
309
        retrieving data without explicitly specifying the details of the
310
        underlying implementation.
311

312
        Args:
313
            name (str): A string that represents the identifier for which the
314
                 corresponding value is being retrieved. It is used to perform
315
                 the lookup.
316

317
        Returns:
318
            Any: The retrieved value associated with the given name. The
319
                 specific type of this value is dependent on the
320
                 implementation and the data being accessed.
321
        """
322
        return self.validated_data.get(name)
1✔
323

324
    def get_values(self) -> dict[str, Any]:
1✔
325
        """
326
        Retrieves a dictionary of key-value pairs from the current object. This
327
        method provides access to the internal state or configuration of the
328
        object in a dictionary format, where keys are strings and values can be
329
        of various types depending on the object's design.
330

331
        Returns:
332
            dict[str, Any]: A dictionary containing string keys and their
333
                            corresponding values of any data type.
334
        """
335
        return self.validated_data
1✔
336

337
    def get_raw_value(self, name: str) -> Any:
1✔
338
        """
339
        Fetches the raw value associated with the provided key.
340

341
        This method is used to retrieve the underlying value linked to the
342
        given key without applying any transformations or validations. It
343
        directly fetches the raw stored value and is typically used in
344
        scenarios where the raw data is needed for processing or debugging
345
        purposes.
346

347
        Args:
348
            name (str): The name of the key whose raw value is to be
349
                retrieved.
350

351
        Returns:
352
            Any: The raw value associated with the provided key.
353
        """
354
        return self.data.get(name)
1✔
355

356
    def get_raw_values(self) -> dict[str, Any]:
1✔
357
        """
358
        Retrieves raw values from a given source and returns them as a
359
        dictionary.
360

361
        This method is used to fetch and return unprocessed or raw data in
362
        the form of a dictionary where the keys are strings, representing
363
        the identifiers, and the values are of any data type.
364

365
        Returns:
366
            dict[str, Any]: A dictionary containing the raw values retrieved.
367
               The keys are strings representing the identifiers, and the
368
               values can be of any type, depending on the source
369
               being accessed.
370
        """
371
        if not self.fields:
1✔
372
            return {}
1✔
373

374
        # Use optimized intersection for larger datasets
375
        if len(self.fields) > 10:
1✔
376
            field_set = set(self.fields.keys())
×
377
            data_set = set(self.data.keys())
×
378
            common_fields = field_set & data_set
×
379
            return {field: self.data[field] for field in common_fields}
×
380
        return {
1✔
381
            field: self.data[field]
382
            for field in self.fields
383
            if field in self.data
384
        }
385

386
    def get_unfiltered_data(self) -> dict[str, Any]:
1✔
387
        """
388
        Fetches unfiltered data from the data source.
389

390
        This method retrieves data without any filtering, processing, or
391
        manipulations applied. It is intended to provide raw data that has
392
        not been altered since being retrieved from its source. The usage
393
        of this method should be limited to scenarios where unprocessed data
394
        is required, as it does not perform any validations or checks.
395

396
        Returns:
397
            dict[str, Any]: The unfiltered, raw data retrieved from the
398
                 data source. The return type may vary based on the
399
                 specific implementation of the data source.
400
        """
401
        return self.data
1✔
402

403
    def set_unfiltered_data(self, data: dict[str, Any]) -> None:
1✔
404
        """
405
        Sets unfiltered data for the current instance. This method assigns a
406
        given dictionary of data to the instance for further processing. It
407
        updates the internal state using the provided data.
408

409
        Parameters:
410
            data (dict[str, Any]): A dictionary containing the unfiltered
411
                data to be associated with the instance.
412
        """
413
        self.data = data
1✔
414

415
    def has_unknown(self) -> bool:
1✔
416
        """
417
        Checks whether any values in the current data do not have corresponding
418
        configurations in the defined fields.
419

420
        Returns:
421
            bool: True if there are any unknown fields; False otherwise.
422
        """
423
        return DataMixin.has_unknown_fields(self.data, self.fields)
1✔
424

425
    def get_error_message(self, field_name: str) -> Optional[str]:
1✔
426
        """
427
        Retrieves and returns a predefined error message.
428

429
        This method is intended to provide a consistent error message
430
        to be used across the application when an error occurs. The
431
        message is predefined and does not accept any parameters.
432
        The exact content of the error message may vary based on
433
        specific implementation, but it is designed to convey meaningful
434
        information about the nature of an error.
435

436
        Args:
437
            field_name (str): The name of the field for which the error
438
                message is being retrieved.
439

440
        Returns:
441
            Optional[str]: A string representing the predefined error message.
442
        """
443
        return self.errors.get(field_name)
1✔
444

445
    def get_error_messages(self) -> dict[str, str]:
1✔
446
        """
447
        Retrieves all error messages associated with the fields in the input
448
        filter.
449

450
        This method aggregates and returns a dictionary of error messages
451
        where the keys represent field names, and the values are their
452
        respective error messages.
453

454
        Returns:
455
            dict[str, str]: A dictionary containing field names as keys and
456
                            their corresponding error messages as values.
457
        """
458
        return self.errors
1✔
459

460
    def add(
1✔
461
        self,
462
        name: str,
463
        required: bool = False,
464
        default: Any = None,
465
        fallback: Any = None,
466
        filters: Optional[list[BaseFilter]] = None,
467
        validators: Optional[list[BaseValidator]] = None,
468
        steps: Optional[list[Union[BaseFilter, BaseValidator]]] = None,
469
        external_api: Optional[ExternalApiConfig] = None,
470
        copy: Optional[str] = None,
471
    ) -> None:
472
        """
473
        Add the field to the input filter.
474

475
        Args:
476
            name (str): The name of the field.
477

478
            required (Optional[bool]): Whether the field is required.
479

480
            default (Optional[Any]): The default value of the field.
481

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

485
            filters (Optional[list[BaseFilter]]): The filters to apply to
486
                the field value.
487

488
            validators (Optional[list[BaseValidator]]): The validators to
489
                apply to the field value.
490

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

494
            external_api (Optional[ExternalApiConfig]): Configuration for an
495
                external API call.
496

497
            copy (Optional[str]): The name of the field to copy the value
498
                from.
499
        """
500
        if name in self.fields:
1✔
501
            raise ValueError(f"Field '{name}' already exists.")
1✔
502

503
        self.fields[name] = FieldModel(
1✔
504
            required,
505
            default,
506
            fallback,
507
            filters or [],
508
            validators or [],
509
            steps or [],
510
            external_api,
511
            copy,
512
        )
513

514
    def has(self, field_name: str) -> bool:
1✔
515
        """
516
        This method checks the existence of a specific field within the input
517
        filter values, identified by its field name. It does not return a
518
        value, serving purely as a validation or existence check mechanism.
519

520
        Args:
521
            field_name (str): The name of the field to check for existence.
522

523
        Returns:
524
            bool: True if the field exists in the input filter,
525
                otherwise False.
526
        """
527
        return field_name in self.fields
1✔
528

529
    def get_input(self, field_name: str) -> Optional[FieldModel]:
1✔
530
        """
531
        Represents a method to retrieve a field by its name.
532

533
        This method allows fetching the configuration of a specific field
534
        within the object, using its name as a string. It ensures
535
        compatibility with various field names and provides a generic
536
        return type to accommodate different data types for the fields.
537

538
        Args:
539
            field_name (str): A string representing the name of the field who
540
                        needs to be retrieved.
541

542
        Returns:
543
            Optional[FieldModel]: The field corresponding to the
544
                specified name.
545
        """
546
        return self.fields.get(field_name)
1✔
547

548
    def get_inputs(self) -> dict[str, FieldModel]:
1✔
549
        """
550
        Retrieve the dictionary of input fields associated with the object.
551

552
        Returns:
553
            dict[str, FieldModel]: Dictionary containing field names as
554
                keys and their corresponding FieldModel instances as values
555
        """
556
        return self.fields
1✔
557

558
    def remove(self, field_name: str) -> Optional[FieldModel]:
1✔
559
        """
560
        Removes the specified field from the instance or collection.
561

562
        This method is used to delete a specific field identified by
563
        its name. It ensures the designated field is removed entirely
564
        from the relevant data structure. No value is returned upon
565
        successful execution.
566

567
        Args:
568
            field_name (str): The name of the field to be removed.
569

570
        Returns:
571
            Any: The value of the removed field, if any.
572
        """
573
        return self.fields.pop(field_name, None)
1✔
574

575
    def count(self) -> int:
1✔
576
        """
577
        Counts the total number of elements in the collection.
578

579
        This method returns the total count of elements stored within the
580
        underlying data structure, providing a quick way to ascertain the
581
        size or number of entries available.
582

583
        Returns:
584
            int: The total number of elements in the collection.
585
        """
586
        return len(self.fields)
1✔
587

588
    def replace(
1✔
589
        self,
590
        name: str,
591
        required: bool = False,
592
        default: Any = None,
593
        fallback: Any = None,
594
        filters: Optional[list[BaseFilter]] = None,
595
        validators: Optional[list[BaseValidator]] = None,
596
        steps: Optional[list[Union[BaseFilter, BaseValidator]]] = None,
597
        external_api: Optional[ExternalApiConfig] = None,
598
        copy: Optional[str] = None,
599
    ) -> None:
600
        """
601
        Replaces a field in the input filter.
602

603
        Args:
604
             name (str): The name of the field.
605

606
            required (Optional[bool]): Whether the field is required.
607

608
            default (Optional[Any]): The default value of the field.
609

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

613
            filters (Optional[list[BaseFilter]]): The filters to apply to
614
                the field value.
615

616
            validators (Optional[list[BaseValidator]]): The validators to
617
                apply to the field value.
618

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

622
            external_api (Optional[ExternalApiConfig]): Configuration for an
623
                external API call.
624

625
            copy (Optional[str]): The name of the field to copy the value
626
                from.
627
        """
628
        self.fields[name] = FieldModel(
1✔
629
            required,
630
            default,
631
            fallback,
632
            filters or [],
633
            validators or [],
634
            steps or [],
635
            external_api,
636
            copy,
637
        )
638

639
    def add_global_filter(self, filter: BaseFilter) -> None:
1✔
640
        """
641
        Add a global filter to be applied to all fields.
642

643
        Args:
644
            filter: The filter to add.
645
        """
646
        self.global_filters.append(filter)
1✔
647

648
    def get_global_filters(self) -> list[BaseFilter]:
1✔
649
        """
650
        Retrieve all global filters associated with this InputFilter instance.
651

652
        This method returns a list of BaseFilter instances that have been
653
        added as global filters. These filters are applied universally to
654
        all fields during data processing.
655

656
        Returns:
657
            list[BaseFilter]: A list of global filters.
658
        """
659
        return self.global_filters
1✔
660

661
    def clear(self) -> None:
1✔
662
        """
663
        Resets all fields of the InputFilter instance to their initial empty
664
        state.
665

666
        This method clears the internal storage of fields, conditions, filters,
667
        validators, and data, effectively resetting the object as if it were
668
        newly initialized.
669
        """
670
        self.fields.clear()
1✔
671
        self.conditions.clear()
1✔
672
        self.global_filters.clear()
1✔
673
        self.global_validators.clear()
1✔
674
        self.data.clear()
1✔
675
        self.validated_data.clear()
1✔
676
        self.errors.clear()
1✔
677

678
    def merge(self, other: InputFilter) -> None:
1✔
679
        """
680
        Merges another InputFilter instance intelligently into the current
681
        instance.
682

683
        - Fields with the same name are merged recursively if possible,
684
            otherwise overwritten.
685
        - Conditions are combined and duplicated.
686
        - Global filters and validators are merged without duplicates.
687

688
        Args:
689
            other (InputFilter): The InputFilter instance to merge.
690
        """
691
        if not isinstance(other, InputFilter):
1✔
692
            raise TypeError(
1✔
693
                "Can only merge with another InputFilter instance."
694
            )
695

696
        DataMixin.merge_input_filters(self, other)
1✔
697

698
    def set_model(self, model_class: Type[T]) -> None:
1✔
699
        """
700
        Set the model class for serialization.
701

702
        Args:
703
            model_class (Type[T]): The class to use for serialization.
704
        """
705
        self.model_class = model_class
1✔
706

707
    def _serialize(self) -> Union[dict[str, Any], T]:
1✔
708
        """
709
        Serialize the validated data. If a model class is set, returns an
710
        instance of that class, otherwise returns the raw validated data.
711

712
        Returns:
713
            Union[dict[str, Any], T]: The serialized data.
714
        """
715
        if self.model_class is None:
1✔
716
            return self.validated_data
1✔
717

718
        try:
1✔
719
            return self.model_class(**self.validated_data)
1✔
720
        except TypeError:
1✔
721
            pass
1✔
722

723
        if dataclasses.is_dataclass(self.model_class):
1✔
724
            field_names = {
1✔
725
                f.name for f in dataclasses.fields(self.model_class)
726
            }
NEW
727
        elif hasattr(self.model_class, "__fields__"):
×
NEW
728
            field_names = set(self.model_class.__fields__.keys())
×
NEW
729
        elif hasattr(self.model_class, "__annotations__"):
×
NEW
730
            field_names = set(self.model_class.__annotations__.keys())
×
731
        else:
NEW
732
            sig = inspect.signature(self.model_class.__init__)
×
NEW
733
            field_names = set(sig.parameters.keys()) - {"self"}
×
734

735
        filtered_data = {
1✔
736
            k: v for k, v in self.validated_data.items() if k in field_names
737
        }
738

739
        return self.model_class(**filtered_data)
1✔
740

741
    def add_global_validator(self, validator: BaseValidator) -> None:
1✔
742
        """
743
        Add a global validator to be applied to all fields.
744

745
        Args:
746
            validator (BaseValidator): The validator to add.
747
        """
748
        self.global_validators.append(validator)
1✔
749

750
    def get_global_validators(self) -> list[BaseValidator]:
1✔
751
        """
752
        Retrieve all global validators associated with this InputFilter
753
        instance.
754

755
        This method returns a list of BaseValidator instances that have been
756
        added as global validators. These validators are applied universally
757
        to all fields during validation.
758

759
        Returns:
760
            list[BaseValidator]: A list of global validators.
761
        """
762
        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