megfile 4.2.4__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/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
- def smart_method(self, *args, **kwargs):
18
- return getattr(self.pathlike, name)(*args, **kwargs)
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
- @property
27
- def smart_property(self):
28
- return getattr(self.pathlike, name)
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(path)
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 _extract_protocol(
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
- protocol = "file"
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), str(path)
170
+ return str(path.protocol), path.path_without_protocol
78
171
  elif isinstance(path, (PurePath, BasePath)):
79
- return SmartPath._extract_protocol(fspath(path))
80
- else:
81
- raise ProtocolNotFoundError("protocol not found: %r" % path)
82
- aliases: Dict[str, Dict[str, str]] = cls._aliases # pyre-ignore[9]
83
- if protocol in aliases:
84
- prefix = aliases[protocol].get("prefix", "")
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, un_aliased_path = cls._extract_protocol(path)
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(un_aliased_path)
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
- symlink = _bind_function("symlink")
108
- symlink_to = _bind_function("symlink_to")
109
- hardlink_to = _bind_function("hardlink_to")
110
- readlink = _bind_function("readlink")
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
- @property
145
- def protocol(self) -> str:
146
- return self.pathlike.protocol
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("is_relative_to")
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
- parts = _bind_property("parts")
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 = "4.2.4"
1
+ VERSION = "5.0.0"