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/datasource/data_connector/data_connector.py b/great_expectations/datasource/data_connector/data_connector.py
--- a/great_expectations/datasource/data_connector/data_connector.py
+++ b/great_expectations/datasource/data_connector/data_connector.py
@@ -1,6 +1,6 @@
 import logging
 from copy import deepcopy
-from typing import Any, Callable, Dict, List, Optional, Tuple
+from typing import Any, Dict, List, Optional, Tuple
 
 import great_expectations.exceptions as gx_exceptions
 from great_expectations.core._docs_decorators import public_api
@@ -10,7 +10,6 @@
     BatchRequestBase,  # noqa: TCH001
 )
 from great_expectations.core.id_dict import BatchSpec
-from great_expectations.core.util import AzureUrl, DBFSPath, GCSUrl, S3Url
 from great_expectations.execution_engine import ExecutionEngine  # noqa: TCH001
 from great_expectations.validator.metric_configuration import MetricConfiguration
 from great_expectations.validator.validator import Validator
@@ -18,87 +17,6 @@
 logger = logging.getLogger(__name__)
 
 
-class DataConnectorStorageDataReferenceResolver:
-    DATA_CONNECTOR_NAME_TO_STORAGE_NAME_MAP: Dict[str, str] = {
-        "InferredAssetS3DataConnector": "S3",
-        "ConfiguredAssetS3DataConnector": "S3",
-        "InferredAssetGCSDataConnector": "GCS",
-        "ConfiguredAssetGCSDataConnector": "GCS",
-        "InferredAssetAzureDataConnector": "ABS",
-        "ConfiguredAssetAzureDataConnector": "ABS",
-        "InferredAssetDBFSDataConnector": "DBFS",
-        "ConfiguredAssetDBFSDataConnector": "DBFS",
-    }
-    STORAGE_NAME_EXECUTION_ENGINE_NAME_PATH_RESOLVERS: Dict[
-        Tuple[str, str], Callable
-    ] = {
-        (
-            "S3",
-            "PandasExecutionEngine",
-        ): lambda template_arguments: S3Url.OBJECT_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "S3",
-            "SparkDFExecutionEngine",
-        ): lambda template_arguments: S3Url.OBJECT_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "GCS",
-            "PandasExecutionEngine",
-        ): lambda template_arguments: GCSUrl.OBJECT_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "GCS",
-            "SparkDFExecutionEngine",
-        ): lambda template_arguments: GCSUrl.OBJECT_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "ABS",
-            "PandasExecutionEngine",
-        ): lambda template_arguments: AzureUrl.AZURE_BLOB_STORAGE_HTTPS_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "ABS",
-            "SparkDFExecutionEngine",
-        ): lambda template_arguments: AzureUrl.AZURE_BLOB_STORAGE_WASBS_URL_TEMPLATE.format(
-            **template_arguments
-        ),
-        (
-            "DBFS",
-            "SparkDFExecutionEngine",
-        ): lambda template_arguments: DBFSPath.convert_to_protocol_version(
-            **template_arguments
-        ),
-        (
-            "DBFS",
-            "PandasExecutionEngine",
-        ): lambda template_arguments: DBFSPath.convert_to_file_semantics_version(
-            **template_arguments
-        ),
-    }
-
-    @staticmethod
-    def resolve_data_reference(
-        data_connector_name: str,
-        execution_engine_name: str,
-        template_arguments: dict,
-    ):
-        """Resolve file path for a (data_connector_name, execution_engine_name) combination."""
-        storage_name: str = DataConnectorStorageDataReferenceResolver.DATA_CONNECTOR_NAME_TO_STORAGE_NAME_MAP[
-            data_connector_name
-        ]
-        return DataConnectorStorageDataReferenceResolver.STORAGE_NAME_EXECUTION_ENGINE_NAME_PATH_RESOLVERS[
-            (storage_name, execution_engine_name)
-        ](
-            template_arguments
-        )
-
-
 # noinspection SpellCheckingInspection
 @public_api
 class DataConnector:
@@ -302,14 +220,6 @@ def _generate_batch_spec_parameters_from_batch_definition(
     ) -> dict:
         raise NotImplementedError
 
