Skip to content

validator

mlte/validation/validator.py

The validation base class.

Validator

Class that represents a validation, including condition, and results for success or failure.

Source code in mlte/validation/validator.py
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
class Validator:
    """
    Class that represents a validation, including condition, and results for success or failure.
    """

    def __init__(
        self,
        *,
        bool_exp: Optional[Callable[[Any], bool]] = None,
        success: Optional[str] = None,
        failure: Optional[str] = None,
        info: Optional[str] = None,
        creator: Optional[FunctionInfo] = None,
    ):
        """
        Constructor.

        :param bool_exp: A boolean expression that can be used to test the actual condition we want to validate.
        :param success: A string indicating the message to record in case of success (bool_exp evaluating to True).
        :param failure: A string indicating the message to record in case of failure (bool_exp evaluating to False).
        :param info: A string indicating the message to record in case no bool expression is passed (no condition, just recording information).
        :param creator: Information about the class and method that created this validator, if any.
        """
        if success is not None and failure is None:
            raise ValueError(
                "If success message is defined, failure message has to be defined as well"
            )
        if success is None and failure is not None:
            raise ValueError(
                "If failure message is defined, success message has to be defined as well"
            )
        if success is None and failure is None and info is None:
            raise ValueError(
                "All messages can't be empty, either info or success and failure have to be defined"
            )

        self.bool_exp = bool_exp
        self.success = success
        self.failure = failure
        self.info = info
        self.creator = creator

        self.bool_exp_str = (
            reflection.get_lambda_code(bool_exp)
            if bool_exp is not None
            else None
        )
        """We also store the bool expression as a string from its code, for tracking purposes."""

    @staticmethod
    def build_validator(
        bool_exp: Optional[Callable[[Any], bool]] = None,
        success: Optional[str] = None,
        failure: Optional[str] = None,
        info: Optional[str] = None,
        caller_function: Optional[FrameType] = None,
    ) -> Validator:
        """
        Creates a Validator using the provided test, extracting context info from the function that called us.

        :param bool_exp: A boolean expression that can be used to test the actual condition we want to validate.
        :param success: A string indicating the message to record in case of success (bool_exp evaluating to True).
        :param failure: A string indicating the message to record in case of failure (bool_exp evaluating to False).
        :param info: A string indicating the message to record in case no bool expression is passed (no condition, just recording information).
        :param caller_function: A FrameType with data about function that originally called this function. SHOULD BE REMOVED WHEN CONDITIONS ARE.

        :returns: A Validator, potentially with caller creator information.
        """
        # Get function info, passing our caller as argument.
        if caller_function is None:
            curr_frame = inspect.currentframe()
            caller_function = (
                curr_frame.f_back if curr_frame is not None else None
            )
        function_info = FunctionInfo.get_function_info(caller_function)

        # Build the validator. We can't really check at this point if the bool_exp actually returns a bool.
        validator = Validator(
            bool_exp=bool_exp,
            success=success,
            failure=failure,
            info=info,
            creator=function_info,
        )
        return validator

    def validate(self, *args, **kwargs) -> Result:
        """
        Generates a result based on the arguments received, and the configured attributes in the Validator.

        :param args, kwargs: Arguments to pass to the boolean expression to be evaluated in this specific case.
        :return: A Result, including a message with details about the validation result.
        """
        if self.bool_exp is None and self.info is None:
            raise RuntimeError(
                "Can't validate, Validator has no bool expression and is also missing informational message that is used in those cases."
            )

        # First execute bool expression (if any), and get its boolean result.
        executed_bool_exp_value: Optional[bool] = None
        if self.bool_exp is not None:
            executed_bool_exp_value = self.bool_exp(*args, **kwargs)
            if not isinstance(executed_bool_exp_value, bool):
                raise ValueError(
                    "Configured bool expression does not return a bool."
                )

        # Create the result to be returned.
        values_str = self._args_to_string(*args, **kwargs)
        result = (
            Info(self.info)
            if self.bool_exp is None and self.info is not None
            else (
                Success(f"{self.success} {values_str}")
                if executed_bool_exp_value
                else Failure(f"{self.failure} {values_str}")
            )
        )
        return result

    def _args_to_string(self, *args, **kwargs) -> str:
        """
        Stringify arguments so that result's message can include generic information about arguments used when validating.
        """
        # First ensure args are turned to string separately, to allow them to use their own str()
        MAX_STRING_LENGTH = 300
        str_args = [
            (
                str(arg)
                if len(str(arg)) < MAX_STRING_LENGTH
                else str(arg)[:MAX_STRING_LENGTH] + "..."
            )
            for arg in args
        ]
        str_kwargs = {key: str(arg) for key, arg in kwargs.items()}

        # Now string them together, depending on whether we got args of each type.
        values = "- values: "
        if len(args) > 0:
            values = values + f"{json.dumps(str_args)}"
        if len(args) > 0 and len(kwargs) > 0:
            values = values + f"{', '}"
        if len(kwargs) > 0:
            values = values + f"{json.dumps(str_kwargs)}"

        return values

    # -------------------------------------------------------------------------
    # Model handling.
    # -------------------------------------------------------------------------

    def to_model(self) -> ValidatorModel:
        """
        Returns this validator as a model.

        :return: The serialized model object.
        """
        return ValidatorModel(
            bool_exp=(
                serializing.encode_callable(self.bool_exp)
                if self.bool_exp
                else None
            ),
            success=self.success,
            failure=self.failure,
            info=self.info,
            bool_exp_str=self.bool_exp_str,
            creator_class=(
                self.creator.function_class
                if self.creator is not None
                else None
            ),
            creator_function=(
                self.creator.function_name if self.creator is not None else None
            ),
            creator_args=(
                self.creator.arguments if self.creator is not None else []
            ),
        )

    @classmethod
    def from_model(cls, model: ValidatorModel) -> Validator:
        """
        Deserialize a Validator from a model.

        :param model: The model.

        :return: The deserialized Validator
        """
        validator: Validator = Validator(
            bool_exp=(
                typing.cast(
                    Callable[[Any], bool],
                    serializing.decode_callable(model.bool_exp),
                )
                if model.bool_exp
                else None
            ),
            success=model.success,
            failure=model.failure,
            info=model.info,
            creator=(
                FunctionInfo(
                    model.creator_function,
                    model.creator_args,
                    model.creator_class,
                )
                if model.creator_function is not None
                and model.creator_class is not None
                else None
            ),
        )
        return validator

    # -------------------------------------------------------------------------
    # Equality Testing
    # -------------------------------------------------------------------------

    def __eq__(self, other: object) -> bool:
        """Compare Validator instances for equality."""
        if not isinstance(other, Validator):
            return False
        reference: Validator = other
        return self.to_model() == reference.to_model()

bool_exp_str = reflection.get_lambda_code(bool_exp) if bool_exp is not None else None instance-attribute

We also store the bool expression as a string from its code, for tracking purposes.

__eq__(other)

Compare Validator instances for equality.

Source code in mlte/validation/validator.py
239
240
241
242
243
244
def __eq__(self, other: object) -> bool:
    """Compare Validator instances for equality."""
    if not isinstance(other, Validator):
        return False
    reference: Validator = other
    return self.to_model() == reference.to_model()

__init__(*, bool_exp=None, success=None, failure=None, info=None, creator=None)

Constructor.

Parameters:

Name Type Description Default
bool_exp Optional[Callable[[Any], bool]]

A boolean expression that can be used to test the actual condition we want to validate.

None
success Optional[str]

A string indicating the message to record in case of success (bool_exp evaluating to True).

None
failure Optional[str]

A string indicating the message to record in case of failure (bool_exp evaluating to False).

None
info Optional[str]

A string indicating the message to record in case no bool expression is passed (no condition, just recording information).

None
creator Optional[FunctionInfo]

Information about the class and method that created this validator, if any.

None
Source code in mlte/validation/validator.py
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
def __init__(
    self,
    *,
    bool_exp: Optional[Callable[[Any], bool]] = None,
    success: Optional[str] = None,
    failure: Optional[str] = None,
    info: Optional[str] = None,
    creator: Optional[FunctionInfo] = None,
):
    """
    Constructor.

    :param bool_exp: A boolean expression that can be used to test the actual condition we want to validate.
    :param success: A string indicating the message to record in case of success (bool_exp evaluating to True).
    :param failure: A string indicating the message to record in case of failure (bool_exp evaluating to False).
    :param info: A string indicating the message to record in case no bool expression is passed (no condition, just recording information).
    :param creator: Information about the class and method that created this validator, if any.
    """
    if success is not None and failure is None:
        raise ValueError(
            "If success message is defined, failure message has to be defined as well"
        )
    if success is None and failure is not None:
        raise ValueError(
            "If failure message is defined, success message has to be defined as well"
        )
    if success is None and failure is None and info is None:
        raise ValueError(
            "All messages can't be empty, either info or success and failure have to be defined"
        )

    self.bool_exp = bool_exp
    self.success = success
    self.failure = failure
    self.info = info
    self.creator = creator

    self.bool_exp_str = (
        reflection.get_lambda_code(bool_exp)
        if bool_exp is not None
        else None
    )
    """We also store the bool expression as a string from its code, for tracking purposes."""

build_validator(bool_exp=None, success=None, failure=None, info=None, caller_function=None) staticmethod

Creates a Validator using the provided test, extracting context info from the function that called us.

Parameters:

Name Type Description Default
bool_exp Optional[Callable[[Any], bool]]

A boolean expression that can be used to test the actual condition we want to validate.

None
success Optional[str]

A string indicating the message to record in case of success (bool_exp evaluating to True).

None
failure Optional[str]

A string indicating the message to record in case of failure (bool_exp evaluating to False).

None
info Optional[str]

A string indicating the message to record in case no bool expression is passed (no condition, just recording information).

None
caller_function Optional[FrameType]

A FrameType with data about function that originally called this function. SHOULD BE REMOVED WHEN CONDITIONS ARE.

None

Returns:

Type Description
Validator

A Validator, potentially with caller creator information.

Source code in mlte/validation/validator.py
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
@staticmethod
def build_validator(
    bool_exp: Optional[Callable[[Any], bool]] = None,
    success: Optional[str] = None,
    failure: Optional[str] = None,
    info: Optional[str] = None,
    caller_function: Optional[FrameType] = None,
) -> Validator:
    """
    Creates a Validator using the provided test, extracting context info from the function that called us.

    :param bool_exp: A boolean expression that can be used to test the actual condition we want to validate.
    :param success: A string indicating the message to record in case of success (bool_exp evaluating to True).
    :param failure: A string indicating the message to record in case of failure (bool_exp evaluating to False).
    :param info: A string indicating the message to record in case no bool expression is passed (no condition, just recording information).
    :param caller_function: A FrameType with data about function that originally called this function. SHOULD BE REMOVED WHEN CONDITIONS ARE.

    :returns: A Validator, potentially with caller creator information.
    """
    # Get function info, passing our caller as argument.
    if caller_function is None:
        curr_frame = inspect.currentframe()
        caller_function = (
            curr_frame.f_back if curr_frame is not None else None
        )
    function_info = FunctionInfo.get_function_info(caller_function)

    # Build the validator. We can't really check at this point if the bool_exp actually returns a bool.
    validator = Validator(
        bool_exp=bool_exp,
        success=success,
        failure=failure,
        info=info,
        creator=function_info,
    )
    return validator

from_model(model) classmethod

Deserialize a Validator from a model.

Parameters:

Name Type Description Default
model ValidatorModel

The model.

required

Returns:

Type Description
Validator

The deserialized Validator

Source code in mlte/validation/validator.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
@classmethod
def from_model(cls, model: ValidatorModel) -> Validator:
    """
    Deserialize a Validator from a model.

    :param model: The model.

    :return: The deserialized Validator
    """
    validator: Validator = Validator(
        bool_exp=(
            typing.cast(
                Callable[[Any], bool],
                serializing.decode_callable(model.bool_exp),
            )
            if model.bool_exp
            else None
        ),
        success=model.success,
        failure=model.failure,
        info=model.info,
        creator=(
            FunctionInfo(
                model.creator_function,
                model.creator_args,
                model.creator_class,
            )
            if model.creator_function is not None
            and model.creator_class is not None
            else None
        ),
    )
    return validator

to_model()

Returns this validator as a model.

Returns:

Type Description
ValidatorModel

The serialized model object.

Source code in mlte/validation/validator.py
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
def to_model(self) -> ValidatorModel:
    """
    Returns this validator as a model.

    :return: The serialized model object.
    """
    return ValidatorModel(
        bool_exp=(
            serializing.encode_callable(self.bool_exp)
            if self.bool_exp
            else None
        ),
        success=self.success,
        failure=self.failure,
        info=self.info,
        bool_exp_str=self.bool_exp_str,
        creator_class=(
            self.creator.function_class
            if self.creator is not None
            else None
        ),
        creator_function=(
            self.creator.function_name if self.creator is not None else None
        ),
        creator_args=(
            self.creator.arguments if self.creator is not None else []
        ),
    )

validate(*args, **kwargs)

Generates a result based on the arguments received, and the configured attributes in the Validator.

Parameters:

Name Type Description Default
kwargs args,

Arguments to pass to the boolean expression to be evaluated in this specific case.

{}

Returns:

Type Description
Result

A Result, including a message with details about the validation result.

Source code in mlte/validation/validator.py
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
def validate(self, *args, **kwargs) -> Result:
    """
    Generates a result based on the arguments received, and the configured attributes in the Validator.

    :param args, kwargs: Arguments to pass to the boolean expression to be evaluated in this specific case.
    :return: A Result, including a message with details about the validation result.
    """
    if self.bool_exp is None and self.info is None:
        raise RuntimeError(
            "Can't validate, Validator has no bool expression and is also missing informational message that is used in those cases."
        )

    # First execute bool expression (if any), and get its boolean result.
    executed_bool_exp_value: Optional[bool] = None
    if self.bool_exp is not None:
        executed_bool_exp_value = self.bool_exp(*args, **kwargs)
        if not isinstance(executed_bool_exp_value, bool):
            raise ValueError(
                "Configured bool expression does not return a bool."
            )

    # Create the result to be returned.
    values_str = self._args_to_string(*args, **kwargs)
    result = (
        Info(self.info)
        if self.bool_exp is None and self.info is not None
        else (
            Success(f"{self.success} {values_str}")
            if executed_bool_exp_value
            else Failure(f"{self.failure} {values_str}")
        )
    )
    return result