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/checkpoint/checkpoint.py b/great_expectations/checkpoint/checkpoint.py
--- a/great_expectations/checkpoint/checkpoint.py
+++ b/great_expectations/checkpoint/checkpoint.py
@@ -132,15 +132,6 @@ def get_substituted_config(
         if isinstance(config, dict):
             config = CheckpointConfig(**config)
 
-        # Necessary when using RuntimeDataConnector with SimpleCheckpoint
-        if isinstance(config.batch_request, BatchRequest):
-            config.batch_request = config.batch_request.get_json_dict()
-        runtime_kwargs_batch_request = runtime_kwargs.get("batch_request")
-        if isinstance(runtime_kwargs_batch_request, BatchRequest):
-            runtime_kwargs[
-                "batch_request"
-            ] = runtime_kwargs_batch_request.get_json_dict()
-
         substituted_config: Union[CheckpointConfig, dict]
         if (
             self._substituted_config is not None
diff --git a/great_expectations/checkpoint/util.py b/great_expectations/checkpoint/util.py
--- a/great_expectations/checkpoint/util.py
+++ b/great_expectations/checkpoint/util.py
@@ -198,6 +198,12 @@ def get_runtime_batch_request(
     validation_batch_request: Optional[dict] = None,
 ) -> Union[BatchRequest, RuntimeBatchRequest]:
     runtime_config_batch_request = substituted_runtime_config.batch_request
+    batch_data = None
+    if isinstance(runtime_config_batch_request, RuntimeBatchRequest):
+        runtime_parameters = runtime_config_batch_request.runtime_parameters
+        if isinstance(runtime_parameters, dict) and "batch_data" in runtime_parameters:
+            batch_data = runtime_parameters["batch_data"]
+            runtime_config_batch_request = runtime_config_batch_request.get_json_dict()
 
     if (
         runtime_config_batch_request is not None
@@ -210,15 +216,14 @@ def get_runtime_batch_request(
     else:
         batch_request_class = BatchRequest
 
+    if runtime_config_batch_request is None and validation_batch_request is None:
+        return None
+
     if runtime_config_batch_request is None:
-        return (
-            validation_batch_request
-            if validation_batch_request is None
-            else batch_request_class(**validation_batch_request)
-        )
+        runtime_config_batch_request = {}
 
     if validation_batch_request is None:
-        return batch_request_class(**runtime_config_batch_request)
+        validation_batch_request = {}
 
     runtime_batch_request_dict: dict = copy.deepcopy(validation_batch_request)
     for key, val in runtime_batch_request_dict.items():
@@ -227,6 +232,10 @@ def get_runtime_batch_request(
                 f'BatchRequest attribute "{key}" was specified in both validation and top-level CheckpointConfig.'
             )
     runtime_batch_request_dict.update(runtime_config_batch_request)
+    if batch_data is not None:
+        if "runtime_parameters" not in runtime_batch_request_dict:
+            runtime_batch_request_dict["runtime_parameters"] = {}
+        runtime_batch_request_dict["runtime_parameters"]["batch_data"] = batch_data
     return batch_request_class(**runtime_batch_request_dict)
 
 
diff --git a/great_expectations/core/batch.py b/great_expectations/core/batch.py
--- a/great_expectations/core/batch.py
+++ b/great_expectations/core/batch.py
@@ -2,6 +2,7 @@
 import datetime
 import hashlib
 import json
+import types
 from typing import Dict, Optional, Union
 
 from great_expectations.core.id_dict import BatchKwargs, BatchSpec, IDDict
@@ -412,6 +413,30 @@ def __init__(
         self._runtime_parameters = runtime_parameters
         self._batch_identifiers = batch_identifiers
 
+    def __deepcopy__(self, memo):
+        runtime_parameters = getattr(self, "_runtime_parameters", None)
+        if isinstance(runtime_parameters, dict) and "batch_data" in runtime_parameters:
+            batch_data = runtime_parameters.pop("batch_data")
+            deepcopy_method = self.__deepcopy__
+            self.__deepcopy__ = None
+            cp = copy.deepcopy(self, memo)
+            self.__deepcopy__ = deepcopy_method
+            # Copy the function object
+            func = types.FunctionType(
+                deepcopy_method.__code__,
+                deepcopy_method.__globals__,
+                deepcopy_method.__name__,
+                deepcopy_method.__defaults__,
+                deepcopy_method.__closure__,
+            )
+            # Bind to cp and set
+            bound_method = func.__get__(cp, cp.__class__)
+            cp.__deepcopy__ = bound_method
+            cp._runtime_parameters["batch_data"] = batch_data
+            self._runtime_parameters["batch_data"] = batch_data
+            return cp
+        return copy.deepcopy(self, memo)
+
 
 # TODO: <Alex>The following class is to support the backward compatibility with the legacy design.</Alex>
 class BatchMarkers(BatchKwargs):


Test patch:
diff --git a/tests/core/test_batch_related_objects.py b/tests/core/test_batch_related_objects.py
--- a/tests/core/test_batch_related_objects.py
+++ b/tests/core/test_batch_related_objects.py
@@ -1,3 +1,6 @@
+import copy
+import datetime
+
 import pandas as pd
 import pytest
 
@@ -8,6 +11,7 @@
     BatchRequest,
     BatchSpec,
     IDDict,
+    RuntimeBatchRequest,
 )
 from great_expectations.core.batch_spec import RuntimeDataBatchSpec
 from great_expectations.exceptions import InvalidBatchSpecError
@@ -144,3 +148,33 @@ def test_RuntimeDataBatchSpec():
             "batch_data": "we don't check types yet",
         }
     )
+
+
+def test_runtime_batch_request_deepcopy():
+    """
+    Python's built-in id() function returns the memory location of a given object.
+    It is used here to ascertain "sameness" and the lack thereof
+    of different objects.
+    """
+    df = pd.DataFrame({"a": [1, 2, 3]})
+    id_1 = id(df)
+    batch_request = RuntimeBatchRequest(
+        datasource_name="context_weeks_datasource",
+        data_connector_name="context_weeks_data_connector",
+        data_asset_name="context_weeks_processed",  # this can be anything that identifies this data_asset for you
+        runtime_parameters={"batch_data": df},  # insert our loaded dataframe
+        batch_identifiers={
+            "test_dataframe": datetime.date.today().strftime("%Y-%m-%d")
+        },
+    )
+    cp = copy.deepcopy(batch_request)
+    id_2 = id(cp.runtime_parameters["batch_data"])
+    id_3 = id(batch_request.runtime_parameters["batch_data"])
+
+    assert id_1 == id_2
+    assert id_2 == id_3
+
+    id_4 = id(batch_request.batch_identifiers)
+    id_5 = id(cp.batch_identifiers)
+
+    assert id_4 != id_5