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/util.py b/great_expectations/core/util.py
--- a/great_expectations/core/util.py
+++ b/great_expectations/core/util.py
@@ -406,25 +406,51 @@ def datetime_to_int(dt: datetime.date) -> int:
 
 class AzureUrl:
     """
-    Parses an Azure Blob Storage URL into its separate components
-    Format: <ACCOUNT_NAME>.blob.core.windows.net/<CONTAINER>/<BLOB>
+    Parses an Azure Blob Storage URL into its separate components.
+    Formats:
+        WASBS (for Spark): "wasbs://<CONTAINER>@<ACCOUNT_NAME>.blob.core.windows.net/<BLOB>"
+        HTTP(S) (for Pandas) "<ACCOUNT_NAME>.blob.core.windows.net/<CONTAINER>/<BLOB>"
+
+        Reference: WASBS -- Windows Azure Storage Blob (https://datacadamia.com/azure/wasb).
     """
 
     def __init__(self, url: str):
-        search = re.search(
-            r"^(?:https?://)?(.+?).blob.core.windows.net/([^/]+)/(.+)$", url
-        )
-        assert (
-            search is not None
-        ), "The provided URL does not adhere to the format specified by the Azure SDK (<ACCOUNT_NAME>.blob.core.windows.net/<CONTAINER>/<BLOB>)"
-        self._account_name = search.group(1)
-        self._container = search.group(2)
-        self._blob = search.group(3)
+        search = re.search(r"^[^@]+@.+\.blob\.core\.windows\.net\/.+$", url)
+        if search is None:
+            search = re.search(
+                r"^(https?:\/\/)?(.+?).blob.core.windows.net/([^/]+)/(.+)$", url
+            )
+            assert (
+                search is not None
+            ), "The provided URL does not adhere to the format specified by the Azure SDK (<ACCOUNT_NAME>.blob.core.windows.net/<CONTAINER>/<BLOB>)"
+            self._protocol = search.group(1)
+            self._account_name = search.group(2)
+            self._container = search.group(3)
+            self._blob = search.group(4)
+        else:
+            search = re.search(
+                r"^(wasbs?:\/\/)?([^/]+)@(.+?).blob.core.windows.net/(.+)$", url
+            )
+            assert (
+                search is not None
+            ), "The provided URL does not adhere to the format specified by the Azure SDK (wasbs://<CONTAINER>@<ACCOUNT_NAME>.blob.core.windows.net/<BLOB>)"
+            self._protocol = search.group(1)
+            self._container = search.group(2)
+            self._account_name = search.group(3)
+            self._blob = search.group(4)
+
+    @property
+    def protocol(self):
+        return self._protocol
 
     @property
     def account_name(self):
         return self._account_name
 
+    @property
+    def account_url(self):
+        return f"{self.account_name}.blob.core.windows.net"
+
     @property
     def container(self):
         return self._container


Test patch:
diff --git a/tests/core/test_util.py b/tests/core/test_util.py
--- a/tests/core/test_util.py
+++ b/tests/core/test_util.py
@@ -65,43 +65,80 @@ def test_sniff_s3_compression(url, expected):
     assert sniff_s3_compression(S3Url(url)) == expected
 
 
-def test_azure_url():
+def test_azure_pandas_url():
     url = AzureUrl("my_account.blob.core.windows.net/my_container/my_blob")
     assert url.account_name == "my_account"
     assert url.container == "my_container"
     assert url.blob == "my_blob"
 
 
-def test_azure_url_with_https():
+def test_azure_pandas_url_with_https():
     url = AzureUrl("https://my_account.blob.core.windows.net/my_container/my_blob")
     assert url.account_name == "my_account"
     assert url.container == "my_container"
     assert url.blob == "my_blob"
 
 
-def test_azure_url_with_nested_blob():
+def test_azure_pandas_url_with_nested_blob():
     url = AzureUrl("my_account.blob.core.windows.net/my_container/a/b/c/d/e/my_blob")
     assert url.account_name == "my_account"
     assert url.container == "my_container"
     assert url.blob == "a/b/c/d/e/my_blob"
 
 
-def test_azure_url_with_invalid_url():
-    with pytest.raises(AssertionError):
-        AzureUrl("my_bucket/my_blob")
+def test_azure_pandas_url_with_special_chars():
+    # Note that `url` conforms with the naming restrictions set by the Azure API
+    # Azure naming restrictions: https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
+    url = AzureUrl(
+        "my_account.blob.core.windows.net/my-container_1.0/my-blob_`~!@#$%^&*()=+"
+    )
+    assert url.account_name == "my_account"
+    assert url.account_url == "my_account.blob.core.windows.net"
+    assert url.container == "my-container_1.0"
+    assert url.blob == "my-blob_`~!@#$%^&*()=+"
+
+
+def test_azure_spark_url():
+    url = AzureUrl("my_container@my_account.blob.core.windows.net/my_blob")
+    assert url.account_name == "my_account"
+    assert url.account_url == "my_account.blob.core.windows.net"
+    assert url.container == "my_container"
+    assert url.blob == "my_blob"
 
 
-def test_azure_url_with_special_chars():
+def test_azure_spark_url_with_wasbs():
+    url = AzureUrl("wasbs://my_container@my_account.blob.core.windows.net/my_blob")
+    assert url.account_name == "my_account"
+    assert url.account_url == "my_account.blob.core.windows.net"
+    assert url.container == "my_container"
+    assert url.blob == "my_blob"
+
+
+def test_azure_spark_url_with_nested_blob():
+    url = AzureUrl("my_container@my_account.blob.core.windows.net/a/b/c/d/e/my_blob")
+    assert url.account_name == "my_account"
+    assert url.account_url == "my_account.blob.core.windows.net"
+    assert url.container == "my_container"
+    assert url.blob == "a/b/c/d/e/my_blob"
+
+
+def test_azure_spark_url_with_special_chars():
     # Note that `url` conforms with the naming restrictions set by the Azure API
     # Azure naming restrictions: https://docs.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
     url = AzureUrl(
-        "my_account.blob.core.windows.net/my-container_1.0/my-blob_`~!@#$%^&*()=+"
+        "my-container_1.0@my_account.blob.core.windows.net/my-blob_`~!@#$%^&*()=+"
     )
     assert url.account_name == "my_account"
+    assert url.account_url == "my_account.blob.core.windows.net"
     assert url.container == "my-container_1.0"
     assert url.blob == "my-blob_`~!@#$%^&*()=+"
 
 
+def test_azure_url_with_invalid_url():
+    with pytest.raises(AssertionError):
+        AzureUrl("my_bucket/my_blob")
+
+
 def test_gcs_url():
     url = GCSUrl("gs://my_bucket/my_blob")
     assert url.bucket == "my_bucket"