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/core/evaluation_parameters.py b/great_expectations/core/evaluation_parameters.py
--- a/great_expectations/core/evaluation_parameters.py
+++ b/great_expectations/core/evaluation_parameters.py
@@ -5,6 +5,7 @@
 import operator
 import traceback
 from collections import namedtuple
+from typing import Any, Dict, Optional
 
 from pyparsing import (
     CaselessKeyword,
@@ -305,8 +306,10 @@ def find_evaluation_parameter_dependencies(parameter_expression):
 
 
 def parse_evaluation_parameter(
-    parameter_expression, evaluation_parameters=None, data_context=None
-):
+    parameter_expression: str,
+    evaluation_parameters: Optional[Dict[str, Any]] = None,
+    data_context: Optional[Any] = None,  # Cannot type 'DataContext' due to import cycle
+) -> Any:
     """Use the provided evaluation_parameters dict to parse a given parameter expression.
 
     Args:
@@ -332,7 +335,13 @@ def parse_evaluation_parameter(
     except ParseException as err:
         L = ["Parse Failure", parameter_expression, (str(err), err.line, err.column)]
 
-    if len(L) == 1 and L[0] not in evaluation_parameters:
+    # Represents a valid parser result of a single function that has no arguments
+    if len(L) == 1 and isinstance(L[0], tuple) and L[0][2] is False:
+        # Necessary to catch `now()` (which only needs to be evaluated with `expr.exprStack`)
+        # NOTE: 20211122 - Chetan - Any future built-ins that are zero arity functions will match this behavior
+        pass
+
+    elif len(L) == 1 and L[0] not in evaluation_parameters:
         # In this special case there were no operations to find, so only one value, but we don't have something to
         # substitute for that value
         try:


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
@@ -7,7 +7,7 @@
 import pytest
 
 from great_expectations.core import ExpectationValidationResult
-from great_expectations.core.batch import BatchRequest, RuntimeBatchRequest
+from great_expectations.core.batch import RuntimeBatchRequest
 from great_expectations.core.evaluation_parameters import (
     _deduplicate_evaluation_parameter_dependencies,
     find_evaluation_parameter_dependencies,
@@ -133,6 +133,8 @@ def test_parser_timing():
 
 def test_math_evaluation_paramaters():
     assert parse_evaluation_parameter("sin(2*PI)") == math.sin(math.pi * 2)
+    assert parse_evaluation_parameter("cos(2*PI)") == math.cos(math.pi * 2)
+    assert parse_evaluation_parameter("tan(2*PI)") == math.tan(math.pi * 2)
 
 
 def test_temporal_evaluation_parameters():
@@ -306,3 +308,25 @@ def test_evaluation_parameters_for_between_expectations_parse_correctly(
             "result": {"observed_value": 3},
         }
     )
+
+
+def test_now_evaluation_parameter():
+    """
+    now() is unique in the fact that it is the only evaluation param built-in that has zero arity (takes no arguments).
+    The following tests ensure that it is properly parsed and evaluated in a variety of contexts.
+    """
+    # By itself
+    res = parse_evaluation_parameter("now()")
+    assert dateutil.parser.parse(
+        res
+    ), "Provided evaluation parameter is not dateutil-parseable"
+
+    # In conjunction with timedelta
+    res = parse_evaluation_parameter("now() - timedelta(weeks=1)")
+    assert dateutil.parser.parse(
+        res
+    ), "Provided evaluation parameter is not dateutil-parseable"
+
+    # Require parens to actually invoke
+    with pytest.raises(EvaluationParameterError):
+        parse_evaluation_parameter("now")
diff --git a/tests/test_definitions/column_map_expectations/expect_column_values_to_be_between__evaluation_parameters.json b/tests/test_definitions/column_map_expectations/expect_column_values_to_be_between__evaluation_parameters.json
--- a/tests/test_definitions/column_map_expectations/expect_column_values_to_be_between__evaluation_parameters.json
+++ b/tests/test_definitions/column_map_expectations/expect_column_values_to_be_between__evaluation_parameters.json
@@ -119,6 +119,35 @@
         }
       },
       "tests": [
+        {
+          "title": "basic_positive_now_test",
+          "exact_match_out": false,
+          "out": {
+            "unexpected_list": [],
+            "unexpected_index_list": [],
+            "success": true
+          },
+          "in": {
+            "column": "ts",
+            "max_value": {
+              "$PARAMETER": "now()"
+            }
+          }
+        },
+        {
+          "title": "basic_negative_now_test",
+          "exact_match_out": false,
+          "out": {
+            "unexpected_index_list": [0, 1, 2, 3, 4, 5, 6, 7, 9],
+            "success": false
+          },
+          "in": {
+            "column": "ts",
+            "min_value": {
+              "$PARAMETER": "now()"
+            }
+          }
+        },
         {
           "title": "basic_positive_now_timedelta_test",
           "exact_match_out": false,