Repository: fsspec/universal_pathlib
Pull request #131 (2023-08-07 20:42:46)

provide _kwargs and _url on local paths
Some downstream dependencies rely on accessing `._kwargs` and `._url` which is not available on the `PosixUPath` and `WindowsUPath` subclasses.

While the two are implementation details, we should provide a reasonable deprecation cycle until we offer a better (or better documented) public API (see #129)

So for now, let's ensure that `._kwargs` and `._url` are available on all UPath subclasses.


Patch:
diff --git a/upath/core.py b/upath/core.py
index c42a6c4..9e07e8d 100644
--- a/upath/core.py
+++ b/upath/core.py
@@ -34,7 +34,7 @@ class _FSSpecAccessor:
     __slots__ = ("_fs",)
 
     def __init__(self, parsed_url: SplitResult | None, **kwargs: Any) -> None:
-        if parsed_url:
+        if parsed_url and parsed_url.scheme:
             cls = get_filesystem_class(parsed_url.scheme)
             url_kwargs = cls._get_kwargs_from_urls(urlunsplit(parsed_url))
         else:
@@ -166,7 +166,7 @@ class UPath(Path):
             _kwargs = getattr(other, "_kwargs", {})
             _url = getattr(other, "_url", None)
             other_kwargs = _kwargs.copy()
-            if _url:
+            if _url and _url.scheme:
                 other_kwargs["url"] = _url
             new_kwargs = _kwargs.copy()
             new_kwargs.update(kwargs)
diff --git a/upath/implementations/local.py b/upath/implementations/local.py
index ec2f1fe..0af1ad7 100644
--- a/upath/implementations/local.py
+++ b/upath/implementations/local.py
@@ -6,6 +6,7 @@ from pathlib import PosixPath
 from pathlib import WindowsPath
 from typing import Any
 from typing import Iterable
+from urllib.parse import SplitResult
 
 from fsspec.implementations.local import LocalFileSystem
 
@@ -49,11 +50,7 @@ class PosixUPath(PosixPath, UPath):
 
     @property
     def fs(self):
-        try:
-            return self._cached_fs
-        except AttributeError:
-            self._cached_fs = fs = LocalFileSystem()
-            return fs
+        return LocalFileSystem()
 
     @property
     def path(self) -> str:
@@ -61,7 +58,10 @@ class PosixUPath(PosixPath, UPath):
 
     @classmethod
     def _from_parts(cls, args, *, url=None, **kw):
-        return super(UPath, cls)._from_parts(args)
+        obj = super(UPath, cls)._from_parts(args)
+        obj._kwargs = {}
+        obj._url = SplitResult("", "", str(obj), "", "")
+        return obj
 
 
 class WindowsUPath(WindowsPath, UPath):
@@ -77,11 +77,7 @@ class WindowsUPath(WindowsPath, UPath):
 
     @property
     def fs(self):
-        try:
-            return self._cached_fs
-        except AttributeError:
-            self._cached_fs = fs = LocalFileSystem()
-            return fs
+        return LocalFileSystem()
 
     @property
     def path(self) -> str:
@@ -89,4 +85,7 @@ class WindowsUPath(WindowsPath, UPath):
 
     @classmethod
     def _from_parts(cls, args, *, url=None, **kw):
-        return super(UPath, cls)._from_parts(args)
+        obj = super(UPath, cls)._from_parts(args)
+        obj._kwargs = {}
+        obj._url = SplitResult("", "", str(obj), "", "")
+        return obj


Test patch:
diff --git a/upath/tests/test_core.py b/upath/tests/test_core.py
index d90246f..25bd0b7 100644
--- a/upath/tests/test_core.py
+++ b/upath/tests/test_core.py
@@ -3,6 +3,7 @@ import pathlib
 import pickle
 import sys
 import warnings
+from urllib.parse import SplitResult
 
 import pytest
 
@@ -242,6 +243,24 @@ def test_copy_path_append():
     assert str(path / "folder2" / "folder3") == str(copy_path)
 
 
+@pytest.mark.parametrize(
+    "urlpath",
+    [
+        os.getcwd(),
+        pathlib.Path.cwd().as_uri(),
+        "mock:///abc",
+    ],
+)
+def test_access_to_private_kwargs_and_url(urlpath):
+    # fixme: this should be deprecated...
+    pth = UPath(urlpath)
+    assert isinstance(pth._kwargs, dict)
+    assert pth._kwargs == {}
+    assert isinstance(pth._url, SplitResult)
+    assert pth._url.scheme == "" or pth._url.scheme in pth.fs.protocol
+    assert pth._url.path == pth.path
+
+
 def test_copy_path_append_kwargs():
     path = UPath("gcs://bucket/folder", anon=True)
     copy_path = UPath(path, anon=False)