From 7c99e29e196e7e32e2fd125f37ab34aeaf10c67c Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 20:54:23 +0200 Subject: [PATCH 1/6] tests: add test to reproduce chained path access --- upath/tests/test_chain.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/upath/tests/test_chain.py b/upath/tests/test_chain.py index dff872f8..a1ab8e55 100644 --- a/upath/tests/test_chain.py +++ b/upath/tests/test_chain.py @@ -18,6 +18,18 @@ def test_chaining_upath_protocol(urlpath, expected): assert pth.protocol == expected +@pytest.mark.parametrize( + "urlpath,expected", + [ + ("simplecache::file:///tmp", "/tmp"), + ("zip://file.txt::file:///tmp.zip", "file.txt"), + ], +) +def test_chaining_upath_path(urlpath, expected): + pth = UPath(urlpath) + assert pth.path == expected + + @pytest.mark.parametrize( "urlpath,expected", [ From 3d6881433123c3ad88e09d0e54204e7fc56ab5df Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 20:56:30 +0200 Subject: [PATCH 2/6] upath: return correct .path for chained paths --- upath/core.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/upath/core.py b/upath/core.py index 71a07743..691c6675 100644 --- a/upath/core.py +++ b/upath/core.py @@ -234,9 +234,8 @@ def path(self) -> str: path = str(current_dir) else: path = current_dir.parser.join(str(self), self_path) - else: - path = str(self) - return self.parser.strip_protocol(path) + return self.parser.strip_protocol(path) + return self._chain.active_path def joinuri(self, uri: JoinablePathLike) -> UPath: """Join with urljoin behavior for UPath instances""" From 14bba42cb9cbe4d7433b3e3fd3544309becfcc4d Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 22:10:12 +0200 Subject: [PATCH 3/6] upath.implementations: correct .path normalization for cloud and http paths --- upath/implementations/cloud.py | 7 +++++++ upath/implementations/http.py | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/upath/implementations/cloud.py b/upath/implementations/cloud.py index 27bef5de..5c39108d 100644 --- a/upath/implementations/cloud.py +++ b/upath/implementations/cloud.py @@ -58,6 +58,13 @@ def __vfspath__(self) -> str: return f"{path}{self.root}" return path + @property + def path(self) -> str: + self_path = super().path + if self._relative_base is None and self.parser.sep not in self_path: + return self_path + self.root + return self_path + def mkdir( self, mode: int = 0o777, parents: bool = False, exist_ok: bool = False ) -> None: diff --git a/upath/implementations/http.py b/upath/implementations/http.py index 4e4f4dbd..b2c2ee58 100644 --- a/upath/implementations/http.py +++ b/upath/implementations/http.py @@ -41,6 +41,11 @@ def __str__(self) -> str: sr = urlsplit(super().__str__()) return sr._replace(path=sr.path or "/").geturl() + @property + def path(self) -> str: + sr = urlsplit(super().path) + return sr._replace(path=sr.path or "/").geturl() + def is_file(self) -> bool: try: next(super().iterdir()) From d4cd3ca0b04d8e2decbf76fb43ca19d2da8aebb0 Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 22:22:59 +0200 Subject: [PATCH 4/6] tests: add more chained tests --- upath/tests/test_chain.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/upath/tests/test_chain.py b/upath/tests/test_chain.py index a1ab8e55..d09209d3 100644 --- a/upath/tests/test_chain.py +++ b/upath/tests/test_chain.py @@ -1,9 +1,12 @@ +import os from pathlib import Path import pytest from fsspec.implementations.memory import MemoryFileSystem from upath import UPath +from upath.tests.utils import only_on_windows +from upath.tests.utils import skip_on_windows @pytest.mark.parametrize( @@ -18,11 +21,21 @@ def test_chaining_upath_protocol(urlpath, expected): assert pth.protocol == expected +CURRENT_DRIVE = os.path.splitdrive(Path.cwd().as_posix())[0] + + @pytest.mark.parametrize( "urlpath,expected", [ ("simplecache::file:///tmp", "/tmp"), - ("zip://file.txt::file:///tmp.zip", "file.txt"), + pytest.param( + "zip://file.txt::file:///tmp.zip", "file.txt", marks=skip_on_windows(None) + ), + pytest.param( + "zip://file.txt::file:///tmp.zip", + f"{CURRENT_DRIVE}/file.txt", + marks=only_on_windows(None), + ), ], ) def test_chaining_upath_path(urlpath, expected): From 0d464b426529e9d49e7b8bc1aa448fd0e2fbf43c Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 22:24:53 +0200 Subject: [PATCH 5/6] tests: add more chained tests --- upath/tests/test_chain.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/upath/tests/test_chain.py b/upath/tests/test_chain.py index d09209d3..eb8075fd 100644 --- a/upath/tests/test_chain.py +++ b/upath/tests/test_chain.py @@ -29,13 +29,16 @@ def test_chaining_upath_protocol(urlpath, expected): [ ("simplecache::file:///tmp", "/tmp"), pytest.param( - "zip://file.txt::file:///tmp.zip", "file.txt", marks=skip_on_windows(None) + "zip://file.txt::file:///tmp.zip", + "file.txt", + marks=skip_on_windows(None), ), pytest.param( "zip://file.txt::file:///tmp.zip", f"{CURRENT_DRIVE}/file.txt", marks=only_on_windows(None), ), + ("zip://a/b/c.txt::simplecache::memory://zipfile.zip", "a/b/c.txt"), ], ) def test_chaining_upath_path(urlpath, expected): From 3082838e1318a120e580ae2816d8eeaeb48531f2 Mon Sep 17 00:00:00 2001 From: Andreas Poehlmann Date: Fri, 3 Oct 2025 22:34:30 +0200 Subject: [PATCH 6/6] tests: fix test parameters for unix and win --- upath/tests/test_chain.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/upath/tests/test_chain.py b/upath/tests/test_chain.py index eb8075fd..8455dbcd 100644 --- a/upath/tests/test_chain.py +++ b/upath/tests/test_chain.py @@ -5,8 +5,6 @@ from fsspec.implementations.memory import MemoryFileSystem from upath import UPath -from upath.tests.utils import only_on_windows -from upath.tests.utils import skip_on_windows @pytest.mark.parametrize( @@ -21,24 +19,26 @@ def test_chaining_upath_protocol(urlpath, expected): assert pth.protocol == expected -CURRENT_DRIVE = os.path.splitdrive(Path.cwd().as_posix())[0] +def add_current_drive_on_windows(pth: str) -> str: + drive = os.path.splitdrive(Path.cwd().as_posix())[0] + return f"{drive}{pth}" @pytest.mark.parametrize( "urlpath,expected", [ - ("simplecache::file:///tmp", "/tmp"), + pytest.param( + "simplecache::file:///tmp", + add_current_drive_on_windows("/tmp"), + ), pytest.param( "zip://file.txt::file:///tmp.zip", "file.txt", - marks=skip_on_windows(None), ), pytest.param( - "zip://file.txt::file:///tmp.zip", - f"{CURRENT_DRIVE}/file.txt", - marks=only_on_windows(None), + "zip://a/b/c.txt::simplecache::memory://zipfile.zip", + "a/b/c.txt", ), - ("zip://a/b/c.txt::simplecache::memory://zipfile.zip", "a/b/c.txt"), ], ) def test_chaining_upath_path(urlpath, expected):