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/data_context/data_context.py b/great_expectations/data_context/data_context.py
--- a/great_expectations/data_context/data_context.py
+++ b/great_expectations/data_context/data_context.py
@@ -1260,6 +1260,12 @@ def _build_datasource_from_config(
         if isinstance(config, DatasourceConfig):
             config = datasourceConfigSchema.dump(config)
         config.update({"name": name})
+        # While the new Datasource classes accept "data_context_root_directory", the Legacy Datasource classes do not.
+        if config["class_name"] in [
+            "BaseDatasource",
+            "Datasource",
+        ]:
+            config.update({"data_context_root_directory": self.root_directory})
         module_name = "great_expectations.datasource"
         datasource = instantiate_class_from_config(
             config=config,
diff --git a/great_expectations/datasource/data_connector/configured_asset_filesystem_data_connector.py b/great_expectations/datasource/data_connector/configured_asset_filesystem_data_connector.py
--- a/great_expectations/datasource/data_connector/configured_asset_filesystem_data_connector.py
+++ b/great_expectations/datasource/data_connector/configured_asset_filesystem_data_connector.py
@@ -66,13 +66,12 @@ def _get_full_file_path_for_asset(
         if asset is not None:
             if asset.base_directory:
                 base_directory = normalize_directory_path(
-                    dir_path=asset.base_directory,
-                    root_directory_path=self.data_context_root_directory,
+                    dir_path=asset.base_directory, root_directory_path=base_directory,
                 )
         return str(Path(base_directory).joinpath(path))
 
     @property
-    def base_directory(self):
+    def base_directory(self) -> str:
         return normalize_directory_path(
             dir_path=self._base_directory,
             root_directory_path=self.data_context_root_directory,
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
@@ -225,7 +225,7 @@ def normalize_directory_path(
     if Path(dir_path).is_absolute() or root_directory_path is None:
         return dir_path
     else:
-        return Path(root_directory_path).joinpath(dir_path)
+        return str(Path(root_directory_path).joinpath(dir_path))
 
 
 def get_filesystem_one_level_directory_glob_path_list(


Test patch:
diff --git a/tests/data_context/test_data_context_v3.py b/tests/data_context/test_data_context_v3.py
--- a/tests/data_context/test_data_context_v3.py
+++ b/tests/data_context/test_data_context_v3.py
@@ -176,3 +176,73 @@ def test_conveying_splitting_and_sampling_directives_from_data_context_to_pandas
     )
     df_data = df_data[df_data["belongs_in_split"]]
     assert df_data.drop("belongs_in_split", axis=1).shape == (4, 10)
+
+
+def test_relative_data_connector_default_and_relative_asset_base_directory_paths(
+    empty_data_context_v3, test_df, tmp_path_factory
+):
+    context = empty_data_context_v3
+
+    create_files_in_directory(
+        directory=context.root_directory,
+        file_name_list=[
+            "test_dir_0/A/B/C/logfile_0.csv",
+            "test_dir_0/A/B/C/bigfile_1.csv",
+            "test_dir_0/A/filename2.csv",
+            "test_dir_0/A/filename3.csv",
+        ],
+        file_content_fn=lambda: test_df.to_csv(header=True, index=False),
+    )
+
+    yaml_config = f"""
+class_name: Datasource
+
+execution_engine:
+    class_name: PandasExecutionEngine
+
+data_connectors:
+    my_filesystem_data_connector:
+        class_name: ConfiguredAssetFilesystemDataConnector
+        base_directory: test_dir_0/A
+        glob_directive: "*"
+        default_regex:
+            pattern: (.+)\\.csv
+            group_names:
+            - name
+
+        assets:
+            A:
+                base_directory: B/C
+                glob_directive: "log*.csv"
+                pattern: (.+)_(\\d+)\\.csv
+                group_names:
+                - name
+                - number
+"""
+    my_datasource = context.test_yaml_config(
+        name="my_directory_datasource", yaml_config=yaml_config,
+    )
+    assert (
+        my_datasource.data_connectors["my_filesystem_data_connector"].base_directory
+        == f"{context.root_directory}/test_dir_0/A"
+    )
+    assert (
+        my_datasource.data_connectors[
+            "my_filesystem_data_connector"
+        ]._get_full_file_path_for_asset(
+            path="bigfile_1.csv",
+            asset=my_datasource.data_connectors["my_filesystem_data_connector"].assets[
+                "A"
+            ],
+        )
+        == f"{context.root_directory}/test_dir_0/A/B/C/bigfile_1.csv"
+    )
+
+    my_batch = context.get_batch(
+        datasource_name="my_directory_datasource",
+        data_connector_name="my_filesystem_data_connector",
+        data_asset_name="A",
+    )
+
+    df_data = my_batch.data
+    assert df_data.shape == (120, 10)
diff --git a/tests/datasource/data_connector/test_configured_asset_filesystem_data_connector.py b/tests/datasource/data_connector/test_configured_asset_filesystem_data_connector.py
--- a/tests/datasource/data_connector/test_configured_asset_filesystem_data_connector.py
+++ b/tests/datasource/data_connector/test_configured_asset_filesystem_data_connector.py
@@ -704,6 +704,173 @@ def test_foxtrot(tmp_path_factory):
     assert len(my_batch_definition_list) == 3
 
 
+def test_relative_asset_base_directory_path(tmp_path_factory):
+    base_directory = str(
+        tmp_path_factory.mktemp("test_relative_asset_base_directory_path")
+    )
+    create_files_in_directory(
+        directory=base_directory,
+        file_name_list=[
+            "test_dir_0/A/B/C/logfile_0.csv",
+            "test_dir_0/A/B/C/bigfile_1.csv",
+            "test_dir_0/A/filename2.csv",
+            "test_dir_0/A/filename3.csv",
+        ],
+    )
+
+    my_data_connector_yaml = yaml.load(
+        f"""
+            module_name: great_expectations.datasource.data_connector
+            class_name: ConfiguredAssetFilesystemDataConnector
+            base_directory: {base_directory}/test_dir_0/A
+            glob_directive: "*"
+            default_regex:
+              pattern: (.+)\\.csv
+              group_names:
+              - name
+
+            assets:
+              A:
+                base_directory: B/C
+                glob_directive: "log*.csv"
+                pattern: (.+)_(\\d+)\\.csv
+                group_names:
+                - name
+                - number
+        """,
+    )
+
+    my_data_connector: ConfiguredAssetFilesystemDataConnector = instantiate_class_from_config(
+        config=my_data_connector_yaml,
+        runtime_environment={
+            "name": "my_configured_asset_filesystem_data_connector",
+            "datasource_name": "BASE",
+        },
+        config_defaults={"module_name": "great_expectations.datasource.data_connector"},
+    )
+    my_data_connector.data_context_root_directory = base_directory
+
+    assert (
+        my_data_connector._get_full_file_path_for_asset(
+            path="bigfile_1.csv", asset=my_data_connector.assets["A"]
+        )
+        == f"{base_directory}/test_dir_0/A/B/C/bigfile_1.csv"
+    )
+    self_check_report = my_data_connector.self_check()
+    assert self_check_report == {
+        "class_name": "ConfiguredAssetFilesystemDataConnector",
+        "data_asset_count": 1,
+        "example_data_asset_names": ["A"],
+        "data_assets": {
+            "A": {
+                "batch_definition_count": 1,
+                "example_data_references": ["logfile_0.csv"],
+            }
+        },
+        "unmatched_data_reference_count": 0,
+        "example_unmatched_data_references": [],
+        "example_data_reference": {},
+    }
+
+    my_batch_definition_list: List[BatchDefinition]
+    my_batch_definition: BatchDefinition
+    my_batch_request = BatchRequest(
+        datasource_name="BASE",
+        data_connector_name="my_configured_asset_filesystem_data_connector",
+        data_asset_name="A",
+        partition_request=None,
+    )
+    my_batch_definition_list = my_data_connector.get_batch_definition_list_from_batch_request(
+        batch_request=my_batch_request
+    )
+    assert len(my_batch_definition_list) == 1
+
+
+def test_relative_default_and_relative_asset_base_directory_paths(tmp_path_factory):
+    base_directory = str(
+        tmp_path_factory.mktemp(
+            "test_relative_default_and_relative_asset_base_directory_paths"
+        )
+    )
+    create_files_in_directory(
+        directory=base_directory,
+        file_name_list=[
+            "test_dir_0/A/B/C/logfile_0.csv",
+            "test_dir_0/A/B/C/bigfile_1.csv",
+            "test_dir_0/A/filename2.csv",
+            "test_dir_0/A/filename3.csv",
+        ],
+    )
+
+    my_data_connector_yaml = yaml.load(
+        f"""
+            module_name: great_expectations.datasource.data_connector
+            class_name: ConfiguredAssetFilesystemDataConnector
+            base_directory: test_dir_0/A
+            glob_directive: "*"
+            default_regex:
+              pattern: (.+)\\.csv
+              group_names:
+              - name
+
+            assets:
+              A:
+                base_directory: B/C
+                glob_directive: "log*.csv"
+                pattern: (.+)_(\\d+)\\.csv
+                group_names:
+                - name
+                - number
+        """,
+    )
+
+    my_data_connector: ConfiguredAssetFilesystemDataConnector = instantiate_class_from_config(
+        config=my_data_connector_yaml,
+        runtime_environment={
+            "name": "my_configured_asset_filesystem_data_connector",
+            "datasource_name": "BASE",
+        },
+        config_defaults={"module_name": "great_expectations.datasource.data_connector"},
+    )
+    my_data_connector.data_context_root_directory = base_directory
+
+    assert my_data_connector.base_directory == f"{base_directory}/test_dir_0/A"
+    assert (
+        my_data_connector._get_full_file_path_for_asset(
+            path="bigfile_1.csv", asset=my_data_connector.assets["A"]
+        )
+        == f"{base_directory}/test_dir_0/A/B/C/bigfile_1.csv"
+    )
+    self_check_report = my_data_connector.self_check()
+    assert self_check_report == {
+        "class_name": "ConfiguredAssetFilesystemDataConnector",
+        "data_asset_count": 1,
+        "example_data_asset_names": ["A"],
+        "data_assets": {
+            "A": {
+                "batch_definition_count": 1,
+                "example_data_references": ["logfile_0.csv"],
+            }
+        },
+        "unmatched_data_reference_count": 0,
+        "example_unmatched_data_references": [],
+        "example_data_reference": {},
+    }
+
+    my_batch_definition_list: List[BatchDefinition]
+    my_batch_definition: BatchDefinition
+    my_batch_request = BatchRequest(
+        datasource_name="BASE",
+        data_connector_name="my_configured_asset_filesystem_data_connector",
+        data_asset_name="A",
+        partition_request=None,
+    )
+    my_batch_definition_list = my_data_connector.get_batch_definition_list_from_batch_request(
+        batch_request=my_batch_request
+    )
+    assert len(my_batch_definition_list) == 1
+
+
 def test_return_all_batch_definitions_sorted_sorter_named_that_does_not_match_group(
     tmp_path_factory,
 ):