Skip to content

Commit f6e9adc

Browse files
joouhaap--
andauthored
Override path formatting method for data-URIs (#169)
* Override path formatting method for data-URIs This fixes an issue with `UPath.stat()` for data URIs, where fsspec was expecting the full URI to be passed to `fs.info` instead of just the URI path as was previously implemented. * Update registry tests * upath.implementations.data: adjust DataPath and add tests * tests: xfail data tests when fsspec is too old --------- Co-authored-by: Andreas Poehlmann <[email protected]>
1 parent a98c507 commit f6e9adc

File tree

4 files changed

+208
-0
lines changed

4 files changed

+208
-0
lines changed

upath/implementations/data.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
from __future__ import annotations
2+
3+
import upath.core
4+
5+
6+
class DataPath(upath.core.UPath):
7+
8+
@property
9+
def parts(self):
10+
return (self.path,)
11+
12+
def __str__(self):
13+
return self.path
14+
15+
def with_segments(self, *pathsegments):
16+
raise NotImplementedError("path operation not supported by DataPath")
17+
18+
def mkdir(self, mode=0o777, parents=False, exist_ok=False):
19+
raise FileExistsError(str(self))
20+
21+
def write_bytes(self, data):
22+
raise NotImplementedError("DataPath does not support writing")
23+
24+
def write_text(self, data, **kwargs):
25+
raise NotImplementedError("DataPath does not support writing")

upath/registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class _Registry(MutableMapping[str, "type[upath.UPath]"]):
6565
"abfss": "upath.implementations.cloud.AzurePath",
6666
"adl": "upath.implementations.cloud.AzurePath",
6767
"az": "upath.implementations.cloud.AzurePath",
68+
"data": "upath.implementations.data.DataPath",
6869
"file": "upath.implementations.local.FilePath",
6970
"local": "upath.implementations.local.FilePath",
7071
"gcs": "upath.implementations.cloud.GCSPath",
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
import stat
2+
3+
import fsspec
4+
import pytest
5+
6+
from upath import UPath
7+
from upath.implementations.data import DataPath
8+
from upath.tests.cases import BaseTests
9+
10+
from ..utils import xfail_if_version
11+
12+
pytestmark = xfail_if_version(
13+
"fsspec", lt="2023.12.2", reason="fsspec<2023.12.2 does not support data"
14+
)
15+
16+
17+
class TestUPathDataPath(BaseTests):
18+
"""
19+
Unit-tests for the DataPath implementation of UPath.
20+
"""
21+
22+
@pytest.fixture(autouse=True)
23+
def path(self):
24+
"""
25+
Fixture for the UPath instance to be tested.
26+
"""
27+
path = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVQI12PYeuECAASTAlbqXbfWAAAAAElFTkSuQmCC" # noqa: E501
28+
self.path = UPath(path)
29+
30+
def test_is_DataPath(self):
31+
"""
32+
Test that the path is a GitHubPath instance.
33+
"""
34+
assert isinstance(self.path, DataPath)
35+
36+
@pytest.mark.skip(reason="DataPath does not have directories")
37+
def test_stat_dir_st_mode(self):
38+
super().test_stat_dir_st_mode()
39+
40+
def test_stat_file_st_mode(self):
41+
assert self.path.is_file()
42+
assert stat.S_ISREG(self.path.stat().st_mode)
43+
44+
def test_stat_st_size(self):
45+
assert self.path.stat().st_size == 69
46+
47+
def test_exists(self):
48+
# datapath exists is always true...
49+
path = self.path
50+
assert path.exists()
51+
52+
@pytest.mark.skip(reason="DataPath does support joins or globs")
53+
def test_glob(self, pathlib_base):
54+
with pytest.raises(NotImplementedError):
55+
pathlib_base.glob("*")
56+
57+
def test_is_dir(self):
58+
assert not self.path.is_dir()
59+
60+
def test_is_file(self):
61+
assert self.path.is_file()
62+
63+
def test_iterdir(self):
64+
with pytest.raises(NotImplementedError):
65+
list(self.path.iterdir())
66+
67+
@pytest.mark.skip(reason="DataPath does not have directories")
68+
def test_iterdir2(self):
69+
pass
70+
71+
@pytest.mark.skip(reason="DataPath does not have directories")
72+
def test_iterdir_trailing_slash(self):
73+
pass
74+
75+
def test_mkdir(self):
76+
with pytest.raises(FileExistsError):
77+
self.path.mkdir()
78+
79+
@pytest.mark.skip(reason="DataPath does not have directories")
80+
def test_mkdir_exists_ok_true(self):
81+
pass
82+
83+
@pytest.mark.skip(reason="DataPath does not have directories")
84+
def test_mkdir_exists_ok_false(self):
85+
pass
86+
87+
@pytest.mark.skip(reason="DataPath does not have directories")
88+
def test_mkdir_parents_true_exists_ok_true(self):
89+
pass
90+
91+
@pytest.mark.skip(reason="DataPath does not have directories")
92+
def test_mkdir_parents_true_exists_ok_false(self):
93+
pass
94+
95+
def test_read_bytes(self, pathlib_base):
96+
assert len(self.path.read_bytes()) == 69
97+
98+
def test_read_text(self, local_testdir):
99+
assert UPath("data:base64,SGVsbG8gV29ybGQ=").read_text() == "Hello World"
100+
101+
def test_parents(self):
102+
with pytest.raises(NotImplementedError):
103+
self.path.parents[0]
104+
105+
def test_rename(self):
106+
with pytest.raises(NotImplementedError):
107+
self.path.rename("newname")
108+
109+
def test_rename2(self):
110+
self.path.rename(self.path)
111+
112+
def test_rglob(self, pathlib_base):
113+
with pytest.raises(NotImplementedError):
114+
list(self.path.rglob("*"))
115+
116+
def test_touch_unlink(self):
117+
with pytest.raises(NotImplementedError):
118+
self.path.touch()
119+
with pytest.raises(NotImplementedError):
120+
self.path.unlink()
121+
122+
def test_write_bytes(self, pathlib_base):
123+
with pytest.raises(NotImplementedError):
124+
self.path.write_bytes(b"test")
125+
126+
def test_write_text(self, pathlib_base):
127+
with pytest.raises(NotImplementedError):
128+
self.path.write_text("test")
129+
130+
def test_read_with_fsspec(self):
131+
pth = self.path
132+
fs = fsspec.filesystem(pth.protocol, **pth.storage_options)
133+
assert fs.cat_file(pth.path) == pth.read_bytes()
134+
135+
@pytest.mark.skip(reason="DataPath does not support joins")
136+
def test_pickling_child_path(self):
137+
pass
138+
139+
@pytest.mark.skip(reason="DataPath does not support joins")
140+
def test_child_path(self):
141+
pass
142+
143+
def test_with_name(self):
144+
with pytest.raises(NotImplementedError):
145+
self.path.with_name("newname")
146+
147+
def test_with_suffix(self):
148+
with pytest.raises(NotImplementedError):
149+
self.path.with_suffix(".new")
150+
151+
def test_with_stem(self):
152+
with pytest.raises(NotImplementedError):
153+
self.path.with_stem("newname")
154+
155+
@pytest.mark.skip(reason="DataPath does not support joins")
156+
def test_repr_after_with_suffix(self):
157+
pass
158+
159+
@pytest.mark.skip(reason="DataPath does not support joins")
160+
def test_repr_after_with_name(self):
161+
pass
162+
163+
@pytest.mark.skip(reason="DataPath does not support directories")
164+
def test_rmdir_no_dir(self):
165+
pass
166+
167+
@pytest.mark.skip(reason="DataPath does not support directories")
168+
def test_iterdir_no_dir(self):
169+
pass
170+
171+
@pytest.mark.skip(reason="DataPath does not support joins")
172+
def test_private_url_attr_in_sync(self):
173+
pass
174+
175+
@pytest.mark.skip(reason="DataPath does not support joins")
176+
def test_fsspec_compat(self):
177+
pass
178+
179+
def test_rmdir_not_empty(self):
180+
with pytest.raises(NotADirectoryError):
181+
self.path.rmdir()

upath/tests/test_registry.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
"abfss",
1212
"adl",
1313
"az",
14+
"data",
1415
"file",
1516
"gcs",
1617
"gs",

0 commit comments

Comments
 (0)