Skip to content

Commit 5a080b0

Browse files
authored
Fix trailing slash behavior (#488)
* upath: ensure stripped trailing slashes in cloudpaths * tests: data path doesn't do joins * tests: adjust test cases to match intent of having identical __init__, joinpath, truediv behavior * tests: adjust trailing slash behavior of gcs and azure path * tests: add multiple args in init to trailing slash conditions
1 parent 52433c7 commit 5a080b0

File tree

4 files changed

+47
-7
lines changed

4 files changed

+47
-7
lines changed

upath/core.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -573,12 +573,10 @@ def __init__(
573573
# FIXME: normalization needs to happen in unchain already...
574574
chain = Chain.from_list(Chain.from_list(segments).to_list())
575575
if len(args) > 1:
576-
chain = chain.replace(
577-
path=WrappedFileSystemFlavour.from_protocol(protocol).join(
578-
chain.active_path,
579-
*args[1:],
580-
)
581-
)
576+
flavour = WrappedFileSystemFlavour.from_protocol(protocol)
577+
joined = flavour.join(chain.active_path, *args[1:])
578+
stripped = flavour.strip_protocol(joined)
579+
chain = chain.replace(path=stripped)
582580
self._chain = chain
583581
self._chain_parser = chain_parser
584582
self._raw_urlpaths = args

upath/implementations/cloud.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def __str__(self) -> str:
7272

7373
@property
7474
def path(self) -> str:
75-
self_path = super().path
75+
self_path = super().path.rstrip(self.parser.sep)
7676
if (
7777
self._relative_base is None
7878
and self_path

upath/tests/cases.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -677,3 +677,37 @@ def test_relative_to(self):
677677
child = self.path / "folder1" / "file1.txt"
678678
relative = child.relative_to(base)
679679
assert str(relative) == "folder1/file1.txt"
680+
681+
def test_trailing_slash_joinpath_is_identical(self):
682+
# setup
683+
cls = type(self.path)
684+
protocol = self.path.protocol
685+
path = self.path.path
686+
sopts = self.path.storage_options
687+
if not path:
688+
path = "something"
689+
path_with_slash = "something/"
690+
elif path.endswith("/"):
691+
path_with_slash = path
692+
path = path.removeprefix("/")
693+
else:
694+
path_with_slash = path + "/"
695+
key = "key/"
696+
697+
# test
698+
a = cls(path_with_slash + key, protocol=protocol, **sopts)
699+
b = cls(path_with_slash, key, protocol=protocol, **sopts)
700+
c = cls(path_with_slash, protocol=protocol, **sopts).joinpath(key)
701+
d = cls(path_with_slash, protocol=protocol, **sopts) / key
702+
assert a.path == b.path == c.path == d.path
703+
704+
def test_trailing_slash_is_stripped(self):
705+
has_meaningful_trailing_slash = getattr(
706+
self.path.parser, "has_meaningful_trailing_slash", False
707+
)
708+
if has_meaningful_trailing_slash:
709+
assert not self.path.joinpath("key").path.endswith("/")
710+
assert self.path.joinpath("key/").path.endswith("/")
711+
else:
712+
assert not self.path.joinpath("key").path.endswith("/")
713+
assert not self.path.joinpath("key/").path.endswith("/")

upath/tests/implementations/test_data.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,11 @@ def test_move_into_memory(self, clear_fsspec_memory_cache):
287287
@pytest.mark.skip(reason="DataPath does not support relative_to")
288288
def test_relative_to(self):
289289
pass
290+
291+
@pytest.mark.skip(reason="DataPath does not support joins")
292+
def test_trailing_slash_joinpath_is_identical(self):
293+
pass
294+
295+
@pytest.mark.skip(reason="DataPath does not support joins")
296+
def test_trailing_slash_is_stripped(self):
297+
pass

0 commit comments

Comments
 (0)