From fffd81d8bc0b5d580b0cb57a171dd8f55ebd900b Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 19:42:20 +0200 Subject: [PATCH 1/7] tests: add test for relative_to --- upath/tests/cases.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/upath/tests/cases.py b/upath/tests/cases.py index 56bb0141..82bd136d 100644 --- a/upath/tests/cases.py +++ b/upath/tests/cases.py @@ -655,3 +655,9 @@ def test_move_into_memory(self, clear_fsspec_memory_cache): assert target.exists() assert target.read_text() == content assert not source.exists() + + def test_relative_to(self): + base = self.path + child = self.path / "folder1" / "file1.txt" + relative = child.relative_to(base) + assert str(relative) == "folder1/file1.txt" From 8256ea28aa0708be204eae51a3815aee2bd4cfee Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 19:46:25 +0200 Subject: [PATCH 2/7] upath.core: fix relative_to --- upath/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upath/core.py b/upath/core.py index 3aaa93af..7f38b098 100644 --- a/upath/core.py +++ b/upath/core.py @@ -1371,7 +1371,7 @@ def relative_to( # type: ignore[override] raise ValueError(f"{self!s} is not in the subpath of {other!s}") else: rel = copy(self) - rel._relative_base = str(other) + rel._relative_base = other.path return rel def is_relative_to(self, other, /, *_deprecated) -> bool: # type: ignore[override] From a438e2dc8005736a7158715af1df66d00d503b2c Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 20:33:09 +0200 Subject: [PATCH 3/7] tests: skip data test case --- upath/tests/implementations/test_data.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/upath/tests/implementations/test_data.py b/upath/tests/implementations/test_data.py index 5b1fe92e..db59cb10 100644 --- a/upath/tests/implementations/test_data.py +++ b/upath/tests/implementations/test_data.py @@ -283,3 +283,7 @@ def test_move_memory(self, clear_fsspec_memory_cache): @pytest.mark.skip(reason="DataPath does not support unlink") def test_move_into_memory(self, clear_fsspec_memory_cache): pass + + @pytest.mark.skip(reason="DataPath does not support relative_to") + def test_relative_to(self): + pass From d3022718ca8d3fa0d597c9ce2de2cc028db161a0 Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 20:33:34 +0200 Subject: [PATCH 4/7] upath.implementations.sftp: fix trailing slashes --- upath/implementations/sftp.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/upath/implementations/sftp.py b/upath/implementations/sftp.py index 018b8e8b..ff924a52 100644 --- a/upath/implementations/sftp.py +++ b/upath/implementations/sftp.py @@ -36,6 +36,19 @@ def __init__( **storage_options: Unpack[SFTPStorageOptions], ) -> None: ... + @property + def path(self) -> str: + path = super().path + if len(path) > 1: + return path.removesuffix("/") + return path + + def __str__(self) -> str: + path_str = super().__str__() + if path_str.startswith(("ssh:///", "sftp:///")): + return path_str.removesuffix("/") + return path_str + def iterdir(self) -> Iterator[Self]: if not self.is_dir(): raise NotADirectoryError(str(self)) From 3604d64505805d049588f38939d9c656ebaab7a9 Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 20:42:30 +0200 Subject: [PATCH 5/7] upath.implementations.smb: fix trailing slashes --- upath/implementations/smb.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/upath/implementations/smb.py b/upath/implementations/smb.py index 685f4539..7adaade1 100644 --- a/upath/implementations/smb.py +++ b/upath/implementations/smb.py @@ -40,6 +40,19 @@ def __init__( **storage_options: Unpack[SMBStorageOptions], ) -> None: ... + @property + def path(self) -> str: + path = super().path + if len(path) > 1: + return path.removesuffix("/") + return path + + def __str__(self) -> str: + path_str = super().__str__() + if path_str.startswith("smb:///"): + return path_str.removesuffix("/") + return path_str + def mkdir( self, mode: int = 0o777, From 455265fbc68bb110b880315b552123737f6dd09c Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 21:00:28 +0200 Subject: [PATCH 6/7] upath.extensions: fix relative_to for ProxyUPath --- upath/extensions.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/upath/extensions.py b/upath/extensions.py index fabc86a6..48966cbd 100644 --- a/upath/extensions.py +++ b/upath/extensions.py @@ -370,6 +370,8 @@ def relative_to( # type: ignore[override] *_deprecated, walk_up=False, ) -> Self: + if isinstance(other, ProxyUPath): + other = other.__wrapped__ return self._from_upath( self.__wrapped__.relative_to(other, *_deprecated, walk_up=walk_up) ) From 84979cdf951771c7f82983eb636610260ccaf9a0 Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 17 Oct 2025 21:01:08 +0200 Subject: [PATCH 7/7] upath.core: fix error message use protocol --- upath/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/upath/core.py b/upath/core.py index 7f38b098..0e7b48ff 100644 --- a/upath/core.py +++ b/upath/core.py @@ -1355,7 +1355,7 @@ def relative_to( # type: ignore[override] if self.__class__ is not other.__class__: raise ValueError( "incompatible protocols:" - f" {self._protocol!r} != {other._protocol!r}" + f" {self.protocol!r} != {other.protocol!r}" ) if self.storage_options != other.storage_options: raise ValueError(