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/rule_based_profiler/domain_builder/categorical_column_domain_builder.py b/great_expectations/rule_based_profiler/domain_builder/categorical_column_domain_builder.py
--- a/great_expectations/rule_based_profiler/domain_builder/categorical_column_domain_builder.py
+++ b/great_expectations/rule_based_profiler/domain_builder/categorical_column_domain_builder.py
@@ -8,9 +8,11 @@
     CardinalityChecker,
     CardinalityLimitMode,
     RelativeCardinalityLimit,
+    validate_input_parameters,
 )
 from great_expectations.rule_based_profiler.helpers.util import (
     build_simple_domains_from_column_names,
+    get_parameter_value_and_validate_return_type,
     get_resolved_metrics_by_key,
 )
 from great_expectations.rule_based_profiler.types import Domain, ParameterContainer
@@ -31,10 +33,11 @@ def __init__(
         batch_list: Optional[List[Batch]] = None,
         batch_request: Optional[Union[BatchRequest, RuntimeBatchRequest, dict]] = None,
         data_context: Optional["DataContext"] = None,  # noqa: F821
-        limit_mode: Optional[Union[CardinalityLimitMode, str]] = None,
-        max_unique_values: Optional[int] = None,
-        max_proportion_unique: Optional[int] = None,
+        column_names: Optional[Union[str, Optional[List[str]]]] = None,
         exclude_columns: Optional[Union[str, Optional[List[str]]]] = None,
+        limit_mode: Optional[Union[CardinalityLimitMode, str]] = None,
+        max_unique_values: Optional[Union[str, int]] = None,
+        max_proportion_unique: Optional[Union[str, float]] = None,
     ):
         """Create column domains where cardinality is within the specified limit.
 
@@ -55,6 +58,8 @@ def __init__(
             batch_list: explicitly specified Batch objects for use in DomainBuilder
             batch_request: BatchRequest to be optionally used to define batches to consider for this domain builder.
             data_context: DataContext associated with this profiler.
+            column_names: Explicitly specified column_names list desired (if None, it is computed based on active Batch)
+            exclude_columns: If provided, these columns are pre-filtered and excluded from consideration, cardinality is not computed.
             limit_mode: CardinalityLimitMode or string name of the mode
                 defining the maximum allowable cardinality to use when
                 filtering columns.
@@ -62,36 +67,46 @@ def __init__(
                 cardinality limit to use when filtering columns.
             max_proportion_unique: proportion of unique values for a
                 custom cardinality limit to use when filtering columns.
-            exclude_columns: If provided, these columns are pre-filtered and
-                excluded from consideration, cardinality is not computed.
         """
         super().__init__(
             batch_list=batch_list,
             batch_request=batch_request,
             data_context=data_context,
-            column_names=None,
-        )
-
-        self._cardinality_checker = CardinalityChecker(
-            limit_mode=limit_mode,
-            max_unique_values=max_unique_values,
-            max_proportion_unique=max_proportion_unique,
+            column_names=column_names,
         )
 
         self._exclude_columns = exclude_columns
 
+        self._limit_mode = limit_mode
+        self._max_unique_values = max_unique_values
+        self._max_proportion_unique = max_proportion_unique
+
+        self._cardinality_checker = None
+
     @property
     def domain_type(self) -> Union[str, MetricDomainTypes]:
         return MetricDomainTypes.COLUMN
 
     @property
-    def cardinality_checker(self) -> CardinalityChecker:
-        return self._cardinality_checker
+    def limit_mode(self) -> Optional[Union[CardinalityLimitMode, str]]:
+        return self._limit_mode
+
+    @property
+    def max_unique_values(self) -> Optional[Union[str, int]]:
+        return self._max_unique_values
+
+    @property
+    def max_proportion_unique(self) -> Optional[Union[str, float]]:
+        return self._max_proportion_unique
 
     @property
-    def exclude_columns(self) -> List[str]:
+    def exclude_columns(self) -> Optional[Union[str, Optional[List[str]]]]:
         return self._exclude_columns
 
+    @property
+    def cardinality_checker(self) -> Optional[CardinalityChecker]:
+        return self._cardinality_checker
+
     def _get_domains(
         self,
         variables: Optional[ParameterContainer] = None,
@@ -105,11 +120,54 @@ def _get_domains(
             List of domains that match the desired cardinality.
         """
         table_column_names: List[str] = self.get_effective_column_names(
-            include_columns=None,
+            include_columns=self.column_names,
             exclude_columns=self.exclude_columns,
             variables=variables,
         )
 
+        # Obtain limit_mode from "rule state" (i.e., variables and parameters); from instance variable otherwise.
+        limit_mode: Optional[
+            Union[CardinalityLimitMode, str]
+        ] = get_parameter_value_and_validate_return_type(
+            domain=None,
+            parameter_reference=self.limit_mode,
+            expected_return_type=None,
+            variables=variables,
+            parameters=None,
+        )
+
+        # Obtain max_unique_values from "rule state" (i.e., variables and parameters); from instance variable otherwise.
+        max_unique_values: Optional[int] = get_parameter_value_and_validate_return_type(
+            domain=None,
+            parameter_reference=self.max_unique_values,
+            expected_return_type=None,
+            variables=variables,
+            parameters=None,
+        )
+
+        # Obtain max_proportion_unique from "rule state" (i.e., variables and parameters); from instance variable otherwise.
+        max_proportion_unique: Optional[
+            float
+        ] = get_parameter_value_and_validate_return_type(
+            domain=None,
+            parameter_reference=self.max_proportion_unique,
+            expected_return_type=None,
+            variables=variables,
+            parameters=None,
+        )
+
+        validate_input_parameters(
+            limit_mode=limit_mode,
+            max_unique_values=max_unique_values,
+            max_proportion_unique=max_proportion_unique,
+        )
+
+        self._cardinality_checker = CardinalityChecker(
+            limit_mode=limit_mode,
+            max_unique_values=max_unique_values,
+            max_proportion_unique=max_proportion_unique,
+        )
+
         batch_ids: List[str] = self.get_batch_ids(variables=variables)
 
         metrics_for_cardinality_check: Dict[
diff --git a/great_expectations/rule_based_profiler/helpers/cardinality_checker.py b/great_expectations/rule_based_profiler/helpers/cardinality_checker.py
--- a/great_expectations/rule_based_profiler/helpers/cardinality_checker.py
+++ b/great_expectations/rule_based_profiler/helpers/cardinality_checker.py
@@ -1,7 +1,7 @@
 import abc
 import enum
 from dataclasses import dataclass
-from typing import List, Optional, Tuple, Union
+from typing import Optional, Tuple, Union
 
 from great_expectations.exceptions import ProfilerConfigurationError
 
@@ -85,6 +85,9 @@ class CardinalityChecker:
         AbsoluteCardinalityLimit,
         RelativeCardinalityLimit,
     )
+    SUPPORTED_LIMIT_MODE_CLASS_NAMES: Tuple[str] = (
+        mode.__name__ for mode in SUPPORTED_CARDINALITY_LIMIT_MODE_CLASSES
+    )
     SUPPORTED_CARDINALITY_LIMIT_MODE_STRINGS: Tuple[str] = (
         mode.name for mode in CardinalityLimitMode
     )
@@ -95,10 +98,6 @@ def __init__(
         max_unique_values: Optional[int] = None,
         max_proportion_unique: Optional[float] = None,
     ):
-        self.supported_limit_mode_class_names = (
-            mode.__name__ for mode in self.SUPPORTED_CARDINALITY_LIMIT_MODE_CLASSES
-        )
-
         self._limit_mode = self._convert_to_cardinality_mode(
             limit_mode=limit_mode,
             max_unique_values=max_unique_values,
@@ -134,18 +133,19 @@ def _validate_metric_value(metric_value: float) -> None:
             raise ProfilerConfigurationError(
                 f"Value of measured cardinality must be of type int or float, you provided {type(metric_value)}"
             )
+
         if metric_value < 0.00:
             raise ProfilerConfigurationError(
                 f"Value of cardinality (number of rows or percent unique) should be greater than 0.00, your value is {metric_value}"
             )
 
+    @staticmethod
     def _convert_to_cardinality_mode(
-        self,
         limit_mode: Optional[Union[CardinalityLimitMode, str]] = None,
         max_unique_values: Optional[int] = None,
         max_proportion_unique: Optional[float] = None,
     ) -> Union[AbsoluteCardinalityLimit, RelativeCardinalityLimit]:
-        self._validate_input_parameters(
+        validate_input_parameters(
             limit_mode=limit_mode,
             max_unique_values=max_unique_values,
             max_proportion_unique=max_proportion_unique,
@@ -172,42 +172,41 @@ def _convert_to_cardinality_mode(
                 max_proportion_unique=max_proportion_unique,
             )
 
-    def _validate_input_parameters(
-        self,
-        limit_mode: Optional[Union[CardinalityLimitMode, str]] = None,
-        max_unique_values: Optional[int] = None,
-        max_proportion_unique: Optional[int] = None,
-    ) -> None:
-        num_supplied_params: int = sum(
-            [
-                0 if param is None else 1
-                for param in (
-                    limit_mode,
-                    max_unique_values,
-                    max_proportion_unique,
-                )
-            ]
+
+def validate_input_parameters(
+    limit_mode: Optional[Union[CardinalityLimitMode, str]] = None,
+    max_unique_values: Optional[int] = None,
+    max_proportion_unique: Optional[int] = None,
+) -> None:
+    num_supplied_params: int = sum(
+        [
+            0 if param is None else 1
+            for param in (
+                limit_mode,
+                max_unique_values,
+                max_proportion_unique,
+            )
+        ]
+    )
+    if num_supplied_params != 1:
+        raise ProfilerConfigurationError(
+            f"Please pass ONE of the following parameters: limit_mode, max_unique_values, max_proportion_unique, you passed {num_supplied_params} parameters."
         )
-        if num_supplied_params != 1:
+
+    if limit_mode is not None:
+        if not (
+            isinstance(limit_mode, CardinalityLimitMode) or isinstance(limit_mode, str)
+        ):
             raise ProfilerConfigurationError(
-                f"Please pass ONE of the following parameters: limit_mode, max_unique_values, max_proportion_unique, you passed {num_supplied_params} parameters."
+                f"Please specify a supported cardinality limit type, supported classes are {','.join(CardinalityChecker.SUPPORTED_LIMIT_MODE_CLASS_NAMES)} and supported strings are {','.join(CardinalityChecker.SUPPORTED_CARDINALITY_LIMIT_MODE_STRINGS)}"
+            )
+    if max_unique_values is not None:
+        if not isinstance(max_unique_values, int):
+            raise ProfilerConfigurationError(
+                f"Please specify an int, you specified a {type(max_unique_values)}"
+            )
+    if max_proportion_unique is not None:
+        if not isinstance(max_proportion_unique, (float, int)):
+            raise ProfilerConfigurationError(
+                f"Please specify a float or int, you specified a {type(max_proportion_unique)}"
             )
-
-        if limit_mode is not None:
-            if not (
-                isinstance(limit_mode, CardinalityLimitMode)
-                or isinstance(limit_mode, str)
-            ):
-                raise ProfilerConfigurationError(
-                    f"Please specify a supported cardinality limit type, supported classes are {','.join(self.supported_limit_mode_class_names)} and supported strings are {','.join(self.SUPPORTED_CARDINALITY_LIMIT_MODE_STRINGS)}"
-                )
-        if max_unique_values is not None:
-            if not isinstance(max_unique_values, int):
-                raise ProfilerConfigurationError(
-                    f"Please specify an int, you specified a {type(max_unique_values)}"
-                )
-        if max_proportion_unique is not None:
-            if not isinstance(max_proportion_unique, (float, int)):
-                raise ProfilerConfigurationError(
-                    f"Please specify a float or int, you specified a {type(max_proportion_unique)}"
-                )
diff --git a/great_expectations/rule_based_profiler/parameter_builder/value_set_multi_batch_parameter_builder.py b/great_expectations/rule_based_profiler/parameter_builder/value_set_multi_batch_parameter_builder.py
--- a/great_expectations/rule_based_profiler/parameter_builder/value_set_multi_batch_parameter_builder.py
+++ b/great_expectations/rule_based_profiler/parameter_builder/value_set_multi_batch_parameter_builder.py
@@ -33,6 +33,15 @@ class ValueSetMultiBatchParameterBuilder(MetricMultiBatchParameterBuilder):
         2. This ParameterBuilder filters null values out from the unique value_set.
     """
 
+    exclude_field_names: Set[
+        str
+    ] = MetricMultiBatchParameterBuilder.exclude_field_names | {
+        "metric_name",
+        "enforce_numeric_metric",
+        "replace_nan_with_zero",
+        "reduce_scalar_metric",
+    }
+
     def __init__(
         self,
         name: str,


Test patch:
diff --git a/tests/rule_based_profiler/domain_builder/test_cardinality_checker.py b/tests/rule_based_profiler/domain_builder/test_cardinality_checker.py
--- a/tests/rule_based_profiler/domain_builder/test_cardinality_checker.py
+++ b/tests/rule_based_profiler/domain_builder/test_cardinality_checker.py
@@ -93,6 +93,7 @@ def test_cardinality_checker_instantiation_invalid_limit_mode_parameter():
 
 def test_cardinality_checker_instantiation_invalid_max_unique_values_parameter():
     with pytest.raises(ProfilerConfigurationError) as excinfo:
+        # noinspection PyTypeChecker
         _: CardinalityChecker = CardinalityChecker(
             max_unique_values="invalid_input_type_string"
         )
@@ -102,6 +103,7 @@ def test_cardinality_checker_instantiation_invalid_max_unique_values_parameter()
 
 def test_cardinality_checker_instantiation_invalid_max_proportion_unique_parameter():
     with pytest.raises(ProfilerConfigurationError) as excinfo:
+        # noinspection PyTypeChecker
         _: CardinalityChecker = CardinalityChecker(
             max_proportion_unique="invalid_input_type_string"
         )
diff --git a/tests/rule_based_profiler/domain_builder/test_categorical_column_domain_builder.py b/tests/rule_based_profiler/domain_builder/test_categorical_column_domain_builder.py
--- a/tests/rule_based_profiler/domain_builder/test_categorical_column_domain_builder.py
+++ b/tests/rule_based_profiler/domain_builder/test_categorical_column_domain_builder.py
@@ -1,5 +1,4 @@
 from typing import List
-from unittest import mock
 
 import pytest
 
@@ -88,34 +87,46 @@ def test_single_batch_one_cardinality(alice_columnar_table_single_batch_context)
     assert domains == alice_all_column_domains
 
 
-@mock.patch("great_expectations.data_context.data_context.DataContext")
-@mock.patch("great_expectations.core.batch.BatchRequest")
 def test_unsupported_cardinality_limit(
-    mock_data_context: mock.MagicMock,
-    mock_batch_request: mock.MagicMock,
+    alice_columnar_table_single_batch_context,
 ):
+    data_context: DataContext = alice_columnar_table_single_batch_context
+
+    batch_request: dict = {
+        "datasource_name": "alice_columnar_table_single_batch_datasource",
+        "data_connector_name": "alice_columnar_table_single_batch_data_connector",
+        "data_asset_name": "alice_columnar_table_single_batch_data_asset",
+    }
+
     with pytest.raises(ProfilerConfigurationError) as excinfo:
-        _: DomainBuilder = CategoricalColumnDomainBuilder(
-            batch_request=mock_batch_request,
-            data_context=mock_data_context,
+        _: List[Domain] = CategoricalColumnDomainBuilder(
+            batch_request=batch_request,
+            data_context=data_context,
             limit_mode="&*#$&INVALID&*#$*&",
-        )
+        ).get_domains()
+
     assert "specify a supported cardinality mode" in str(excinfo.value)
     assert "REL_1" in str(excinfo.value)
     assert "MANY" in str(excinfo.value)
 
 
-@mock.patch("great_expectations.data_context.data_context.DataContext")
-@mock.patch("great_expectations.core.batch.BatchRequest")
 def test_unspecified_cardinality_limit(
-    mock_data_context: mock.MagicMock,
-    mock_batch_request: mock.MagicMock,
+    alice_columnar_table_single_batch_context,
 ):
+    data_context: DataContext = alice_columnar_table_single_batch_context
+
+    batch_request: dict = {
+        "datasource_name": "alice_columnar_table_single_batch_datasource",
+        "data_connector_name": "alice_columnar_table_single_batch_data_connector",
+        "data_asset_name": "alice_columnar_table_single_batch_data_asset",
+    }
+
     with pytest.raises(ProfilerConfigurationError) as excinfo:
-        _: DomainBuilder = CategoricalColumnDomainBuilder(
-            data_context=mock_data_context,
-            batch_request=mock_batch_request,
-        )
+        _: List[Domain] = CategoricalColumnDomainBuilder(
+            data_context=data_context,
+            batch_request=batch_request,
+        ).get_domains()
+
     assert "Please pass ONE of the following parameters" in str(excinfo.value)
     assert "you passed 0 parameters" in str(excinfo.value)