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

LeanderCS / flask-inputfilter / #222

29 Mar 2025 09:02AM UTC coverage: 96.986% (-0.06%) from 97.046%
#222

push

coveralls-python

LeanderCS
Fixed unexpected message in error message of IsIntegerValidator

1609 of 1659 relevant lines covered (96.99%)

0.97 hits per line

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

94.04
/flask_inputfilter/InputFilter.py
1
import re
1✔
2
from typing import Any, Callable, Dict, List, Optional, Tuple, Union
1✔
3

4
from flask import Response, g, request
1✔
5
from typing_extensions import final
1✔
6

7
from flask_inputfilter.Condition import BaseCondition
1✔
8
from flask_inputfilter.Exception import ValidationError
1✔
9
from flask_inputfilter.Filter import BaseFilter
1✔
10
from flask_inputfilter.Model import ExternalApiConfig, FieldModel
1✔
11
from flask_inputfilter.Validator import BaseValidator
1✔
12

13
API_PLACEHOLDER_PATTERN = re.compile(r"{{(.*?)}}")
1✔
14

15

16
class InputFilter:
1✔
17
    """
18
    Base class for input filters.
19
    """
20

21
    def __init__(self, methods: Optional[List[str]] = None) -> None:
1✔
22
        self.__methods = methods or ["GET", "POST", "PATCH", "PUT", "DELETE"]
1✔
23
        self.__fields: Dict[str, FieldModel] = {}
1✔
24
        self.__conditions: List[BaseCondition] = []
1✔
25
        self.__global_filters: List[BaseFilter] = []
1✔
26
        self.__global_validators: List[BaseValidator] = []
1✔
27
        self.__data: Dict[str, Any] = {}
1✔
28
        self.__validated_data: Dict[str, Any] = {}
1✔
29
        self.__error_message: str = ""
1✔
30

31
    @final
1✔
32
    def add(
1✔
33
        self,
34
        name: str,
35
        required: bool = False,
36
        default: Any = None,
37
        fallback: Any = None,
38
        filters: Optional[List[BaseFilter]] = None,
39
        validators: Optional[List[BaseValidator]] = None,
40
        steps: Optional[List[Union[BaseFilter, BaseValidator]]] = None,
41
        external_api: Optional[ExternalApiConfig] = None,
42
        copy: Optional[str] = None,
43
    ) -> None:
44
        """
45
        Add the field to the input filter.
46

47
        Args:
48
            name: The name of the field.
49
            required: Whether the field is required.
50
            default: The default value of the field.
51
            fallback: The fallback value of the field, if validations fails
52
                or field None, although it is required .
53
            filters: The filters to apply to the field value.
54
            validators: The validators to apply to the field value.
55
            steps: Allows to apply multiple filters and validators
56
                in a specific order.
57
            external_api: Configuration for an external API call.
58
            copy: The name of the field to copy the value from.
59
        """
60
        self.__fields[name] = FieldModel(
1✔
61
            required=required,
62
            default=default,
63
            fallback=fallback,
64
            filters=filters or [],
65
            validators=validators or [],
66
            steps=steps or [],
67
            external_api=external_api,
68
            copy=copy,
69
        )
70

71
    @final
1✔
72
    def addCondition(self, condition: BaseCondition) -> None:
1✔
73
        """
74
        Add a condition to the input filter.
75

76
        Args:
77
            condition: The condition to add.
78
        """
79
        self.__conditions.append(condition)
1✔
80

81
    @final
1✔
82
    def addGlobalFilter(self, filter: BaseFilter) -> None:
1✔
83
        """
84
        Add a global filter to be applied to all fields.
85

86
        Args:
87
            filter: The filter to add.
88
        """
89
        self.__global_filters.append(filter)
1✔
90

91
    @final
1✔
92
    def addGlobalValidator(self, validator: BaseValidator) -> None:
1✔
93
        """
94
        Add a global validator to be applied to all fields.
95

96
        Args:
97
            validator: The validator to add.
98
        """
99
        self.__global_validators.append(validator)
1✔
100

101
    @final
1✔
102
    def has(self, field_name: str) -> bool:
1✔
103
        """
104
        This method checks the existence of a specific field within the
105
        input filter values, identified by its field name. It does not return a
106
        value, serving purely as a validation or existence check mechanism.
107

108
        Args:
109
            field_name (str): The name of the field to check for existence.
110

111
        Returns:
112
            bool: True if the field exists in the input filter,
113
            otherwise False.
114
        """
115
        return field_name in self.__fields
1✔
116

117
    @final
1✔
118
    def getInput(self, field_name: str) -> FieldModel:
1✔
119
        """
120
        Represents a method to retrieve the value of a field by its name.
121

122
        This method allows fetching the value of a specific field within the
123
        object, using its name as a string. It ensures compatibility with
124
        various field names and provides a generic return type to accommodate
125
        different data types for the fields.
126

127
        Args:
128
            field_name: A string representing the name of the field whose value
129
                        needs to be retrieved.
130

131
        Returns:
132
            Any: The value of the field corresponding to the specified name.
133
        """
134
        return self.__fields.get(field_name)
1✔
135

136
    @final
1✔
137
    def getInputs(self) -> Dict[str, FieldModel]:
1✔
138
        """
139
        Retrieve the dictionary of input fields associated with the object.
140

141
        Returns:
142
            Dict[str, FieldModel]: Dictionary containing field names as
143
                keys and their corresponding FieldModel instances as values
144
        """
145
        return self.__fields
1✔
146

147
    @final
1✔
148
    def remove(self, field_name: str) -> Any:
1✔
149
        """
150
        Removes the specified field from the instance or collection.
151

152
        This method is used to delete a specific field identified by
153
        its name. It ensures the designated field is removed entirely
154
        from the relevant data structure. No value is returned upon
155
        successful execution.
156

157
        Args:
158
            field_name: The name of the field to be removed.
159

160
        Returns:
161
            Any: The value of the removed field, if any.
162
        """
163
        return self.__fields.pop(field_name, None)
1✔
164

165
    @final
1✔
166
    def count(self) -> int:
1✔
167
        """
168
        Counts the total number of elements in the collection.
169

170
        This method returns the total count of elements stored within the
171
        underlying data structure, providing a quick way to ascertain the
172
        size or number of entries available.
173

174
        Returns:
175
            int: The total number of elements in the collection.
176
        """
177
        return len(self.__fields)
1✔
178

179
    @final
1✔
180
    def replace(
1✔
181
        self,
182
        name: str,
183
        required: bool = False,
184
        default: Any = None,
185
        fallback: Any = None,
186
        filters: Optional[List[BaseFilter]] = None,
187
        validators: Optional[List[BaseValidator]] = None,
188
        steps: Optional[List[Union[BaseFilter, BaseValidator]]] = None,
189
        external_api: Optional[ExternalApiConfig] = None,
190
        copy: Optional[str] = None,
191
    ) -> None:
192
        """
193
        Replaces a field in the input filter.
194

195
        Args:
196
            name: The name of the field.
197
            required: Whether the field is required.
198
            default: The default value of the field.
199
            fallback: The fallback value of the field, if validations fails
200
                or field None, although it is required .
201
            filters: The filters to apply to the field value.
202
            validators: The validators to apply to the field value.
203
            steps: Allows to apply multiple filters and validators
204
                in a specific order.
205
            external_api: Configuration for an external API call.
206
            copy: The name of the field to copy the value from.
207
        """
208
        self.__fields[name] = FieldModel(
1✔
209
            required=required,
210
            default=default,
211
            fallback=fallback,
212
            filters=filters or [],
213
            validators=validators or [],
214
            steps=steps or [],
215
            external_api=external_api,
216
            copy=copy,
217
        )
218

219
    @final
1✔
220
    def setData(self, data: Dict[str, Any]) -> None:
1✔
221
        """
222
        Filters and sets the provided data into the object's internal
223
        storage, ensuring that only the specified fields are considered and
224
        their values are processed through defined filters.
225

226
        Parameters:
227
            data:
228
                The input dictionary containing key-value pairs where keys
229
                represent field names and values represent the associated
230
                data to be filtered and stored.
231
        """
232
        filtered_data = {}
1✔
233
        for field_name, field_value in data.items():
1✔
234
            if field_name in self.__fields:
1✔
235
                filtered_data[field_name] = self.__applyFilters(
1✔
236
                    filters=self.__fields[field_name].filters,
237
                    value=field_value,
238
                )
239
            else:
240
                filtered_data[field_name] = field_value
1✔
241

242
        self.__data = filtered_data
1✔
243

244
    @final
1✔
245
    def getErrorMessage(self) -> str:
1✔
246
        """
247
        Retrieves and returns a predefined error message.
248

249
        This method is intended to provide a consistent error message
250
        to be used across the application when an error occurs. The
251
        message is predefined and does not accept any parameters.
252
        The exact content of the error message may vary based on
253
        specific implementation, but it is designed to convey meaningful
254
        information about the nature of an error.
255

256
        Returns:
257
            str: A string representing the predefined error message.
258
        """
259
        return self.__error_message
1✔
260

261
    @final
1✔
262
    def getValue(self, name: str) -> Any:
1✔
263
        """
264
        This method retrieves a value associated with the provided name. It
265
        searches for the value based on the given identifier and returns the
266
        corresponding result. If no value is found, it typically returns a
267
        default or fallback output. The method aims to provide flexibility in
268
        retrieving data without explicitly specifying the details of the
269
        underlying implementation.
270

271
        Args:
272
            name: A string that represents the identifier for which the
273
                 corresponding value is being retrieved. It is used to perform
274
                 the lookup.
275

276
        Returns:
277
            Any: The retrieved value associated with the given name. The
278
                 specific type of this value is dependent on the
279
                 implementation and the data being accessed.
280
        """
281
        return self.__validated_data.get(name)
1✔
282

283
    @final
1✔
284
    def getValues(self) -> Dict[str, Any]:
1✔
285
        """
286
        Retrieves a dictionary of key-value pairs from the current object.
287
        This method provides access to the internal state or configuration of
288
        the object in a dictionary format, where keys are strings and values
289
        can be of various types depending on the object’s design.
290

291
        Returns:
292
            Dict[str, Any]: A dictionary containing string keys and their
293
                            corresponding values of any data type.
294
        """
295
        return self.__validated_data
1✔
296

297
    @final
1✔
298
    def getRawValue(self, name: str) -> Any:
1✔
299
        """
300
        Fetches the raw value associated with the provided key.
301

302
        This method is used to retrieve the underlying value linked to the
303
        given key without applying any transformations or validations. It
304
        directly fetches the raw stored value and is typically used in
305
        scenarios where the raw data is needed for processing or debugging
306
        purposes.
307

308
        Args:
309
            name: The name of the key whose raw value is to be retrieved.
310

311
        Returns:
312
            Any: The raw value associated with the provided key.
313
        """
314
        return self.__data.get(name) if name in self.__data else None
1✔
315

316
    @final
1✔
317
    def getRawValues(self) -> Dict[str, Any]:
1✔
318
        """
319
        Retrieves raw values from a given source and returns them as a
320
        dictionary.
321

322
        This method is used to fetch and return unprocessed or raw data in
323
        the form of a dictionary where the keys are strings, representing
324
        the identifiers, and the values are of any data type.
325

326
        Returns:
327
            Dict[str, Any]: A dictionary containing the raw values retrieved.
328
               The keys are strings representing the identifiers, and the
329
               values can be of any type, depending on the source
330
               being accessed.
331
        """
332
        if not self.__fields:
1✔
333
            return {}
×
334

335
        return {
1✔
336
            field: self.__data[field]
337
            for field in self.__fields
338
            if field in self.__data
339
        }
340

341
    @final
1✔
342
    def getUnfilteredData(self) -> Dict[str, Any]:
1✔
343
        """
344
        Fetches unfiltered data from the data source.
345

346
        This method retrieves data without any filtering, processing, or
347
        manipulations applied. It is intended to provide raw data that has
348
        not been altered since being retrieved from its source. The usage
349
        of this method should be limited to scenarios where unprocessed data
350
        is required, as it does not perform any validations or checks.
351

352
        Returns:
353
            Dict[str, Any]: The unfiltered, raw data retrieved from the
354
                 data source. The return type may vary based on the
355
                 specific implementation of the data source.
356
        """
357
        return self.__data
1✔
358

359
    @final
1✔
360
    def setUnfilteredData(self, data: Dict[str, Any]) -> None:
1✔
361
        """
362
        Sets unfiltered data for the current instance. This method assigns a
363
        given dictionary of data to the instance for further processing. It
364
        updates the internal state using the provided data.
365

366
        Parameters:
367
            data: A dictionary containing the unfiltered
368
                data to be associated with the instance.
369
        """
370
        self.__data = data
1✔
371

372
    @final
1✔
373
    def hasUnknown(self) -> bool:
1✔
374
        """
375
        Checks whether any values in the current data do not have
376
        corresponding configurations in the defined fields.
377

378
        Returns:
379
            bool: True if there are any unknown fields; False otherwise.
380
        """
381
        if not self.__data and self.__fields:
1✔
382
            return True
1✔
383
        return any(
1✔
384
            field_name not in self.__fields.keys()
385
            for field_name in self.__data.keys()
386
        )
387

388
    @final
1✔
389
    def merge(self, other: "InputFilter") -> None:
1✔
390
        """
391
        Merges another InputFilter instance intelligently into the current
392
        instance.
393

394
        - Fields with the same name are merged recursively if possible,
395
            otherwise overwritten.
396
        - Conditions are combined and deduplicated.
397
        - Global filters and validators are merged without duplicates.
398

399
        Args:
400
            other (InputFilter): The InputFilter instance to merge.
401
        """
402
        if not isinstance(other, InputFilter):
1✔
403
            raise TypeError(
×
404
                "Can only merge with another InputFilter instance."
405
            )
406

407
        for key, new_field in other.getInputs().items():
1✔
408
            if key not in self.__fields:
1✔
409
                self.__fields[key] = new_field
1✔
410
                continue
1✔
411

412
            existing_field = self.__fields[key]
×
413
            if not isinstance(existing_field, InputFilter) or not isinstance(
×
414
                new_field, InputFilter
415
            ):
416
                self.__fields[key] = new_field
×
417

418
            existing_field.merge(new_field)
×
419

420
        self.__conditions = list(set(self.__conditions + other.__conditions))
1✔
421

422
        existing_global_filters = set(self.__global_filters)
1✔
423
        for filter in other.__global_filters:
1✔
424
            if filter not in existing_global_filters:
×
425
                self.__global_filters.append(filter)
×
426
                existing_global_filters.add(filter)
×
427

428
        existing_global_validators = set(self.__global_validators)
1✔
429
        for validator in other.__global_validators:
1✔
430
            if validator not in existing_global_validators:
×
431
                self.__global_validators.append(validator)
×
432
                existing_global_validators.add(validator)
×
433

434
    @final
1✔
435
    def isValid(self) -> bool:
1✔
436
        """
437
        Checks if the object's state or its attributes meet certain
438
        conditions to be considered valid. This function is typically used to
439
        ensure that the current state complies with specific requirements or
440
        rules.
441

442
        Returns:
443
            bool: Returns True if the state or attributes of the object fulfill
444
                all required conditions; otherwise, returns False.
445
        """
446
        try:
1✔
447
            self.validateData(self.__data)
1✔
448

449
        except (ValidationError, Exception) as e:
1✔
450
            self.__error_message = str(e)
1✔
451
            return False
1✔
452

453
        return True
1✔
454

455
    @final
1✔
456
    def validateData(
1✔
457
        self, data: Optional[Dict[str, Any]] = None
458
    ) -> Dict[str, Any]:
459
        """
460
        Validates input data against defined field rules, including applying
461
        filters, validators, custom logic steps, and fallback mechanisms. The
462
        validation process also ensures the required fields are handled
463
        appropriately and conditions are checked after processing.
464

465
        Args:
466
            data (Dict[str, Any]): A dictionary containing the input data to
467
                be validated where keys represent field names and values
468
                represent the corresponding data.
469

470
        Returns:
471
            Dict[str, Any]: A dictionary containing the validated data with
472
                any modifications, default values, or processed values as
473
                per the defined validation rules.
474

475
        Raises:
476
            Any errors raised during external API calls, validation, or
477
                logical steps execution of the respective fields or conditions
478
                will propagate without explicit handling here.
479
        """
480
        validated_data = self.__validated_data
1✔
481
        data = data or self.__data
1✔
482

483
        for field_name, field_info in self.__fields.items():
1✔
484
            value = data.get(field_name)
1✔
485

486
            required = field_info.required
1✔
487
            default = field_info.default
1✔
488
            fallback = field_info.fallback
1✔
489
            filters = field_info.filters
1✔
490
            validators = field_info.validators
1✔
491
            steps = field_info.steps
1✔
492
            external_api = field_info.external_api
1✔
493
            copy = field_info.copy
1✔
494

495
            if copy:
1✔
496
                value = validated_data.get(copy)
1✔
497

498
            if external_api:
1✔
499
                value = self.__callExternalApi(
1✔
500
                    external_api, fallback, validated_data
501
                )
502

503
            value = self.__applyFilters(filters, value)
1✔
504

505
            value = self.__validateField(validators, fallback, value) or value
1✔
506

507
            value = self.__applySteps(steps, fallback, value) or value
1✔
508

509
            value = self.__checkForRequired(
1✔
510
                field_name, required, default, fallback, value
511
            )
512

513
            validated_data[field_name] = value
1✔
514

515
        self.__checkConditions(validated_data)
1✔
516

517
        self.__validated_data = validated_data
1✔
518

519
        return validated_data
1✔
520

521
    @classmethod
1✔
522
    @final
1✔
523
    def validate(
1✔
524
        cls,
525
    ) -> Callable[
526
        [Any],
527
        Callable[
528
            [Tuple[Any, ...], Dict[str, Any]],
529
            Union[Response, Tuple[Any, Dict[str, Any]]],
530
        ],
531
    ]:
532
        """
533
        Decorator for validating input data in routes.
534
        """
535

536
        def decorator(
1✔
537
            f,
538
        ) -> Callable[
539
            [Tuple[Any, ...], Dict[str, Any]],
540
            Union[Response, Tuple[Any, Dict[str, Any]]],
541
        ]:
542
            def wrapper(
1✔
543
                *args, **kwargs
544
            ) -> Union[Response, Tuple[Any, Dict[str, Any]]]:
545
                input_filter = cls()
1✔
546
                if request.method not in input_filter.__methods:
1✔
547
                    return Response(status=405, response="Method Not Allowed")
×
548

549
                data = request.json if request.is_json else request.args
1✔
550

551
                try:
1✔
552
                    kwargs = kwargs or {}
1✔
553

554
                    input_filter.__data = {**data, **kwargs}
1✔
555

556
                    g.validated_data = input_filter.validateData()
1✔
557

558
                except ValidationError as e:
1✔
559
                    return Response(status=400, response=str(e))
1✔
560

561
                return f(*args, **kwargs)
1✔
562

563
            return wrapper
1✔
564

565
        return decorator
1✔
566

567
    def __applyFilters(self, filters: List[BaseFilter], value: Any) -> Any:
1✔
568
        """
569
        Apply filters to the field value.
570
        """
571
        if value is None:
1✔
572
            return value
1✔
573

574
        for filter_ in self.__global_filters + filters:
1✔
575
            value = filter_.apply(value)
1✔
576

577
        return value
1✔
578

579
    def __validateField(
1✔
580
        self, validators: List[BaseValidator], fallback: Any, value: Any
581
    ) -> None:
582
        """
583
        Validate the field value.
584
        """
585
        if value is None:
1✔
586
            return
1✔
587

588
        try:
1✔
589
            for validator in self.__global_validators + validators:
1✔
590
                validator.validate(value)
1✔
591
        except ValidationError:
1✔
592
            if fallback is None:
1✔
593
                raise
1✔
594

595
            return fallback
1✔
596

597
    @staticmethod
1✔
598
    def __applySteps(
1✔
599
        steps: List[Union[BaseFilter, BaseValidator]],
600
        fallback: Any,
601
        value: Any,
602
    ) -> Any:
603
        """
604
        Apply multiple filters and validators in a specific order.
605
        """
606
        if value is None:
1✔
607
            return
1✔
608

609
        try:
1✔
610
            for step in steps:
1✔
611
                if isinstance(step, BaseFilter):
1✔
612
                    value = step.apply(value)
1✔
613
                elif isinstance(step, BaseValidator):
1✔
614
                    step.validate(value)
1✔
615
        except ValidationError:
1✔
616
            if fallback is None:
1✔
617
                raise
1✔
618
            return fallback
1✔
619
        return value
1✔
620

621
    def __callExternalApi(
1✔
622
        self, config: ExternalApiConfig, fallback: Any, validated_data: dict
623
    ) -> Optional[Any]:
624
        """
625
        Makes a call to an external API using provided configuration and
626
        returns the response.
627

628
        Summary:
629
        The function constructs a request based on the given API
630
        configuration and validated data, including headers, parameters,
631
        and other request settings. It utilizes the `requests` library
632
        to send the API call and processes the response. If a fallback
633
        value is supplied, it is returned in case of any failure during
634
        the API call. If no fallback is provided, a validation error is
635
        raised.
636

637
        Parameters:
638
            config:
639
                An object containing the configuration details for the
640
                external API call, such as URL, headers, method, and API key.
641
            fallback:
642
                The value to be returned in case the external API call fails.
643
            validated_data:
644
                The dictionary containing data used to replace placeholders
645
                in the URL and parameters of the API request.
646

647
        Returns:
648
            Optional[Any]:
649
                The JSON-decoded response from the API, or the fallback
650
                value if the call fails and a fallback is provided.
651

652
        Raises:
653
            ValidationError
654
                Raised if the external API call does not succeed and no
655
                fallback value is provided.
656
        """
657
        import requests
1✔
658

659
        requestData = {
1✔
660
            "headers": {},
661
            "params": {},
662
        }
663

664
        if config.api_key:
1✔
665
            requestData["headers"]["Authorization"] = (
1✔
666
                f"Bearer " f"{config.api_key}"
667
            )
668

669
        if config.headers:
1✔
670
            requestData["headers"].update(config.headers)
1✔
671

672
        if config.params:
1✔
673
            requestData["params"] = self.__replacePlaceholdersInParams(
1✔
674
                config.params, validated_data
675
            )
676

677
        requestData["url"] = self.__replacePlaceholders(
1✔
678
            config.url, validated_data
679
        )
680
        requestData["method"] = config.method
1✔
681

682
        try:
1✔
683
            response = requests.request(**requestData)
1✔
684

685
            if response.status_code != 200:
1✔
686
                raise ValidationError(
1✔
687
                    f"External API call failed with "
688
                    f"status code {response.status_code}"
689
                )
690

691
            result = response.json()
1✔
692

693
            data_key = config.data_key
1✔
694
            if data_key:
1✔
695
                return result.get(data_key)
1✔
696

697
            return result
×
698
        except Exception as e:
1✔
699
            if fallback is None:
1✔
700
                self.__error_message = str(e)
1✔
701

702
                raise ValidationError(
1✔
703
                    f"External API call failed for field "
704
                    f"'{config.data_key}'."
705
                )
706

707
            return fallback
1✔
708

709
    @staticmethod
1✔
710
    def __replacePlaceholders(value: str, validated_data: dict) -> str:
1✔
711
        """
712
        Replace all placeholders, marked with '{{ }}' in value
713
        with the corresponding values from validated_data.
714
        """
715
        return API_PLACEHOLDER_PATTERN.sub(
1✔
716
            lambda match: str(validated_data.get(match.group(1))),
717
            value,
718
        )
719

720
    def __replacePlaceholdersInParams(
1✔
721
        self, params: dict, validated_data: dict
722
    ) -> dict:
723
        """
724
        Replace all placeholders in params with the corresponding
725
        values from validated_data.
726
        """
727
        return {
1✔
728
            key: self.__replacePlaceholders(value, validated_data)
729
            if isinstance(value, str)
730
            else value
731
            for key, value in params.items()
732
        }
733

734
    @staticmethod
1✔
735
    def __checkForRequired(
1✔
736
        field_name: str,
737
        required: bool,
738
        default: Any,
739
        fallback: Any,
740
        value: Any,
741
    ) -> Any:
742
        """
743
        Determine the value of the field, considering the required and
744
        fallback attributes.
745

746
        If the field is not required and no value is provided, the default
747
        value is returned. If the field is required and no value is provided,
748
        the fallback value is returned. If no of the above conditions are met,
749
        a ValidationError is raised.
750
        """
751
        if value is not None:
1✔
752
            return value
1✔
753

754
        if not required:
1✔
755
            return default
1✔
756

757
        if fallback is not None:
1✔
758
            return fallback
1✔
759

760
        raise ValidationError(f"Field '{field_name}' is required.")
1✔
761

762
    def __checkConditions(self, validated_data: dict) -> None:
1✔
763
        for condition in self.__conditions:
1✔
764
            if not condition.check(validated_data):
1✔
765
                raise ValidationError(f"Condition '{condition}' not met.")
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