Repository: great-expectations/great_expectations

Issue:
Evaluation Parameters | Configuration validation failure | min_value & max_value Expectations
**Describe the bug**
While Using Evaluation Parameters with the expectations having both min_value & max_value parameters , the internal configuration validation fails. As min and max values are technically dictionaries(When using evaluation parameters i.e. `min_value={"$PARAMETER": "trunc(upstream_row_count * 0.9)"}` ), so a dictionary comparison problem occurs. 
`TypeError: '>' not supported between instances of 'dict' and 'dict'`

The trace of the problem is as follows: 

> validator.expect_table_row_count_to_be_between(
>   File "...\venv\lib\site-packages\great_expectations\validator\validator.py", line 281, in inst_expectation
>     raise err
>   File "...\venv\lib\site-packages\great_expectations\validator\validator.py", line 228, in inst_expectation
>     expectation = expectation_impl(configuration)
>   File "...\venv\lib\site-packages\great_expectations\expectations\expectation.py", line 137, in __init__
>     self.validate_configuration(configuration)
>   File "...\venv\lib\site-packages\great_expectations\expectations\core\expect_table_row_count_to_be_between.py", line 106, in validate_configuration
>     self.validate_metric_value_between_configuration(configuration=configuration)
>   File "...\venv\lib\site-packages\great_expectations\expectations\expectation.py", line 1122, in validate_metric_value_between_configuration
>     if min_val is not None and max_val is not None and min_val > max_val:
> TypeError: '>' not supported between instances of 'dict' and 'dict'

**To Reproduce**
Steps to reproduce the behavior:
1. Use this example with validator [GE Example](https://docs.greatexpectations.io/docs/reference/evaluation_parameters)
2. Sample Code
3. 

> batch_request = {
>         "datasource_name": datasource_name,
>         "data_connector_name": data_connector_name,
>         "data_asset_name": data_asset_name
>     }
> 
> validator = context.get_validator(
>         batch_request=BatchRequest(**batch_request),
>         expectation_suite_name=expectation_suite_name,
>     )
> 
> validator.set_evaluation_parameter("upstream_row_count", 10)
>         validator.expect_table_row_count_to_be_between(
>             min_value={"$PARAMETER": "trunc(upstream_row_count * 0.9)"},
>             max_value={"$PARAMETER": "trunc(upstream_row_count * 1.1)"},
>             result_format={'result_format': 'BOOLEAN_ONLY'})


**Expected behavior**
validate_metric_value_between_configuration function should handle dictionaries to tackle evaluation parameters or should remove min_value > max_value check 

**Environment (please complete the following information):**
 - Operating System: [Windows 10]
 - Great Expectations Version: [0.13.35]

**Additional context**



Discussion:
Thanks for opening this issue, @BilalZahid22! We will review and be in touch soon.

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
@@ -733,7 +733,7 @@ def validate(
         runtime_configuration=None,
     ):
         if configuration is None:
-            configuration = self.configuration
+            configuration = deepcopy(self.configuration)
 
         configuration.process_evaluation_parameters(
             evaluation_parameters, interactive_evaluation, data_context
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
@@ -244,6 +244,9 @@ def inst_expectation(*args, **kwargs):
             configuration = ExpectationConfiguration(
                 expectation_type=name, kwargs=expectation_kwargs, meta=meta
             )
+            configuration.process_evaluation_parameters(
+                self._expectation_suite.evaluation_parameters, True, self._data_context
+            )
 
             exception_info: ExceptionInfo
 


Test patch:
diff --git a/tests/core/test_evaluation_parameters.py b/tests/core/test_evaluation_parameters.py
--- a/tests/core/test_evaluation_parameters.py
+++ b/tests/core/test_evaluation_parameters.py
@@ -1,13 +1,16 @@
 from timeit import timeit
 
+import pandas
 import pytest
 
+from great_expectations.core import ExpectationValidationResult
+from great_expectations.core.batch import BatchRequest, RuntimeBatchRequest
 from great_expectations.core.evaluation_parameters import (
     _deduplicate_evaluation_parameter_dependencies,
     find_evaluation_parameter_dependencies,
     parse_evaluation_parameter,
 )
-from great_expectations.exceptions import EvaluationParameterError
+from great_expectations.exceptions import DataContextError, EvaluationParameterError
 
 
 def test_parse_evaluation_parameter():
@@ -197,3 +200,77 @@ def test_deduplicate_evaluation_parameter_dependencies():
             }
         ]
     } == deduplicated
+
+
+def test_evaluation_parameters_for_between_expectations_parse_correctly(
+    titanic_pandas_data_context_with_v013_datasource_with_checkpoints_v1_with_empty_store_stats_enabled,
+):
+    context = titanic_pandas_data_context_with_v013_datasource_with_checkpoints_v1_with_empty_store_stats_enabled
+
+    # Note that if you modify this batch request, you may save the new version as a .json file
+    #  to pass in later via the --batch-request option
+    df = pandas.DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
+    batch_request = {
+        "datasource_name": "my_datasource",
+        "data_connector_name": "my_runtime_data_connector",
+        "data_asset_name": "foo",
+        "runtime_parameters": {"batch_data": df},
+        "batch_identifiers": {
+            "pipeline_stage_name": "kickoff",
+            "airflow_run_id": "1234",
+        },
+    }
+
+    # Feel free to change the name of your suite here. Renaming this will not remove the other one.
+    expectation_suite_name = "abcde"
+    try:
+        suite = context.get_expectation_suite(
+            expectation_suite_name=expectation_suite_name
+        )
+        print(
+            f'Loaded ExpectationSuite "{suite.expectation_suite_name}" containing {len(suite.expectations)} '
+            f"expectations."
+        )
+    except DataContextError:
+        suite = context.create_expectation_suite(
+            expectation_suite_name=expectation_suite_name
+        )
+        print(f'Created ExpectationSuite "{suite.expectation_suite_name}".')
+
+    validator = context.get_validator(
+        batch_request=RuntimeBatchRequest(**batch_request),
+        expectation_suite_name=expectation_suite_name,
+    )
+    column_names = [f'"{column_name}"' for column_name in validator.columns()]
+    print(f"Columns: {', '.join(column_names)}.")
+
+    validator.set_evaluation_parameter("my_min", 1)
+    validator.set_evaluation_parameter("my_max", 5)
+
+    result = validator.expect_table_row_count_to_be_between(
+        min_value={"$PARAMETER": "my_min", "$PARAMETER.upstream_row_count": 10},
+        max_value={"$PARAMETER": "my_max", "$PARAMETER.upstream_row_count": 50},
+    )
+
+    assert result == ExpectationValidationResult(
+        **{
+            "expectation_config": {
+                "meta": {"substituted_parameters": {"min_value": 1, "max_value": 5}},
+                "kwargs": {
+                    "min_value": 1,
+                    "max_value": 5,
+                    "batch_id": "15fe04adb6ff20b9fc6eda486b7a36b7",
+                },
+                "expectation_type": "expect_table_row_count_to_be_between",
+                "ge_cloud_id": None,
+            },
+            "meta": {},
+            "exception_info": {
+                "raised_exception": False,
+                "exception_traceback": None,
+                "exception_message": None,
+            },
+            "success": True,
+            "result": {"observed_value": 3},
+        }
+    )