-    def resolve_data_reference(self, template_arguments: dict):
-        """Resolve file path for a (data_connector_name, execution_engine_name) combination."""
-        return DataConnectorStorageDataReferenceResolver.resolve_data_reference(
-            data_connector_name=self.__class__.__name__,
-            execution_engine_name=self._execution_engine.__class__.__name__,
-            template_arguments=template_arguments,
-        )
-
     def self_check(self, pretty_print=True, max_examples=3):
         """
         Checks the configuration of the current DataConnector by doing the following :
diff --git a/great_expectations/datasource/data_connector/file_path_data_connector.py b/great_expectations/datasource/data_connector/file_path_data_connector.py
--- a/great_expectations/datasource/data_connector/file_path_data_connector.py
+++ b/great_expectations/datasource/data_connector/file_path_data_connector.py
@@ -1,5 +1,5 @@
 import logging
-from typing import Iterator, List, Optional, cast
+from typing import Callable, Dict, Iterator, List, Optional, Tuple, cast
 
 import great_expectations.exceptions as gx_exceptions
 from great_expectations.core._docs_decorators import public_api
@@ -10,6 +10,7 @@
     BatchSpec,
 )
 from great_expectations.core.batch_spec import PathBatchSpec
+from great_expectations.core.util import AzureUrl, DBFSPath, GCSUrl, S3Url
 from great_expectations.datasource.data_connector.batch_filter import (
     BatchFilter,
     build_batch_filter,
@@ -27,6 +28,87 @@
 logger = logging.getLogger(__name__)
 
 
+class DataConnectorStorageDataReferenceResolver:
+    DATA_CONNECTOR_NAME_TO_STORAGE_NAME_MAP: Dict[str, str] = {
+        "InferredAssetS3DataConnector": "S3",
+        "ConfiguredAssetS3DataConnector": "S3",
+        "InferredAssetGCSDataConnector": "GCS",
+        "ConfiguredAssetGCSDataConnector": "GCS",
+        "InferredAssetAzureDataConnector": "ABS",
+        "ConfiguredAssetAzureDataConnector": "ABS",
+        "InferredAssetDBFSDataConnector": "DBFS",
+        "ConfiguredAssetDBFSDataConnector": "DBFS",
+    }
+    STORAGE_NAME_EXECUTION_ENGINE_NAME_PATH_RESOLVERS: Dict[
+        Tuple[str, str], Callable
+    ] = {
+        (
+            "S3",
+            "PandasExecutionEngine",
+        ): lambda template_arguments: S3Url.OBJECT_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "S3",
+            "SparkDFExecutionEngine",
+        ): lambda template_arguments: S3Url.OBJECT_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "GCS",
+            "PandasExecutionEngine",
+        ): lambda template_arguments: GCSUrl.OBJECT_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "GCS",
+            "SparkDFExecutionEngine",
+        ): lambda template_arguments: GCSUrl.OBJECT_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "ABS",
+            "PandasExecutionEngine",
+        ): lambda template_arguments: AzureUrl.AZURE_BLOB_STORAGE_HTTPS_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "ABS",
+            "SparkDFExecutionEngine",
+        ): lambda template_arguments: AzureUrl.AZURE_BLOB_STORAGE_WASBS_URL_TEMPLATE.format(
+            **template_arguments
+        ),
+        (
+            "DBFS",
+            "SparkDFExecutionEngine",
+        ): lambda template_arguments: DBFSPath.convert_to_protocol_version(
+            **template_arguments
+        ),
+        (
+            "DBFS",
+            "PandasExecutionEngine",
+        ): lambda template_arguments: DBFSPath.convert_to_file_semantics_version(
+            **template_arguments
+        ),
+    }
+
+    @staticmethod
+    def resolve_data_reference(
+        data_connector_name: str,
+        execution_engine_name: str,
+        template_arguments: dict,
+    ):
+        """Resolve file path for a (data_connector_name, execution_engine_name) combination."""
+        storage_name: str = DataConnectorStorageDataReferenceResolver.DATA_CONNECTOR_NAME_TO_STORAGE_NAME_MAP[
+            data_connector_name
+        ]
+        return DataConnectorStorageDataReferenceResolver.STORAGE_NAME_EXECUTION_ENGINE_NAME_PATH_RESOLVERS[
+            (storage_name, execution_engine_name)
+        ](
+            template_arguments
+        )
+
+
 @public_api
 class FilePathDataConnector(DataConnector):
     """The base class for Data Connectors designed to access filesystem-like data.
@@ -261,6 +343,14 @@ def build_batch_spec(self, batch_definition: BatchDefinition) -> PathBatchSpec:
         )
         return PathBatchSpec(batch_spec)
 
+    def resolve_data_reference(self, template_arguments: dict):
+        """Resolve file path for a (data_connector_name, execution_engine_name) combination."""
+        return DataConnectorStorageDataReferenceResolver.resolve_data_reference(
+            data_connector_name=self.__class__.__name__,
+            execution_engine_name=self._execution_engine.__class__.__name__,
+            template_arguments=template_arguments,
+        )
+
     def _generate_batch_spec_parameters_from_batch_definition(
         self, batch_definition: BatchDefinition
     ) -> dict:
