From fbb6004451a9b99d2064f9255fe7ebad49dd9fa5 Mon Sep 17 00:00:00 2001 From: joocer Date: Thu, 28 Sep 2023 18:59:34 +0100 Subject: [PATCH] 1.7.0 --- README.md | 17 +++++++++++++++ data_expectations/__init__.py | 2 +- data_expectations/internals/expectations.py | 24 ++++++++++----------- data_expectations/internals/models.py | 16 ++++++++++++++ data_expectations/version.py | 2 +- tests/test_documentation.py | 16 ++++++++++++++ tests/test_test_value.py | 20 +++++++++++++++++ 7 files changed, 83 insertions(+), 14 deletions(-) create mode 100644 tests/test_test_value.py diff --git a/README.md b/README.md index d21b93d..af4f1d2 100644 --- a/README.md +++ b/README.md @@ -52,6 +52,8 @@ Data Expectations has no external dependencies, can be used ad hoc and in-the-mo ## Example Usage +Testing Python Dictionaries + ~~~python import data_expectations as de from data_expectations import Expectation @@ -71,3 +73,18 @@ try: except de.errors.ExpectationNotMetError: # pragma: no cover print("Data Didn't Meet Expectations") ~~~ + +Testing individual Values: + +~~~python +import data_expectations as de +from data_expectations import Expectation +from data_expectations import Behaviors + +expectation = Expectation(Behaviors.EXPECT_COLUMN_VALUES_TO_BE_BETWEEN, column="age", config={"minimum": 0, "maximum": 120}) + +try: + expectation.test_value(55) +except de.errors.ExpectationNotMetError: # pragma: no cover + print("Data Didn't Meet Expectations") +~~~ \ No newline at end of file diff --git a/data_expectations/__init__.py b/data_expectations/__init__.py index a7c9445..7c1f2bd 100644 --- a/data_expectations/__init__.py +++ b/data_expectations/__init__.py @@ -14,7 +14,7 @@ class Behaviors(str, Enum): EXPECT_COLUMN_VALUES_TO_BE_IN_SET = "expect_column_values_to_be_in_set" EXPECT_COLUMN_VALUES_TO_MATCH_REGEX = "expect_column_values_to_match_regex" EXPECT_COLUMN_VALUES_TO_MATCH_LIKE = "expect_column_values_to_match_like" - EXPECT_COLUMN_VALUES_LENGTH_TO_BE_BE = "expect_column_values_length_to_be_be" + EXPECT_COLUMN_VALUES_LENGTH_TO_BE = "expect_column_values_length_to_be" EXPECT_COLUMN_VALUES_LENGTH_TO_BE_BETWEEN = "expect_column_values_length_to_be_between" diff --git a/data_expectations/internals/expectations.py b/data_expectations/internals/expectations.py index f1f85bc..de2461d 100644 --- a/data_expectations/internals/expectations.py +++ b/data_expectations/internals/expectations.py @@ -166,7 +166,7 @@ def expect_column_values_to_be_of_type( True if the type matches or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return type(value).__name__ == expected_type return ignore_nulls @@ -196,7 +196,7 @@ def expect_column_values_to_be_in_type_list( True if the type is in the type list or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return type(value).__name__ in type_list return ignore_nulls @@ -229,7 +229,7 @@ def expect_column_values_to_be_between( True if the value is between the two bounds or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return value >= minimum and value <= maximum return ignore_nulls @@ -260,7 +260,7 @@ def expect_column_values_to_be_increasing( True if the current value is greater than or equal to the previous value or if the value is null and ignore_nulls is True. False otherwise. """ value = row.get(column) - if value: + if value is not None: return previous_value is None or previous_value <= value return ignore_nulls @@ -291,7 +291,7 @@ def expect_column_values_to_be_decreasing( True if the current value is less than or equal to the previous value or if the value is null and ignore_nulls is True. False otherwise. """ value = row.get(column) - if value: + if value is not None: return previous_value is None or previous_value >= value return ignore_nulls @@ -321,7 +321,7 @@ def expect_column_values_to_be_in_set( True if the value is in the provided set or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return value in symbols return ignore_nulls @@ -351,7 +351,7 @@ def expect_column_values_to_match_regex( True if the value matches the regex or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return re.compile(regex).match(str(value)) is not None return ignore_nulls @@ -381,7 +381,7 @@ def expect_column_values_to_match_like( True if the value matches the pattern or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return sql_like_to_regex(like).match(str(value)) is not None return ignore_nulls @@ -411,7 +411,7 @@ def expect_column_values_length_to_be( True if the length of the value matches the specified length or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: if not hasattr(value, "__len__"): value = str(value) return len(value) == length @@ -446,7 +446,7 @@ def expect_column_values_length_to_be_between( True if the length of the value is within the specified range or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: if not hasattr(value, "__len__"): value = str(value) return len(value) >= minimum and len(value) <= maximum @@ -478,7 +478,7 @@ def expect_column_values_to_be_more_than( True if the value is greater than the threshold or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return value > threshold return ignore_nulls @@ -508,6 +508,6 @@ def expect_column_values_to_be_less_than( True if the value is less than the threshold or if the value is null and ignore_nulls is True, False otherwise. """ value = row.get(column) - if value: + if value is not None: return value < threshold return ignore_nulls diff --git a/data_expectations/internals/models.py b/data_expectations/internals/models.py index 8c59431..39eca2b 100644 --- a/data_expectations/internals/models.py +++ b/data_expectations/internals/models.py @@ -19,6 +19,7 @@ from typing import Union from data_expectations import Behaviors +from data_expectations.errors import ExpectationNotUnderstoodError @dataclass @@ -69,3 +70,18 @@ def load(cls: Type["Expectation"], serialized: Union[Dict[str, Any], str]) -> "E ignore_nulls = serialized_copy.pop("ignore_nulls", True) config = serialized_copy return cls(expectation=expectation, column=column, ignore_nulls=ignore_nulls, config=config) + + def test_value(self, value: Any): + """ + Test a single value against this expectation. + + Parameters: + value: Any + The value to be tested. + """ + from data_expectations import Expectations + + test_logic = Expectations.all_expectations().get(self.expectation.value, None) + if not test_logic: + raise ExpectationNotUnderstoodError(expectation=self.expectation) + return test_logic(row={"value": value}, column="value", ignore_nulls=self.ignore_nulls, **self.config) diff --git a/data_expectations/version.py b/data_expectations/version.py index 5be518b..0c9f11d 100644 --- a/data_expectations/version.py +++ b/data_expectations/version.py @@ -13,6 +13,6 @@ # Store the version here so: # 1) we don't load dependencies by storing it in __init__.py # 2) we can import it in setup.py for the same reason -__version__ = "1.6.0" +__version__ = "1.7.0" # nodoc - don't add to the documentation wiki diff --git a/tests/test_documentation.py b/tests/test_documentation.py index c52650f..0b1679b 100644 --- a/tests/test_documentation.py +++ b/tests/test_documentation.py @@ -50,7 +50,23 @@ def test_example(): print("Data Didn't Meet Expectations") +def test_value_example(): + import data_expectations as de + from data_expectations import Expectation + from data_expectations import Behaviors + + expectation = Expectation( + Behaviors.EXPECT_COLUMN_VALUES_TO_BE_BETWEEN, column="age", config={"minimum": 0, "maximum": 120} + ) + + try: + expectation.test_value(55) + except de.errors.ExpectationNotMetError: # pragma: no cover + print("Data Didn't Meet Expectations") + + if __name__ == "__main__": # pragma: no cover test_example() test_example_legacy() + test_value_example() print("✅ okay") diff --git a/tests/test_test_value.py b/tests/test_test_value.py new file mode 100644 index 0000000..5953fd2 --- /dev/null +++ b/tests/test_test_value.py @@ -0,0 +1,20 @@ +import os +import sys + + +sys.path.insert(1, os.path.join(sys.path[0], "..")) + +from data_expectations import Expectation, Behaviors + + +def test_test_value_test(): + expect = Expectation(Behaviors.EXPECT_COLUMN_VALUES_LENGTH_TO_BE, column="value", config={"length": 5}) + assert expect.test_value("12345") + assert not expect.test_value("") + assert expect.test_value(None) + + +if __name__ == "__main__": # pragma: no cover + test_test_value_test() + + print("✅ okay")