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/expectation.py b/great_expectations/expectations/expectation.py
--- a/great_expectations/expectations/expectation.py
+++ b/great_expectations/expectations/expectation.py
@@ -1367,6 +1367,190 @@ def _validate(
         )
 
 
+class MulticolumnMapExpectation(TableExpectation, ABC):
+    map_metric = None
+    domain_keys = (
+        "batch_id",
+        "table",
+        "column_list",
+        "row_condition",
+        "condition_parser",
+    )
+    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
+        "result_format": "BASIC",
+        "include_config": True,
+        "catch_exceptions": True,
+    }
+
+    @classmethod
+    def is_abstract(cls):
+        return cls.map_metric is None or super().is_abstract()
+
+    def validate_configuration(self, configuration: Optional[ExpectationConfiguration]):
+        if not super().validate_configuration(configuration):
+            return False
+        try:
+            assert (
+                "column_list" in configuration.kwargs
+            ), "'column_list' parameter is required for multicolumn map expectations"
+        except AssertionError as e:
+            raise InvalidExpectationConfigurationError(str(e))
+        return True
+
+    def get_validation_dependencies(
+        self,
+        configuration: Optional[ExpectationConfiguration] = None,
+        execution_engine: Optional[ExecutionEngine] = None,
+        runtime_configuration: Optional[dict] = None,
+    ):
+        dependencies = super().get_validation_dependencies(
+            configuration, execution_engine, runtime_configuration
+        )
+        assert isinstance(
+            self.map_metric, str
+        ), "MulticolumnMapExpectation 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."
+        # convenient name for updates
+
+        result_format_str = dependencies["result_format"].get("result_format")
+
+        metric_dependencies = dependencies["metrics"]
+
+        metric_kwargs = get_metric_kwargs(
+            metric_name=self.map_metric + ".unexpected_count",
+            configuration=configuration,
+            runtime_configuration=runtime_configuration,
+        )
+        metric_dependencies[
+            self.map_metric + ".unexpected_count"
+        ] = MetricConfiguration(
+            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="table.row_count",
+            configuration=configuration,
+            runtime_configuration=runtime_configuration,
+        )
+        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"],
+        )
+
+        metric_kwargs = get_metric_kwargs(
+            self.map_metric + ".filtered_row_count",
+            configuration=configuration,
+            runtime_configuration=runtime_configuration,
+        )
+        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"],
+        )
+
+        if result_format_str == "BOOLEAN_ONLY":
+            return dependencies
+
+        metric_kwargs = get_metric_kwargs(
+            self.map_metric + ".unexpected_values",
+            configuration=configuration,
+            runtime_configuration=runtime_configuration,
+        )
+        metric_dependencies[
+            self.map_metric + ".unexpected_values"
+        ] = MetricConfiguration(
+            metric_name=self.map_metric + ".unexpected_values",
+            metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
+            metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
+        )
+
+        if result_format_str in ["BASIC", "SUMMARY"]:
+            return dependencies
+
+        metric_kwargs = get_metric_kwargs(
+            self.map_metric + ".unexpected_rows",
+            configuration=configuration,
+            runtime_configuration=runtime_configuration,
+        )
+        metric_dependencies[self.map_metric + ".unexpected_rows"] = MetricConfiguration(
+            metric_name=self.map_metric + ".unexpected_rows",
+            metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
+            metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
+        )
+
+        if isinstance(execution_engine, PandasExecutionEngine):
+            metric_kwargs = get_metric_kwargs(
+                self.map_metric + ".unexpected_index_list",
+                configuration=configuration,
+                runtime_configuration=runtime_configuration,
+            )
+            metric_dependencies[
+                self.map_metric + ".unexpected_index_list"
+            ] = MetricConfiguration(
+                metric_name=self.map_metric + ".unexpected_index_list",
+                metric_domain_kwargs=metric_kwargs["metric_domain_kwargs"],
+                metric_value_kwargs=metric_kwargs["metric_value_kwargs"],
+            )
+
+        return dependencies
+
+    def _validate(
+        self,
+        configuration: ExpectationConfiguration,
+        metrics: Dict,
+        runtime_configuration: dict = None,
+        execution_engine: ExecutionEngine = None,
+    ):
+        if runtime_configuration:
+            result_format = runtime_configuration.get(
+                "result_format",
+                configuration.kwargs.get(
+                    "result_format", self.default_kwarg_values.get("result_format")
+                ),
+            )
+        else:
+            result_format = configuration.kwargs.get(
+                "result_format", self.default_kwarg_values.get("result_format")
+            )
+
+        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")
+        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 filtered_row_count is None
+            or total_count == 0
+            or filtered_row_count == 0
+        ):
+            # Vacuously true
+            success = True
+        else:
+            success = unexpected_count == 0
+
+        return _format_map_output(
+            result_format=parse_result_format(result_format),
+            success=success,
+            element_count=filtered_row_count,
+            nonnull_count=filtered_row_count,
+            unexpected_count=unexpected_count,
+            unexpected_list=unexpected_values,
+            unexpected_index_list=unexpected_index_list,
+        )
+
+
 class ColumnPairMapExpectation(TableExpectation, ABC):
     map_metric = None
     domain_keys = (


Test patch: