Skip to content

Commit 4e79cb1

Browse files
Merge pull request #56 from scalableminds/subclassing
Change subclassing behavior to Python standard
2 parents bd80e00 + 744a8c2 commit 4e79cb1

File tree

2 files changed

+53
-68
lines changed

2 files changed

+53
-68
lines changed

upath/core.py

Lines changed: 39 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import os
21
import pathlib
32
import re
3+
from typing import Union
44
import urllib
5-
from abc import ABCMeta
65

76
from fsspec.registry import (
87
get_filesystem_class,
@@ -87,22 +86,11 @@ def __getattribute__(self, item):
8786
)
8887

8988

90-
class PureUPath(pathlib.PurePath):
91-
_flavour = pathlib._posix_flavour
92-
__slots__ = ()
93-
94-
95-
class UPathMeta(ABCMeta):
96-
def __instancecheck__(cls, instance):
97-
return isinstance(instance, pathlib.Path)
98-
99-
def __subclasscheck__(cls, subclass):
100-
return issubclass(subclass, pathlib.Path)
101-
102-
103-
class UPath(pathlib.Path, PureUPath, metaclass=UPathMeta):
89+
class UPath(pathlib.Path):
10490

10591
__slots__ = ("_url", "_kwargs", "_closed", "_accessor")
92+
_flavour = pathlib._posix_flavour
93+
_default_accessor = _FSSpecAccessor
10694

10795
not_implemented = [
10896
"cwd",
@@ -114,60 +102,46 @@ class UPath(pathlib.Path, PureUPath, metaclass=UPathMeta):
114102
"owner",
115103
"readlink",
116104
]
117-
_default_accessor = _FSSpecAccessor
118105

119-
def __new__(cls, *args, **kwargs):
120-
if issubclass(cls, UPath):
121-
args_list = list(args)
122-
first = args_list.pop(0)
123-
if isinstance(first, pathlib.PurePath):
124-
# Create a (modified) copy, if first arg is a Path object
125-
other = first
126-
parts = args_list
127-
drv, root, parts = other._parse_args(parts)
128-
drv, root, parts = other._flavour.join_parsed_parts(
129-
other._drv, other._root, other._parts, drv, root, parts
130-
)
106+
def __new__(cls, *args, **kwargs) -> Union["UPath", pathlib.Path]:
107+
args_list = list(args)
108+
first = args_list.pop(0)
109+
if isinstance(first, pathlib.PurePath):
110+
# Create a (modified) copy, if first arg is a Path object
111+
other = first
112+
parts = args_list
113+
drv, root, parts = other._parse_args(parts)
114+
drv, root, parts = other._flavour.join_parsed_parts(
115+
other._drv, other._root, other._parts, drv, root, parts
116+
)
131117

132-
new_kwargs = getattr(other, "_kwargs", {}).copy()
133-
new_kwargs.pop("_url", None)
134-
new_kwargs.update(kwargs)
118+
new_kwargs = getattr(other, "_kwargs", {}).copy()
119+
new_kwargs.pop("_url", None)
120+
new_kwargs.update(kwargs)
135121

136-
return other.__class__(
137-
other._format_parsed_parts(drv, root, parts),
138-
**new_kwargs,
139-
)
122+
return other.__class__(
123+
other._format_parsed_parts(drv, root, parts),
124+
**new_kwargs,
125+
)
140126

141-
url = stringify_path(first)
142-
parsed_url = urllib.parse.urlparse(url)
143-
for key in ["scheme", "netloc"]:
144-
val = kwargs.get(key)
145-
if val:
146-
parsed_url = parsed_url._replace(**{key: val})
147-
# treat as local filesystem, return PosixPath or WindowsPath
148-
impls = list(registry) + list(known_implementations.keys())
149-
if not parsed_url.scheme or parsed_url.scheme not in impls:
150-
cls = (
151-
pathlib.WindowsPath
152-
if os.name == "nt"
153-
else pathlib.PosixPath
154-
)
155-
self = cls._from_parts(args)
156-
if not self._flavour.is_supported:
157-
raise NotImplementedError(
158-
"cannot instantiate %r on your system" % (cls.__name__,)
159-
)
160-
else:
161-
import upath.registry
127+
url = stringify_path(first)
128+
parsed_url = urllib.parse.urlparse(url)
129+
for key in ["scheme", "netloc"]:
130+
val = kwargs.get(key)
131+
if val:
132+
parsed_url = parsed_url._replace(**{key: val})
162133

163-
cls = upath.registry._registry[parsed_url.scheme]
164-
kwargs["_url"] = parsed_url
165-
args_list.insert(0, parsed_url.path)
166-
args = tuple(args_list)
167-
self = cls._from_parts(args, **kwargs)
168-
else:
169-
self = super().__new__(*args, **kwargs)
170-
return self
134+
fsspec_impls = list(registry) + list(known_implementations.keys())
135+
if parsed_url.scheme and parsed_url.scheme in fsspec_impls:
136+
import upath.registry
137+
138+
cls = upath.registry._registry[parsed_url.scheme]
139+
kwargs["_url"] = parsed_url
140+
args_list.insert(0, parsed_url.path)
141+
return cls._from_parts(tuple(args_list), **kwargs)
142+
143+
# treat as local filesystem, return PosixPath or WindowsPath
144+
return pathlib.Path(*args, **kwargs)
171145

172146
def __getattr__(self, item):
173147
if item == "_accessor":

upath/tests/test_core.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,18 @@ class MyPath(UPath):
8181
assert isinstance(path, pathlib.Path)
8282

8383

84+
def test_subclass_with_gcs():
85+
path = UPath("gcs://bucket", anon=True)
86+
assert isinstance(path, UPath)
87+
assert isinstance(path, pathlib.Path)
88+
89+
8490
def test_instance_check(local_testdir):
8591
path = pathlib.Path(local_testdir)
8692
upath = UPath(local_testdir)
8793
# test instance check passes
88-
assert isinstance(upath, UPath)
94+
assert isinstance(upath, pathlib.Path)
95+
assert not isinstance(upath, UPath)
8996
# test type is same as pathlib
9097
assert type(upath) is type(path)
9198
upath = UPath(f"file://{local_testdir}")
@@ -97,6 +104,7 @@ def test_new_method(local_testdir):
97104
path = UPath.__new__(pathlib.Path, local_testdir)
98105
assert str(path) == str(pathlib.Path(local_testdir))
99106
assert isinstance(path, pathlib.Path)
107+
assert not isinstance(path, UPath)
100108

101109

102110
@pytest.mark.skipif(
@@ -182,8 +190,6 @@ def test_copy_path():
182190
path = UPath("gcs://bucket/folder", anon=True)
183191
copy_path = UPath(path)
184192

185-
print(type(path), type(copy_path))
186-
187193
assert type(path) == type(copy_path)
188194
assert str(path) == str(copy_path)
189195
assert path._drv == copy_path._drv
@@ -215,6 +221,11 @@ def test_copy_path_append():
215221

216222
assert str(path / "folder2" / "folder3") == str(copy_path)
217223

224+
path = UPath("/tmp/folder")
225+
copy_path = UPath(path, "folder2", "folder3")
226+
227+
assert str(path / "folder2" / "folder3") == str(copy_path)
228+
218229

219230
def test_copy_path_append_kwargs():
220231
path = UPath("gcs://bucket/folder", anon=True)

0 commit comments

Comments
 (0)