megfile 4.2.5__py3-none-any.whl → 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- megfile/__init__.py +13 -293
- megfile/cli.py +37 -20
- megfile/config.py +10 -1
- megfile/errors.py +2 -2
- megfile/fs_path.py +32 -3
- megfile/interfaces.py +21 -10
- megfile/lib/base_memory_handler.py +92 -0
- megfile/lib/glob.py +3 -3
- megfile/lib/http_prefetch_reader.py +22 -22
- megfile/lib/s3_memory_handler.py +14 -81
- megfile/lib/webdav_memory_handler.py +83 -0
- megfile/lib/webdav_prefetch_reader.py +115 -0
- megfile/pathlike.py +3 -4
- megfile/s3_path.py +40 -32
- megfile/sftp2_path.py +38 -62
- megfile/sftp_path.py +238 -1
- megfile/smart.py +70 -29
- megfile/smart_path.py +181 -85
- megfile/version.py +1 -1
- megfile/webdav_path.py +159 -165
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/METADATA +27 -39
- megfile-5.0.0.dist-info/RECORD +51 -0
- megfile/fs.py +0 -627
- megfile/hdfs.py +0 -408
- megfile/http.py +0 -114
- megfile/s3.py +0 -540
- megfile/sftp.py +0 -821
- megfile/sftp2.py +0 -827
- megfile/stdio.py +0 -30
- megfile/webdav.py +0 -552
- megfile-4.2.5.dist-info/RECORD +0 -56
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/WHEEL +0 -0
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/entry_points.txt +0 -0
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/licenses/LICENSE.pyre +0 -0
- {megfile-4.2.5.dist-info → megfile-5.0.0.dist-info}/top_level.txt +0 -0
megfile/smart_path.py
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from configparser import ConfigParser
|
|
3
|
+
from functools import cached_property
|
|
3
4
|
from pathlib import PurePath
|
|
4
5
|
from typing import Dict, Optional, Tuple, Union
|
|
5
6
|
|
|
6
7
|
from megfile.lib.compat import fspath
|
|
7
8
|
from megfile.lib.url import get_url_scheme
|
|
9
|
+
from megfile.pathlike import URIPathParents
|
|
8
10
|
from megfile.utils import cached_classproperty
|
|
9
11
|
|
|
10
12
|
from .errors import ProtocolExistsError, ProtocolNotFoundError
|
|
@@ -13,19 +15,41 @@ from .interfaces import BasePath, PathLike
|
|
|
13
15
|
aliases_config = "~/.config/megfile/aliases.conf"
|
|
14
16
|
|
|
15
17
|
|
|
16
|
-
def _bind_function(name):
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
def _bind_function(name, after_callback=None, before_callback=None):
|
|
19
|
+
if before_callback is None and after_callback is None:
|
|
20
|
+
|
|
21
|
+
def smart_method(self, *args, **kwargs):
|
|
22
|
+
return getattr(self.pathlike, name)(*args, **kwargs)
|
|
23
|
+
|
|
24
|
+
else:
|
|
25
|
+
|
|
26
|
+
def smart_method(self, *args, **kwargs):
|
|
27
|
+
if before_callback is not None and len(args) > 0:
|
|
28
|
+
first_arg = before_callback(self, args[0])
|
|
29
|
+
args = (first_arg, *args[1:])
|
|
30
|
+
result = getattr(self.pathlike, name)(*args, **kwargs)
|
|
31
|
+
if after_callback is not None:
|
|
32
|
+
return after_callback(self, result)
|
|
33
|
+
return result
|
|
19
34
|
|
|
20
35
|
smart_method.__name__ = name
|
|
36
|
+
smart_method.__doc__ = f"Dynamically bound method for {name}"
|
|
21
37
|
|
|
22
38
|
return smart_method
|
|
23
39
|
|
|
24
40
|
|
|
25
|
-
def _bind_property(name):
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
41
|
+
def _bind_property(name, callback=None):
|
|
42
|
+
if callback is None:
|
|
43
|
+
|
|
44
|
+
@property
|
|
45
|
+
def smart_property(self):
|
|
46
|
+
return getattr(self.pathlike, name)
|
|
47
|
+
|
|
48
|
+
else:
|
|
49
|
+
|
|
50
|
+
@property
|
|
51
|
+
def smart_property(self):
|
|
52
|
+
return callback(self, getattr(self.pathlike, name))
|
|
29
53
|
|
|
30
54
|
return smart_property
|
|
31
55
|
|
|
@@ -41,14 +65,85 @@ def _load_aliases_config(config_path) -> Dict[str, Dict[str, str]]:
|
|
|
41
65
|
return configs
|
|
42
66
|
|
|
43
67
|
|
|
68
|
+
def _to_aliased_path(pathlike, other_path: str) -> str:
|
|
69
|
+
"""Convert path string to aliased path string"""
|
|
70
|
+
if pathlike.protocol == pathlike._unaliased_protocol:
|
|
71
|
+
return other_path
|
|
72
|
+
aliases: Dict[str, Dict[str, str]] = pathlike._aliases
|
|
73
|
+
unaliased_prefix = aliases[pathlike.protocol].get("prefix", "")
|
|
74
|
+
unaliased_prefix = "%s://%s" % (pathlike._unaliased_protocol, unaliased_prefix)
|
|
75
|
+
if not other_path.startswith(unaliased_prefix):
|
|
76
|
+
return other_path
|
|
77
|
+
path_without_protocol = other_path[len(unaliased_prefix) :]
|
|
78
|
+
return f"{pathlike.protocol}://{path_without_protocol}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _to_aliased_pathlike(pathlike, other_pathlike) -> BasePath:
|
|
82
|
+
"""Convert pathlike object to aliased SmartPath object"""
|
|
83
|
+
other_path = str(other_pathlike)
|
|
84
|
+
if pathlike.protocol != pathlike._unaliased_protocol:
|
|
85
|
+
other_path = _to_aliased_path(pathlike, other_path)
|
|
86
|
+
return SmartPath(other_path)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _to_aliased_path_list(pathlike, other_paths):
|
|
90
|
+
"""Convert list of path strings to aliased path strings"""
|
|
91
|
+
return [_to_aliased_path(pathlike, s) for s in other_paths]
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _to_aliased_pathlike_list(pathlike, other_pathlikes):
|
|
95
|
+
"""Convert list of pathlike objects to aliased SmartPath objects"""
|
|
96
|
+
return [_to_aliased_pathlike(pathlike, p) for p in other_pathlikes]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _to_aliased_path_iterator(pathlike, other_paths):
|
|
100
|
+
"""Convert iterator of path strings to aliased path strings"""
|
|
101
|
+
for s in other_paths:
|
|
102
|
+
yield _to_aliased_path(pathlike, s)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _to_aliased_pathlike_iterator(pathlike, other_pathlikes):
|
|
106
|
+
"""Convert iterator of pathlike objects to aliased SmartPath objects"""
|
|
107
|
+
for p in other_pathlikes:
|
|
108
|
+
yield _to_aliased_pathlike(pathlike, p)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _to_aliased_file_entry_iterator(pathlike, file_entries):
|
|
112
|
+
"""Convert iterator of FileEntry objects with aliased paths"""
|
|
113
|
+
for entry in file_entries:
|
|
114
|
+
yield entry._replace(path=_to_aliased_path(pathlike, entry.path))
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _to_aliased_walk_iterator(pathlike, walk_iterator):
|
|
118
|
+
"""Convert walk iterator with aliased paths"""
|
|
119
|
+
for dirpath, dirnames, filenames in walk_iterator:
|
|
120
|
+
aliased_dirpath = _to_aliased_path(pathlike, dirpath)
|
|
121
|
+
yield (aliased_dirpath, dirnames, filenames)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def _to_unaliased_path(pathlike, path):
|
|
125
|
+
"""Convert path string to unaliased path string"""
|
|
126
|
+
aliases: Dict[str, Dict[str, str]] = pathlike._aliases
|
|
127
|
+
protocol, path_without_protocol = pathlike._split_protocol(path)
|
|
128
|
+
if protocol in aliases:
|
|
129
|
+
prefix = aliases[protocol].get("prefix", "")
|
|
130
|
+
protocol = aliases[protocol]["protocol"]
|
|
131
|
+
return "%s://%s%s" % (protocol, prefix, path_without_protocol)
|
|
132
|
+
return path
|
|
133
|
+
|
|
134
|
+
|
|
44
135
|
class SmartPath(BasePath):
|
|
45
136
|
_registered_protocols = dict()
|
|
46
137
|
|
|
47
138
|
def __init__(self, path: Union[PathLike, int], *other_paths: PathLike):
|
|
48
139
|
self.path = str(path) if not isinstance(path, int) else path
|
|
140
|
+
self.protocol = self._extract_protocol(path)
|
|
141
|
+
self._unaliased_path = _to_unaliased_path(self, path)
|
|
142
|
+
self._unaliased_protocol = self._extract_protocol(self._unaliased_path)
|
|
143
|
+
|
|
49
144
|
pathlike = path
|
|
50
145
|
if not isinstance(pathlike, BasePath):
|
|
51
|
-
pathlike = self._create_pathlike(
|
|
146
|
+
pathlike = self._create_pathlike(self._unaliased_path)
|
|
52
147
|
if len(other_paths) > 0:
|
|
53
148
|
pathlike = pathlike.joinpath(*other_paths)
|
|
54
149
|
self.path = str(pathlike)
|
|
@@ -60,41 +155,36 @@ class SmartPath(BasePath):
|
|
|
60
155
|
return _load_aliases_config(config_path)
|
|
61
156
|
|
|
62
157
|
@classmethod
|
|
63
|
-
def
|
|
64
|
-
cls, path: Union[PathLike, int]
|
|
65
|
-
) -> Tuple[str, Union[str, int]]:
|
|
158
|
+
def _split_protocol(cls, path: Union[PathLike, int]) -> Tuple[str, Union[str, int]]:
|
|
66
159
|
if isinstance(path, int):
|
|
67
|
-
|
|
68
|
-
path_without_protocol = path
|
|
160
|
+
return "file", path
|
|
69
161
|
elif isinstance(path, str):
|
|
70
162
|
protocol = get_url_scheme(path)
|
|
71
|
-
if protocol
|
|
163
|
+
if not protocol:
|
|
72
164
|
protocol = "file"
|
|
73
165
|
path_without_protocol = path
|
|
74
166
|
else:
|
|
75
167
|
path_without_protocol = path[len(protocol) + 3 :]
|
|
168
|
+
return protocol, path_without_protocol
|
|
76
169
|
elif isinstance(path, (BasePath, SmartPath)):
|
|
77
|
-
return str(path.protocol),
|
|
170
|
+
return str(path.protocol), path.path_without_protocol
|
|
78
171
|
elif isinstance(path, (PurePath, BasePath)):
|
|
79
|
-
return SmartPath.
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
protocol = aliases[protocol]["protocol"]
|
|
86
|
-
path = "%s://%s%s" % (protocol, prefix, path_without_protocol)
|
|
87
|
-
return protocol, path
|
|
172
|
+
return SmartPath._split_protocol(fspath(path))
|
|
173
|
+
raise ProtocolNotFoundError("protocol not found: %r" % path)
|
|
174
|
+
|
|
175
|
+
@classmethod
|
|
176
|
+
def _extract_protocol(cls, path: Union[PathLike, int]) -> str:
|
|
177
|
+
return cls._split_protocol(path)[0]
|
|
88
178
|
|
|
89
179
|
@classmethod
|
|
90
180
|
def _create_pathlike(cls, path: Union[PathLike, int]) -> BasePath:
|
|
91
|
-
protocol
|
|
181
|
+
protocol = cls._extract_protocol(path)
|
|
92
182
|
if protocol.startswith("s3+"):
|
|
93
183
|
protocol = "s3"
|
|
94
184
|
if protocol not in cls._registered_protocols:
|
|
95
185
|
raise ProtocolNotFoundError("protocol %r not found: %r" % (protocol, path))
|
|
96
186
|
path_class = cls._registered_protocols[protocol]
|
|
97
|
-
return path_class(
|
|
187
|
+
return path_class(path)
|
|
98
188
|
|
|
99
189
|
@classmethod
|
|
100
190
|
def register(cls, path_class, override_ok: bool = False):
|
|
@@ -104,81 +194,87 @@ class SmartPath(BasePath):
|
|
|
104
194
|
cls._registered_protocols[protocol] = path_class
|
|
105
195
|
return path_class
|
|
106
196
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
197
|
+
@classmethod
|
|
198
|
+
def from_uri(cls, path: PathLike):
|
|
199
|
+
return cls(path)
|
|
200
|
+
|
|
201
|
+
def relpath(self, start: Optional[str] = None) -> str:
|
|
202
|
+
"""Return the relative path of given path
|
|
203
|
+
|
|
204
|
+
:param start: Given start directory
|
|
205
|
+
:returns: Relative path from start
|
|
206
|
+
"""
|
|
207
|
+
if start is not None:
|
|
208
|
+
start = _to_unaliased_path(self, start)
|
|
209
|
+
return self.pathlike.relpath(start=start)
|
|
210
|
+
|
|
211
|
+
@cached_property
|
|
212
|
+
def parts(self) -> Tuple[str, ...]:
|
|
213
|
+
"""A tuple giving access to the path’s various components"""
|
|
214
|
+
parts = self.pathlike.parts
|
|
215
|
+
parts = (_to_aliased_path(self, parts[0]), *parts[1:])
|
|
216
|
+
return parts
|
|
217
|
+
|
|
218
|
+
@cached_property
|
|
219
|
+
def parents(self) -> "URIPathParents":
|
|
220
|
+
"""
|
|
221
|
+
An immutable sequence providing access to the logical ancestors of the path
|
|
222
|
+
"""
|
|
223
|
+
return URIPathParents(self)
|
|
224
|
+
|
|
225
|
+
symlink = _bind_function("symlink", before_callback=_to_unaliased_path)
|
|
226
|
+
symlink_to = _bind_function("symlink_to", before_callback=_to_unaliased_path)
|
|
227
|
+
hardlink_to = _bind_function("hardlink_to", before_callback=_to_unaliased_path)
|
|
228
|
+
readlink = _bind_function("readlink", _to_aliased_pathlike)
|
|
111
229
|
is_dir = _bind_function("is_dir")
|
|
112
230
|
is_file = _bind_function("is_file")
|
|
113
231
|
is_symlink = _bind_function("is_symlink")
|
|
114
232
|
access = _bind_function("access")
|
|
115
233
|
exists = _bind_function("exists")
|
|
116
|
-
listdir = _bind_function("listdir")
|
|
117
|
-
scandir = _bind_function("scandir")
|
|
234
|
+
listdir = _bind_function("listdir", _to_aliased_path_list)
|
|
235
|
+
scandir = _bind_function("scandir", _to_aliased_file_entry_iterator)
|
|
118
236
|
getsize = _bind_function("getsize")
|
|
119
237
|
getmtime = _bind_function("getmtime")
|
|
120
238
|
stat = _bind_function("stat")
|
|
121
239
|
lstat = _bind_function("lstat")
|
|
122
240
|
remove = _bind_function("remove")
|
|
123
|
-
rename = _bind_function("rename")
|
|
124
|
-
replace = _bind_function("replace")
|
|
241
|
+
rename = _bind_function("rename", _to_aliased_pathlike, _to_unaliased_path)
|
|
242
|
+
replace = _bind_function("replace", _to_aliased_pathlike, _to_unaliased_path)
|
|
125
243
|
unlink = _bind_function("unlink")
|
|
126
244
|
mkdir = _bind_function("mkdir")
|
|
127
245
|
open = _bind_function("open")
|
|
128
246
|
touch = _bind_function("touch")
|
|
129
|
-
walk = _bind_function("walk")
|
|
130
|
-
scan = _bind_function("scan")
|
|
131
|
-
scan_stat = _bind_function("scan_stat")
|
|
132
|
-
glob = _bind_function("glob")
|
|
133
|
-
iglob = _bind_function("iglob")
|
|
134
|
-
glob_stat = _bind_function("glob_stat")
|
|
247
|
+
walk = _bind_function("walk", _to_aliased_walk_iterator)
|
|
248
|
+
scan = _bind_function("scan", _to_aliased_path_iterator)
|
|
249
|
+
scan_stat = _bind_function("scan_stat", _to_aliased_file_entry_iterator)
|
|
250
|
+
glob = _bind_function("glob", _to_aliased_pathlike_list)
|
|
251
|
+
iglob = _bind_function("iglob", _to_aliased_pathlike_iterator)
|
|
252
|
+
glob_stat = _bind_function("glob_stat", _to_aliased_file_entry_iterator)
|
|
135
253
|
load = _bind_function("load")
|
|
136
254
|
save = _bind_function("save")
|
|
137
|
-
joinpath = _bind_function("joinpath")
|
|
138
|
-
abspath = _bind_function("abspath")
|
|
139
|
-
realpath = _bind_function("realpath")
|
|
255
|
+
joinpath = _bind_function("joinpath", _to_aliased_pathlike)
|
|
256
|
+
abspath = _bind_function("abspath", _to_aliased_path)
|
|
257
|
+
realpath = _bind_function("realpath", _to_aliased_path)
|
|
140
258
|
is_absolute = _bind_function("is_absolute")
|
|
141
259
|
is_mount = _bind_function("is_mount")
|
|
142
260
|
md5 = _bind_function("md5")
|
|
143
261
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
@classmethod
|
|
149
|
-
def from_uri(cls, path: PathLike):
|
|
150
|
-
return cls(path)
|
|
151
|
-
|
|
152
|
-
def relpath(self, start: Optional[str] = None) -> str:
|
|
153
|
-
"""Return the relative path of given path
|
|
154
|
-
|
|
155
|
-
:param start: Given start directory
|
|
156
|
-
:returns: Relative path from start
|
|
157
|
-
"""
|
|
158
|
-
if start is not None:
|
|
159
|
-
_, start = SmartPath._extract_protocol(fspath(start)) # pyre-ignore[9]
|
|
160
|
-
return self.pathlike.relpath(start=start)
|
|
161
|
-
|
|
162
|
-
as_uri = _bind_function("as_uri")
|
|
163
|
-
as_posix = _bind_function("as_posix")
|
|
164
|
-
__lt__ = _bind_function("__lt__")
|
|
165
|
-
__le__ = _bind_function("__le__")
|
|
166
|
-
__gt__ = _bind_function("__gt__")
|
|
167
|
-
__ge__ = _bind_function("__ge__")
|
|
168
|
-
__fspath__ = _bind_function("__fspath__")
|
|
169
|
-
__truediv__ = _bind_function("__truediv__")
|
|
262
|
+
as_uri = _bind_function("as_uri", _to_aliased_path)
|
|
263
|
+
as_posix = _bind_function("as_posix", _to_aliased_path)
|
|
264
|
+
__fspath__ = _bind_function("__fspath__", _to_aliased_path)
|
|
265
|
+
__truediv__ = _bind_function("__truediv__", _to_aliased_pathlike)
|
|
170
266
|
|
|
171
267
|
is_reserved = _bind_function("is_reserved")
|
|
172
|
-
match = _bind_function("match")
|
|
173
|
-
relative_to = _bind_function("relative_to")
|
|
174
|
-
with_name = _bind_function("with_name")
|
|
175
|
-
with_suffix = _bind_function("with_suffix")
|
|
176
|
-
with_stem = _bind_function("with_stem")
|
|
177
|
-
iterdir = _bind_function("iterdir")
|
|
178
|
-
cwd = _bind_function("cwd")
|
|
268
|
+
match = _bind_function("match", before_callback=_to_unaliased_path)
|
|
269
|
+
relative_to = _bind_function("relative_to", before_callback=_to_unaliased_path)
|
|
270
|
+
with_name = _bind_function("with_name", _to_aliased_pathlike)
|
|
271
|
+
with_suffix = _bind_function("with_suffix", _to_aliased_pathlike)
|
|
272
|
+
with_stem = _bind_function("with_stem", _to_aliased_pathlike)
|
|
273
|
+
iterdir = _bind_function("iterdir", _to_aliased_pathlike_iterator)
|
|
274
|
+
cwd = _bind_function("cwd", _to_aliased_pathlike)
|
|
179
275
|
home = _bind_function("home")
|
|
180
276
|
expanduser = _bind_function("expanduser")
|
|
181
|
-
resolve = _bind_function("resolve")
|
|
277
|
+
resolve = _bind_function("resolve", _to_aliased_pathlike)
|
|
182
278
|
chmod = _bind_function("chmod")
|
|
183
279
|
lchmod = _bind_function("lchmod")
|
|
184
280
|
group = _bind_function("group")
|
|
@@ -187,23 +283,23 @@ class SmartPath(BasePath):
|
|
|
187
283
|
is_block_device = _bind_function("is_block_device")
|
|
188
284
|
is_char_device = _bind_function("is_char_device")
|
|
189
285
|
owner = _bind_function("owner")
|
|
190
|
-
absolute = _bind_function("absolute")
|
|
286
|
+
absolute = _bind_function("absolute", _to_aliased_pathlike)
|
|
191
287
|
rmdir = _bind_function("rmdir")
|
|
192
|
-
is_relative_to = _bind_function(
|
|
288
|
+
is_relative_to = _bind_function(
|
|
289
|
+
"is_relative_to", before_callback=_to_unaliased_path
|
|
290
|
+
)
|
|
193
291
|
read_bytes = _bind_function("read_bytes")
|
|
194
292
|
read_text = _bind_function("read_text")
|
|
195
|
-
rglob = _bind_function("rglob")
|
|
293
|
+
rglob = _bind_function("rglob", _to_aliased_pathlike_list)
|
|
196
294
|
samefile = _bind_function("samefile")
|
|
197
295
|
write_bytes = _bind_function("write_bytes")
|
|
198
296
|
write_text = _bind_function("write_text")
|
|
199
297
|
utime = _bind_function("utime")
|
|
200
298
|
|
|
201
299
|
drive = _bind_property("drive")
|
|
202
|
-
root = _bind_property("root")
|
|
203
|
-
anchor = _bind_property("anchor")
|
|
204
|
-
|
|
205
|
-
parents = _bind_property("parents")
|
|
206
|
-
parent = _bind_property("parent")
|
|
300
|
+
root = _bind_property("root", _to_aliased_path)
|
|
301
|
+
anchor = _bind_property("anchor", _to_aliased_path)
|
|
302
|
+
parent = _bind_property("parent", _to_aliased_pathlike)
|
|
207
303
|
name = _bind_property("name")
|
|
208
304
|
suffix = _bind_property("suffix")
|
|
209
305
|
suffixes = _bind_property("suffixes")
|
megfile/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
VERSION = "
|
|
1
|
+
VERSION = "5.0.0"
|