diff --git a/great_expectations/datasource/data_connector/util.py b/great_expectations/datasource/data_connector/util.py
--- a/great_expectations/datasource/data_connector/util.py
+++ b/great_expectations/datasource/data_connector/util.py
@@ -1,4 +1,4 @@
-# Utility methods for dealing with DataConnector objects
+from __future__ import annotations
 
 import copy
 import logging
@@ -65,11 +65,13 @@ def batch_definition_matches_batch_request(
         and batch_request.datasource_name != batch_definition.datasource_name
     ):
         return False
+
     if (
         batch_request.data_connector_name
         and batch_request.data_connector_name != batch_definition.data_connector_name
     ):
         return False
+
     if (
         batch_request.data_asset_name
         and batch_request.data_asset_name != batch_definition.data_asset_name
@@ -272,14 +274,12 @@ def _invert_regex_to_data_reference_template(
         if token == sre_constants.LITERAL:
             # Transcribe the character directly into the template
             data_reference_template += chr(value)
-
         elif token == sre_constants.SUBPATTERN:
             if not (group_name_index < num_groups):
                 break
             # Replace the captured group with "{next_group_name}" in the template
             data_reference_template += f"{{{group_names[group_name_index]}}}"
             group_name_index += 1
-
         elif token in [
             sre_constants.MAX_REPEAT,
             sre_constants.IN,
@@ -288,7 +288,6 @@ def _invert_regex_to_data_reference_template(
         ]:
             # Replace the uncaptured group a wildcard in the template
             data_reference_template += "*"
-
         elif token in [
             sre_constants.AT,
             sre_constants.ASSERT_NOT,
@@ -334,10 +333,12 @@ def sanitize_prefix_for_s3(text: str) -> str:
     path_parts = text.split("/")
     if not path_parts:  # Empty prefix
         return text
-    elif "." in path_parts[-1]:  # File, not folder
+
+    if "." in path_parts[-1]:  # File, not folder
         return text
-    else:  # Folder, should have trailing /
-        return f"{text.rstrip('/')}/"
+
+    # Folder, should have trailing /
+    return f"{text.rstrip('/')}/"
 
 
 def normalize_directory_path(
@@ -401,6 +402,7 @@ def _walk_blob_hierarchy(name_starts_with: str) -> None:
             if isinstance(item, BlobPrefix):
                 if recursive:
                     _walk_blob_hierarchy(name_starts_with=item.name)
+
             else:
                 path_list.append(item.name)
 
@@ -466,6 +468,7 @@ def list_gcs_keys(
         name: str = blob.name
         if name.endswith("/"):  # GCS includes directories in blob output
             continue
+
         keys.append(name)
 
     return keys
@@ -505,6 +508,7 @@ def list_s3_keys(
             item["Key"] for item in s3_objects_info["Contents"] if item["Size"] > 0
         ]
         yield from keys
+
     if recursive and "CommonPrefixes" in s3_objects_info:
         common_prefixes: List[Dict[str, Any]] = s3_objects_info["CommonPrefixes"]
         for prefix_info in common_prefixes:
@@ -517,6 +521,7 @@ def list_s3_keys(
                 iterator_dict={},
                 recursive=recursive,
             )
+
     if s3_objects_info["IsTruncated"]:
         iterator_dict["continuation_token"] = s3_objects_info["NextContinuationToken"]
         # Recursively fetch more
@@ -543,8 +548,10 @@ def build_sorters_from_config(config_list: List[Dict[str, Any]]) -> Optional[dic
             # if sorters were not configured
             if sorter_config is None:
                 return None
+
             if "name" not in sorter_config:
                 raise ValueError("Sorter config should have a name")
+
             sorter_name: str = sorter_config["name"]
             new_sorter: Sorter = _build_sorter_from_config(sorter_config=sorter_config)
             sorter_dict[sorter_name] = new_sorter
@@ -565,11 +572,9 @@ def _build_sorter_from_config(sorter_config: Dict[str, Any]) -> Sorter:
     return sorter
 
 
-def _build_asset_from_config(
-    runtime_environment: "DataConnector", config: dict
-) -> Asset:
+def _build_asset_from_config(runtime_environment: DataConnector, config: dict) -> Asset:
     """Build Asset from configuration and return asset. Used by both ConfiguredAssetDataConnector and RuntimeDataConnector"""
-    runtime_environment_dict: Dict[str, "DataConnector"] = {
+    runtime_environment_dict: Dict[str, DataConnector] = {
         "data_connector": runtime_environment
     }
     config = assetConfigSchema.load(config)


Test patch: