Skip to content

Commit 698602f

Browse files
authored
add AzurePath, tests (#65)
1 parent f551c59 commit 698602f

File tree

7 files changed

+122
-7
lines changed

7 files changed

+122
-7
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ For more examples, see the [example notebook here](notebooks/examples.ipynb)
4141

4242
- `file:` Local filessystem
4343
- `memory:` Ephemeral filesystem in RAM
44+
- `az:`, `adl:` and `abfs:` Azure Storage (requires `adlfs` to be installed)
4445
- `http:` and `https:` HTTP(S)-based filesystem
4546
- `hdfs:` Hadoop distributed filesystem
4647
- `gs:` and `gcs:` Google Cloud Storage (requires `gcsfs` to be installed)

noxfile.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ def install(session):
2929
def smoke(session):
3030
session.install(
3131
"pytest",
32+
"adlfs",
3233
"aiohttp",
3334
"requests",
3435
"gcsfs",

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ dev = []
2626
doc = []
2727
test = [
2828
"aiohttp",
29+
"adlfs",
2930
"flake8",
3031
"gcsfs",
3132
"hadoop-test-cluster",

upath/implementations/cloud.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,7 @@ class GCSPath(CloudPath):
6161

6262
class S3Path(CloudPath):
6363
pass
64+
65+
66+
class AzurePath(CloudPath):
67+
pass

upath/registry.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,20 @@
66

77

88
class _Registry:
9-
from upath.implementations import hdfs, http, memory, cloud, webdav
9+
from upath.implementations import cloud, hdfs, http, memory, webdav
1010

1111
known_implementations: Dict[str, Type[UPath]] = {
12-
"https": http.HTTPPath,
13-
"http": http.HTTPPath,
12+
"abfs": cloud.AzurePath,
13+
"adl": cloud.AzurePath,
14+
"az": cloud.AzurePath,
15+
"gcs": cloud.GCSPath,
16+
"gs": cloud.GCSPath,
1417
"hdfs": hdfs.HDFSPath,
15-
"s3a": cloud.S3Path,
16-
"s3": cloud.S3Path,
18+
"http": http.HTTPPath,
19+
"https": http.HTTPPath,
1720
"memory": memory.MemoryPath,
18-
"gs": cloud.GCSPath,
19-
"gcs": cloud.GCSPath,
21+
"s3": cloud.S3Path,
22+
"s3a": cloud.S3Path,
2023
"webdav+http": webdav.WebdavPath,
2124
"webdav+https": webdav.WebdavPath,
2225
}

upath/tests/conftest.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
from fsspec.implementations.local import LocalFileSystem
1313
from fsspec.registry import register_implementation, _registry
1414

15+
from azure.storage.blob import BlobServiceClient
16+
1517
import fsspec
1618

1719
from .utils import posixify
@@ -334,3 +336,62 @@ def webdav_fixture(local_testdir, webdav_server):
334336
shutil.rmtree(webdav_path)
335337
shutil.copytree(local_testdir, webdav_path)
336338
yield webdav_url
339+
340+
341+
@pytest.fixture(scope="session")
342+
def azurite_credentials():
343+
url = "http://localhost:10000"
344+
account_name = "devstoreaccount1"
345+
key = "Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==" # noqa: E501
346+
endpoint = f"{url}/{account_name}"
347+
connection_string = f"DefaultEndpointsProtocol=http;AccountName={account_name};AccountKey={key};BlobEndpoint={endpoint};" # noqa
348+
349+
yield account_name, connection_string
350+
351+
352+
@pytest.fixture(scope="session")
353+
def docker_azurite(azurite_credentials):
354+
requests = pytest.importorskip("requests")
355+
356+
image = "mcr.microsoft.com/azure-storage/azurite"
357+
container_name = "azure_test"
358+
cmd = (
359+
f"docker run --rm -d -p 10000:10000 --name {container_name} {image}" # noqa: E501
360+
" azurite-blob --loose --blobHost 0.0.0.0" # noqa: E501
361+
)
362+
url = "http://localhost:10000"
363+
364+
stop_docker(container_name)
365+
subprocess.run(shlex.split(cmd), check=True)
366+
367+
retries = 10
368+
while True:
369+
try:
370+
# wait until the container is up, even a 400 status code is ok
371+
r = requests.get(url, timeout=10)
372+
if (
373+
r.status_code == 400
374+
and "Server" in r.headers
375+
and "Azurite" in r.headers["Server"]
376+
):
377+
yield url
378+
break
379+
except Exception as e: # noqa: E722
380+
retries -= 1
381+
if retries < 0:
382+
raise SystemError from e
383+
time.sleep(1)
384+
385+
stop_docker(container_name)
386+
387+
388+
@pytest.fixture(scope="session")
389+
def azure_fixture(azurite_credentials, docker_azurite):
390+
account_name, connection_string = azurite_credentials
391+
client = BlobServiceClient.from_connection_string(
392+
conn_str=connection_string
393+
)
394+
container_name = "data"
395+
client.create_container(container_name)
396+
397+
yield f"az://{container_name}"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import pytest
2+
from upath import UPath
3+
from upath.errors import NotDirectoryError
4+
from upath.implementations.cloud import AzurePath
5+
6+
from ..cases import BaseTests
7+
from ..utils import skip_on_windows
8+
9+
10+
@skip_on_windows
11+
@pytest.mark.usefixtures("path")
12+
class TestAzurePath(BaseTests):
13+
@pytest.fixture(autouse=True, scope="function")
14+
def path(self, azurite_credentials, azure_fixture):
15+
account_name, connection_string = azurite_credentials
16+
17+
self.storage_options = {
18+
"account_name": account_name,
19+
"connection_string": connection_string,
20+
}
21+
self.path = UPath(azure_fixture, **self.storage_options)
22+
self.prepare_file_system()
23+
24+
def test_is_AzurePath(self):
25+
assert isinstance(self.path, AzurePath)
26+
27+
def test_mkdir(self):
28+
new_dir = self.path / "new_dir"
29+
new_dir.mkdir()
30+
(new_dir / "test.txt").touch()
31+
assert new_dir.exists()
32+
33+
def test_rmdir(self):
34+
new_dir = self.path / "new_dir"
35+
new_dir.mkdir()
36+
path = new_dir / "test.txt"
37+
path.write_text("hello")
38+
assert path.exists()
39+
new_dir.fs.invalidate_cache()
40+
new_dir.rmdir()
41+
assert not new_dir.exists()
42+
43+
with pytest.raises(NotDirectoryError):
44+
(self.path / "a" / "file.txt").rmdir()

0 commit comments

Comments
 (0)