Repository: great-expectations/great_expectations

Issue:
Use cleaner solution for non-truncating division in python 2
Prefer `from __future__ import division` to `1.*x/y`


Discussion:
Done inline during improve_partitions push.

Resolving patch:
diff --git a/great_expectations/expectations/core/expect_column_pair_values_a_to_be_greater_than_b.py b/great_expectations/expectations/core/expect_column_pair_values_a_to_be_greater_than_b.py
--- a/great_expectations/expectations/core/expect_column_pair_values_a_to_be_greater_than_b.py
+++ b/great_expectations/expectations/core/expect_column_pair_values_a_to_be_greater_than_b.py
@@ -1,14 +1,6 @@
-from typing import Dict, Optional
+from typing import Optional
 
-from dateutil.parser import parse
-
-from great_expectations.core.batch import Batch
 from great_expectations.core.expectation_configuration import ExpectationConfiguration
-from great_expectations.execution_engine import (
-    ExecutionEngine,
-    PandasExecutionEngine,
-    SparkDFExecutionEngine,
-)
 from great_expectations.expectations.util import render_evaluation_parameter_string
 
 from ...render.renderer.renderer import renderer
@@ -18,10 +10,10 @@
     parse_row_condition_string_pandas_engine,
     substitute_none_for_missing,
 )
-from ..expectation import InvalidExpectationConfigurationError, TableExpectation
+from ..expectation import ColumnPairMapExpectation, InvalidExpectationConfigurationError
 
 
-class ExpectColumnPairValuesAToBeGreaterThanB(TableExpectation):
+class ExpectColumnPairValuesAToBeGreaterThanB(ColumnPairMapExpectation):
     """
     Expect values in column A to be greater than column B.
 
@@ -72,7 +64,7 @@ class ExpectColumnPairValuesAToBeGreaterThanB(TableExpectation):
         "requirements": [],
     }
 
-    metric_dependencies = ("column_a_greater_than_b",)
+    map_metric = "column_pair_values.a_greater_than_b"
     success_keys = (
         "column_A",
         "column_B",
@@ -80,12 +72,14 @@ class ExpectColumnPairValuesAToBeGreaterThanB(TableExpectation):
         "parse_strings_as_datetimes",
         "allow_cross_type_comparisons",
         "or_equal",
+        "mostly",
     )
 
     default_kwarg_values = {
         "column_A": None,
         "column_B": None,
         "or_equal": None,
+        "mostly": 1.0,
         "parse_strings_as_datetimes": False,
         "allow_cross_type_comparisons": None,
         "ignore_row_if": "both_values_are_missing",
diff --git a/great_expectations/expectations/expectation.py b/great_expectations/expectations/expectation.py
--- a/great_expectations/expectations/expectation.py
+++ b/great_expectations/expectations/expectation.py
@@ -1231,7 +1231,9 @@ def get_validation_dependencies(
             self.metric_dependencies == tuple()
         ), "ColumnMapExpectation must be configured using map_metric, and cannot have metric_dependencies declared."
         # convenient name for updates
+
         metric_dependencies = dependencies["metrics"]
+
         metric_kwargs = get_metric_kwargs(
             metric_name="column_values.nonnull.unexpected_count",
             configuration=configuration,
@@ -1257,7 +1259,6 @@ def get_validation_dependencies(
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
 
-        result_format_str = dependencies["result_format"].get("result_format")
         metric_kwargs = get_metric_kwargs(
             metric_name="table.row_count",
             configuration=configuration,
@@ -1268,6 +1269,9 @@ def get_validation_dependencies(
             metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
+
+        result_format_str = dependencies["result_format"].get("result_format")
+
         if result_format_str == "BOOLEAN_ONLY":
             return dependencies
 
@@ -1365,21 +1369,21 @@ def _validate(
         )
 
 
-class MulticolumnMapExpectation(TableExpectation, ABC):
+class ColumnPairMapExpectation(TableExpectation, ABC):
     map_metric = None
     domain_keys = (
         "batch_id",
         "table",
-        "column_list",
+        "column_A",
+        "column_B",
         "row_condition",
         "condition_parser",
-        "ignore_row_if",
     )
-    success_keys = tuple()
+    success_keys = ("mostly",)
     default_kwarg_values = {
         "row_condition": None,
         "condition_parser": None,  # we expect this to be explicitly set whenever a row_condition is passed
-        "ignore_row_if": "all_value_are_missing",
+        "mostly": 1,
         "result_format": "BASIC",
         "include_config": True,
         "catch_exceptions": True,
@@ -1394,8 +1398,17 @@ def validate_configuration(self, configuration: Optional[ExpectationConfiguratio
             return False
         try:
             assert (
-                "column_list" in configuration.kwargs
-            ), "'column_list' parameter is required for multicolumn map expectations"
+                "column_A" in configuration.kwargs
+            ), "'column_A' parameter is required for column pair map expectations"
+            assert (
+                "column_B" in configuration.kwargs
+            ), "'column_B' parameter is required for column pair map expectations"
+            if "mostly" in configuration.kwargs:
+                mostly = configuration.kwargs["mostly"]
+                assert isinstance(
+                    mostly, (int, float)
+                ), "'mostly' parameter must be an integer or float"
+                assert 0 <= mostly <= 1, "'mostly' parameter must be between 0 and 1"
         except AssertionError as e:
             raise InvalidExpectationConfigurationError(str(e))
         return True
@@ -1411,14 +1424,12 @@ def get_validation_dependencies(
         )
         assert isinstance(
             self.map_metric, str
-        ), "MulticolumnMapExpectation must override get_validation_dependencies or declare exactly one map_metric"
+        ), "ColumnPairMapExpectation must override get_validation_dependencies or declare exactly one map_metric"
         assert (
             self.metric_dependencies == tuple()
-        ), "MulticolumnMapExpectation must be configured using map_metric, and cannot have metric_dependencies declared."
+        ), "ColumnPairMapExpectation must be configured using map_metric, and cannot have metric_dependencies declared."
         # convenient name for updates
 
-        result_format_str = dependencies["result_format"].get("result_format")
-
         metric_dependencies = dependencies["metrics"]
 
         metric_kwargs = get_metric_kwargs(
@@ -1458,6 +1469,8 @@ def get_validation_dependencies(
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
 
+        result_format_str = dependencies["result_format"].get("result_format")
+
         if result_format_str == "BOOLEAN_ONLY":
             return dependencies
 
@@ -1523,6 +1536,10 @@ def _validate(
                 "result_format", self.default_kwarg_values.get("result_format")
             )
 
+        mostly = self.get_success_kwargs().get(
+            "mostly", self.default_kwarg_values.get("mostly")
+        )
+
         total_count = metrics.get("table.row_count")
         unexpected_count = metrics.get(self.map_metric + ".unexpected_count")
         unexpected_values = metrics.get(self.map_metric + ".unexpected_values")
@@ -1538,7 +1555,10 @@ def _validate(
             # Vacuously true
             success = True
         else:
-            success = unexpected_count == 0
+            success_ratio = (
+                float(filtered_row_count - unexpected_count) / filtered_row_count
+            )
+            success = success_ratio >= mostly
 
         return _format_map_output(
             result_format=parse_result_format(result_format),
@@ -1551,21 +1571,21 @@ def _validate(
         )
 
 
-class ColumnPairMapExpectation(TableExpectation, ABC):
+class MulticolumnMapExpectation(TableExpectation, ABC):
     map_metric = None
     domain_keys = (
         "batch_id",
         "table",
-        "column_A",
-        "column_B",
+        "column_list",
         "row_condition",
         "condition_parser",
+        "ignore_row_if",
     )
-    success_keys = ("mostly",)
+    success_keys = tuple()
     default_kwarg_values = {
         "row_condition": None,
         "condition_parser": None,  # we expect this to be explicitly set whenever a row_condition is passed
-        "mostly": 1,
+        "ignore_row_if": "all_value_are_missing",
         "result_format": "BASIC",
         "include_config": True,
         "catch_exceptions": True,
@@ -1580,17 +1600,8 @@ def validate_configuration(self, configuration: Optional[ExpectationConfiguratio
             return False
         try:
             assert (
-                "column_A" in configuration.kwargs
-            ), "'column_A' parameter is required for column pair map expectations"
-            assert (
-                "column_B" in configuration.kwargs
-            ), "'column_B' parameter is required for column pair map expectations"
-            if "mostly" in configuration.kwargs:
-                mostly = configuration.kwargs["mostly"]
-                assert isinstance(
-                    mostly, (int, float)
-                ), "'mostly' parameter must be an integer or float"
-                assert 0 <= mostly <= 1, "'mostly' parameter must be between 0 and 1"
+                "column_list" in configuration.kwargs
+            ), "'column_list' parameter is required for multicolumn map expectations"
         except AssertionError as e:
             raise InvalidExpectationConfigurationError(str(e))
         return True
@@ -1606,52 +1617,56 @@ def get_validation_dependencies(
         )
         assert isinstance(
             self.map_metric, str
-        ), "ColumnPairMapExpectation must override get_validation_dependencies or declare exactly one map_metric"
+        ), "MulticolumnMapExpectation must override get_validation_dependencies or declare exactly one map_metric"
         assert (
             self.metric_dependencies == tuple()
-        ), "ColumnPairMapExpectation must be configured using map_metric, and cannot have metric_dependencies declared."
+        ), "MulticolumnMapExpectation must be configured using map_metric, and cannot have metric_dependencies declared."
         # convenient name for updates
+
         metric_dependencies = dependencies["metrics"]
+
         metric_kwargs = get_metric_kwargs(
-            metric_name="column_values.nonnull.unexpected_count",
+            metric_name=self.map_metric + ".unexpected_count",
             configuration=configuration,
             runtime_configuration=runtime_configuration,
         )
         metric_dependencies[
-            "column_values.nonnull.unexpected_count"
+            self.map_metric + ".unexpected_count"
         ] = MetricConfiguration(
-            "column_values.nonnull.unexpected_count",
+            self.map_metric + ".unexpected_count",
             metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
+
         metric_kwargs = get_metric_kwargs(
-            metric_name=self.map_metric + ".unexpected_count",
+            metric_name="table.row_count",
             configuration=configuration,
             runtime_configuration=runtime_configuration,
         )
-        metric_dependencies[
-            self.map_metric + ".unexpected_count"
-        ] = MetricConfiguration(
-            self.map_metric + ".unexpected_count",
+        metric_dependencies["table.row_count"] = MetricConfiguration(
+            metric_name="table.row_count",
             metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
 
-        result_format_str = dependencies["result_format"].get("result_format")
-        if result_format_str == "BOOLEAN_ONLY":
-            return dependencies
-
         metric_kwargs = get_metric_kwargs(
-            metric_name="table.row_count",
+            self.map_metric + ".filtered_row_count",
             configuration=configuration,
             runtime_configuration=runtime_configuration,
         )
-        metric_dependencies["table.row_count"] = MetricConfiguration(
-            metric_name="table.row_count",
+        metric_dependencies[
+            self.map_metric + ".filtered_row_count"
+        ] = MetricConfiguration(
+            metric_name=self.map_metric + ".filtered_row_count",
             metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
             metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
         )
 
+        result_format_str = dependencies["result_format"].get("result_format")
+
+        if result_format_str == "BOOLEAN_ONLY":
+            return dependencies
+
         metric_kwargs = get_metric_kwargs(
             self.map_metric + ".unexpected_values",
             configuration=configuration,
@@ -1713,36 +1728,32 @@ def _validate(
             result_format = configuration.kwargs.get(
                 "result_format", self.default_kwarg_values.get("result_format")
             )
-        mostly = self.get_success_kwargs().get(
-            "mostly", self.default_kwarg_values.get("mostly")
-        )
+
         total_count = metrics.get("table.row_count")
-        null_count = metrics.get("column_values.nonnull.unexpected_count")
         unexpected_count = metrics.get(self.map_metric + ".unexpected_count")
+        unexpected_values = metrics.get(self.map_metric + ".unexpected_values")
+        unexpected_index_list = metrics.get(self.map_metric + ".unexpected_index_list")
+        filtered_row_count = metrics.get(self.map_metric + ".filtered_row_count")
 
-        if total_count is None or null_count is None:
-            total_count = nonnull_count = 0
-        else:
-            nonnull_count = total_count - null_count
-
-        success = None
-        if total_count == 0 or nonnull_count == 0:
+        if (
+            total_count is None
+            or filtered_row_count is None
+            or total_count == 0
+            or filtered_row_count == 0
+        ):
             # Vacuously true
             success = True
-        elif nonnull_count > 0:
-            success_ratio = float(nonnull_count - unexpected_count) / nonnull_count
-            success = success_ratio >= mostly
+        else:
+            success = unexpected_count == 0
 
         return _format_map_output(
             result_format=parse_result_format(result_format),
             success=success,
-            element_count=metrics.get("table.row_count"),
-            nonnull_count=nonnull_count,
-            unexpected_count=metrics.get(self.map_metric + ".unexpected_count"),
-            unexpected_list=metrics.get(self.map_metric + ".unexpected_values"),
-            unexpected_index_list=metrics.get(
-                self.map_metric + ".unexpected_index_list"
-            ),
+            element_count=total_count,
+            nonnull_count=filtered_row_count,
+            unexpected_count=unexpected_count,
+            unexpected_list=unexpected_values,
+            unexpected_index_list=unexpected_index_list,
         )
 
 
diff --git a/great_expectations/validator/validator.py b/great_expectations/validator/validator.py
--- a/great_expectations/validator/validator.py
+++ b/great_expectations/validator/validator.py
@@ -164,6 +164,7 @@ def expose_dataframe_methods(self, value: bool):
         self._expose_dataframe_methods = value
 
     def __getattr__(self, name):
+        name = name.lower()
         if name.startswith("expect_") and get_expectation_impl(name):
             return self.validate_expectation(name)
         elif (


Test patch: