megfile 3.0.6.post1__py3-none-any.whl → 3.1.0.post1__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.
- docs/conf.py +67 -0
- megfile/cli.py +16 -16
- megfile/config.py +37 -6
- megfile/errors.py +26 -20
- megfile/fs.py +13 -8
- megfile/fs_path.py +69 -49
- megfile/hdfs.py +13 -8
- megfile/hdfs_path.py +49 -41
- megfile/http.py +1 -1
- megfile/http_path.py +35 -28
- megfile/interfaces.py +119 -48
- megfile/lib/base_prefetch_reader.py +9 -8
- megfile/lib/combine_reader.py +7 -7
- megfile/lib/fnmatch.py +2 -2
- megfile/lib/glob.py +3 -3
- megfile/lib/hdfs_prefetch_reader.py +2 -1
- megfile/lib/http_prefetch_reader.py +3 -2
- megfile/lib/lazy_handler.py +6 -5
- megfile/lib/s3_buffered_writer.py +8 -7
- megfile/lib/s3_cached_handler.py +3 -4
- megfile/lib/s3_limited_seekable_writer.py +5 -3
- megfile/lib/s3_memory_handler.py +10 -6
- megfile/lib/s3_pipe_handler.py +1 -1
- megfile/lib/s3_prefetch_reader.py +7 -5
- megfile/lib/s3_share_cache_reader.py +2 -2
- megfile/lib/shadow_handler.py +5 -5
- megfile/lib/stdio_handler.py +3 -3
- megfile/pathlike.py +156 -170
- megfile/s3.py +19 -13
- megfile/s3_path.py +98 -83
- megfile/sftp.py +25 -16
- megfile/sftp_path.py +109 -94
- megfile/smart.py +38 -28
- megfile/smart_path.py +6 -6
- megfile/stdio.py +3 -3
- megfile/stdio_path.py +5 -5
- megfile/utils/__init__.py +8 -27
- megfile/version.py +1 -1
- {megfile-3.0.6.post1.dist-info → megfile-3.1.0.post1.dist-info}/METADATA +4 -5
- megfile-3.1.0.post1.dist-info/RECORD +55 -0
- {megfile-3.0.6.post1.dist-info → megfile-3.1.0.post1.dist-info}/WHEEL +1 -1
- megfile-3.1.0.post1.dist-info/top_level.txt +7 -0
- scripts/convert_results_to_sarif.py +124 -0
- scripts/generate_file.py +268 -0
- megfile-3.0.6.post1.dist-info/RECORD +0 -52
- megfile-3.0.6.post1.dist-info/top_level.txt +0 -1
- {megfile-3.0.6.post1.dist-info → megfile-3.1.0.post1.dist-info}/LICENSE +0 -0
- {megfile-3.0.6.post1.dist-info → megfile-3.1.0.post1.dist-info}/LICENSE.pyre +0 -0
- {megfile-3.0.6.post1.dist-info → megfile-3.1.0.post1.dist-info}/entry_points.txt +0 -0
megfile/pathlike.py
CHANGED
|
@@ -1,36 +1,18 @@
|
|
|
1
|
+
# pyre-ignore-all-errors[16]
|
|
1
2
|
import os
|
|
2
3
|
import stat
|
|
3
4
|
from collections.abc import Sequence
|
|
4
5
|
from enum import Enum
|
|
5
|
-
from functools import
|
|
6
|
-
from typing import IO, Any,
|
|
6
|
+
from functools import cached_property
|
|
7
|
+
from typing import IO, Any, BinaryIO, Iterator, List, NamedTuple, Optional, Tuple, Type, TypeVar, Union
|
|
7
8
|
|
|
8
9
|
from megfile.lib.compat import PathLike as _PathLike
|
|
9
10
|
from megfile.lib.compat import fspath
|
|
10
11
|
from megfile.lib.fnmatch import _compile_pattern
|
|
11
12
|
from megfile.lib.joinpath import uri_join
|
|
12
|
-
from megfile.utils import
|
|
13
|
+
from megfile.utils import classproperty
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
'''
|
|
16
|
-
class StatResult(NamedTuple):
|
|
17
|
-
|
|
18
|
-
size: int = 0
|
|
19
|
-
ctime: float = 0.0
|
|
20
|
-
mtime: float = 0.0
|
|
21
|
-
isdir: bool = False
|
|
22
|
-
islnk: bool = False
|
|
23
|
-
extra: Any = None # raw stat info
|
|
24
|
-
|
|
25
|
-
in Python 3.6+
|
|
26
|
-
'''
|
|
27
|
-
|
|
28
|
-
_StatResult = NamedTuple(
|
|
29
|
-
'StatResult', [
|
|
30
|
-
('size', int), ('ctime', float), ('mtime', float), ('isdir', bool),
|
|
31
|
-
('islnk', bool), ('extra', Any)
|
|
32
|
-
])
|
|
33
|
-
_StatResult.__new__.__defaults__ = (0, 0.0, 0.0, False, False, None)
|
|
15
|
+
Self = TypeVar('Self')
|
|
34
16
|
|
|
35
17
|
|
|
36
18
|
class Access(Enum):
|
|
@@ -38,7 +20,13 @@ class Access(Enum):
|
|
|
38
20
|
WRITE = 2
|
|
39
21
|
|
|
40
22
|
|
|
41
|
-
class StatResult(
|
|
23
|
+
class StatResult(NamedTuple):
|
|
24
|
+
size: int = 0
|
|
25
|
+
ctime: float = 0.0
|
|
26
|
+
mtime: float = 0.0
|
|
27
|
+
isdir: bool = False
|
|
28
|
+
islnk: bool = False
|
|
29
|
+
extra: Any = None
|
|
42
30
|
|
|
43
31
|
def is_file(self) -> bool:
|
|
44
32
|
return not self.isdir or self.islnk
|
|
@@ -195,21 +183,12 @@ class StatResult(_StatResult):
|
|
|
195
183
|
return 0
|
|
196
184
|
|
|
197
185
|
|
|
198
|
-
'''
|
|
199
186
|
class FileEntry(NamedTuple):
|
|
200
187
|
|
|
201
188
|
name: str
|
|
189
|
+
path: str
|
|
202
190
|
stat: StatResult
|
|
203
191
|
|
|
204
|
-
in Python 3.6+
|
|
205
|
-
'''
|
|
206
|
-
|
|
207
|
-
_FileEntry = NamedTuple(
|
|
208
|
-
'FileEntry', [('name', str), ('path', str), ('stat', StatResult)])
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
class FileEntry(_FileEntry):
|
|
212
|
-
|
|
213
192
|
def inode(self) -> Optional[Union[int, str]]:
|
|
214
193
|
return self.stat.st_ino
|
|
215
194
|
|
|
@@ -223,16 +202,6 @@ class FileEntry(_FileEntry):
|
|
|
223
202
|
return self.stat.is_symlink()
|
|
224
203
|
|
|
225
204
|
|
|
226
|
-
def method_not_implemented(func):
|
|
227
|
-
|
|
228
|
-
@wraps(func)
|
|
229
|
-
def wrapper(self, *args, **kwargs):
|
|
230
|
-
raise NotImplementedError(
|
|
231
|
-
'method %r not implemented: %r' % (func.__name__, self))
|
|
232
|
-
|
|
233
|
-
return wrapper
|
|
234
|
-
|
|
235
|
-
|
|
236
205
|
class BasePath:
|
|
237
206
|
|
|
238
207
|
def __init__(self, path: "PathLike"):
|
|
@@ -256,142 +225,160 @@ class BasePath:
|
|
|
256
225
|
def __eq__(self, other_path: "BasePath") -> bool:
|
|
257
226
|
return fspath(self) == fspath(other_path)
|
|
258
227
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
@method_not_implemented
|
|
262
|
-
def is_dir(self, followlinks: bool = False) -> bool: # type: ignore
|
|
228
|
+
def is_dir(self, followlinks: bool = False) -> bool:
|
|
263
229
|
"""Return True if the path points to a directory."""
|
|
230
|
+
raise NotImplementedError('method "is_dir" not implemented: %r' % self)
|
|
264
231
|
|
|
265
|
-
|
|
266
|
-
def is_file(self, followlinks: bool = False) -> bool: # type: ignore
|
|
232
|
+
def is_file(self, followlinks: bool = False) -> bool:
|
|
267
233
|
"""Return True if the path points to a regular file."""
|
|
234
|
+
raise NotImplementedError('method "is_file" not implemented: %r' % self)
|
|
268
235
|
|
|
269
236
|
def is_symlink(self) -> bool:
|
|
270
237
|
return False
|
|
271
238
|
|
|
272
|
-
|
|
273
|
-
def access(self, mode: Access) -> bool: # type: ignore
|
|
239
|
+
def access(self, mode: Access) -> bool:
|
|
274
240
|
"""Return True if the path has access permission described by mode."""
|
|
241
|
+
raise NotImplementedError('method "access" not implemented: %r' % self)
|
|
275
242
|
|
|
276
|
-
|
|
277
|
-
def exists(self, followlinks: bool = False) -> bool: # type: ignore
|
|
243
|
+
def exists(self, followlinks: bool = False) -> bool:
|
|
278
244
|
"""Whether the path points to an existing file or directory."""
|
|
245
|
+
raise NotImplementedError('method "exists" not implemented: %r' % self)
|
|
279
246
|
|
|
280
|
-
|
|
281
|
-
@method_not_implemented
|
|
282
|
-
def listdir(self) -> List[str]: # type: ignore
|
|
247
|
+
def listdir(self) -> List[str]:
|
|
283
248
|
"""Return the names of the entries in the directory the path points to."""
|
|
249
|
+
raise NotImplementedError('method "listdir" not implemented: %r' % self)
|
|
284
250
|
|
|
285
|
-
|
|
286
|
-
def scandir(self) -> Iterator[FileEntry]: # type: ignore
|
|
251
|
+
def scandir(self) -> Iterator[FileEntry]:
|
|
287
252
|
"""Return an iterator of FileEntry objects corresponding to the entries in the directory."""
|
|
253
|
+
raise NotImplementedError('method "scandir" not implemented: %r' % self)
|
|
288
254
|
|
|
289
|
-
|
|
290
|
-
def getsize(self, follow_symlinks: bool = True) -> int: # type: ignore
|
|
255
|
+
def getsize(self, follow_symlinks: bool = True) -> int:
|
|
291
256
|
"""Return the size, in bytes."""
|
|
257
|
+
raise NotImplementedError('method "getsize" not implemented: %r' % self)
|
|
292
258
|
|
|
293
|
-
|
|
294
|
-
def getmtime(self, follow_symlinks: bool = True) -> float: # type: ignore
|
|
259
|
+
def getmtime(self, follow_symlinks: bool = True) -> float:
|
|
295
260
|
"""Return the time of last modification."""
|
|
261
|
+
raise NotImplementedError(
|
|
262
|
+
'method "getmtime" not implemented: %r' % self)
|
|
296
263
|
|
|
297
|
-
|
|
298
|
-
def stat(self, follow_symlinks=True) -> StatResult: # type: ignore
|
|
264
|
+
def stat(self, follow_symlinks=True) -> StatResult:
|
|
299
265
|
"""Get the status of the path."""
|
|
266
|
+
raise NotImplementedError('method "stat" not implemented: %r' % self)
|
|
300
267
|
|
|
301
|
-
@method_not_implemented
|
|
302
268
|
def remove(self, missing_ok: bool = False) -> None:
|
|
303
269
|
"""Remove (delete) the file."""
|
|
270
|
+
raise NotImplementedError('method "remove" not implemented: %r' % self)
|
|
304
271
|
|
|
305
|
-
@method_not_implemented
|
|
306
272
|
def unlink(self, missing_ok: bool = False) -> None:
|
|
307
273
|
"""Remove (delete) the file."""
|
|
274
|
+
raise NotImplementedError('method "unlink" not implemented: %r' % self)
|
|
308
275
|
|
|
309
|
-
@method_not_implemented
|
|
310
276
|
def mkdir(
|
|
311
|
-
self,
|
|
277
|
+
self,
|
|
278
|
+
mode=0o777,
|
|
279
|
+
parents: bool = False,
|
|
312
280
|
exist_ok: bool = False) -> None:
|
|
313
281
|
"""Create a directory."""
|
|
282
|
+
raise NotImplementedError('method "mkdir" not implemented: %r' % self)
|
|
314
283
|
|
|
315
|
-
@method_not_implemented
|
|
316
284
|
def rmdir(self) -> None:
|
|
317
285
|
"""Remove (delete) the directory."""
|
|
286
|
+
raise NotImplementedError('method "rmdir" not implemented: %r' % self)
|
|
318
287
|
|
|
319
|
-
|
|
320
|
-
def open(self, mode: str = 'r', **kwargs) -> IO[AnyStr]: # type: ignore
|
|
288
|
+
def open(self, mode: str = 'r', **kwargs) -> IO:
|
|
321
289
|
"""Open the file with mode."""
|
|
290
|
+
raise NotImplementedError('method "open" not implemented: %r' % self)
|
|
322
291
|
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
292
|
+
def walk(
|
|
293
|
+
self,
|
|
294
|
+
followlinks: bool = False
|
|
295
|
+
) -> Iterator[Tuple[str, List[str], List[str]]]:
|
|
326
296
|
"""Generate the file names in a directory tree by walking the tree."""
|
|
297
|
+
raise NotImplementedError('method "walk" not implemented: %r' % self)
|
|
327
298
|
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
followlinks: bool = False) -> Iterator[str]:
|
|
299
|
+
def scan(self,
|
|
300
|
+
missing_ok: bool = True,
|
|
301
|
+
followlinks: bool = False) -> Iterator[str]:
|
|
331
302
|
"""Iterate through the files in the directory."""
|
|
303
|
+
raise NotImplementedError('method "scan" not implemented: %r' % self)
|
|
332
304
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
305
|
+
def scan_stat(self,
|
|
306
|
+
missing_ok: bool = True,
|
|
307
|
+
followlinks: bool = False) -> Iterator[FileEntry]:
|
|
336
308
|
"""Iterate through the files in the directory, with file stat."""
|
|
309
|
+
raise NotImplementedError(
|
|
310
|
+
'method "scan_stat" not implemented: %r' % self)
|
|
337
311
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
312
|
+
def glob(
|
|
313
|
+
self: Self,
|
|
314
|
+
pattern: str,
|
|
315
|
+
recursive: bool = True,
|
|
316
|
+
missing_ok: bool = True) -> List[Self]:
|
|
341
317
|
"""Return files whose paths match the glob pattern."""
|
|
318
|
+
raise NotImplementedError('method "glob" not implemented: %r' % self)
|
|
342
319
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
320
|
+
def iglob(
|
|
321
|
+
self: Self,
|
|
322
|
+
pattern: str,
|
|
323
|
+
recursive: bool = True,
|
|
324
|
+
missing_ok: bool = True) -> Iterator[Self]:
|
|
346
325
|
"""Return an iterator of files whose paths match the glob pattern."""
|
|
326
|
+
raise NotImplementedError('method "iglob" not implemented: %r' % self)
|
|
347
327
|
|
|
348
|
-
@method_not_implemented
|
|
349
328
|
def glob_stat(
|
|
350
|
-
self,
|
|
351
|
-
|
|
329
|
+
self,
|
|
330
|
+
pattern: str,
|
|
331
|
+
recursive: bool = True,
|
|
332
|
+
missing_ok: bool = True) -> Iterator[FileEntry]:
|
|
352
333
|
"""Return an iterator of files with stat whose paths match the glob pattern."""
|
|
334
|
+
raise NotImplementedError(
|
|
335
|
+
'method "glob_stat" not implemented: %r' % self)
|
|
353
336
|
|
|
354
|
-
|
|
355
|
-
def load(self) -> BinaryIO: # type: ignore
|
|
337
|
+
def load(self) -> BinaryIO:
|
|
356
338
|
"""Read all content in binary."""
|
|
339
|
+
raise NotImplementedError('method "load" not implemented: %r' % self)
|
|
357
340
|
|
|
358
|
-
@method_not_implemented
|
|
359
341
|
def save(self, file_object: BinaryIO):
|
|
360
342
|
"""Write the opened binary stream to the path."""
|
|
343
|
+
raise NotImplementedError('method "save" not implemented: %r' % self)
|
|
361
344
|
|
|
362
|
-
|
|
363
|
-
def joinpath(self, *other_paths: "PathLike") -> 'BasePath': # type: ignore
|
|
345
|
+
def joinpath(self: Self, *other_paths: "PathLike") -> Self:
|
|
364
346
|
"""Join or or more path."""
|
|
347
|
+
raise NotImplementedError(
|
|
348
|
+
'method "joinpath" not implemented: %r' % self)
|
|
365
349
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
""
|
|
350
|
+
def abspath(self):
|
|
351
|
+
"""Return a normalized absolute version of the path."""
|
|
352
|
+
raise NotImplementedError('method "abspath" not implemented: %r' % self)
|
|
369
353
|
|
|
370
|
-
|
|
371
|
-
def realpath(self): # type: ignore
|
|
354
|
+
def realpath(self):
|
|
372
355
|
"""Return the canonical path of the path."""
|
|
356
|
+
raise NotImplementedError(
|
|
357
|
+
'method "realpath" not implemented: %r' % self)
|
|
373
358
|
|
|
374
|
-
|
|
375
|
-
def relpath(self, start=None): # type: ignore
|
|
359
|
+
def relpath(self, start=None):
|
|
376
360
|
"""Return the relative path."""
|
|
361
|
+
raise NotImplementedError('method "relpath" not implemented: %r' % self)
|
|
377
362
|
|
|
378
|
-
|
|
379
|
-
def is_absolute(self) -> bool: # type: ignore
|
|
363
|
+
def is_absolute(self) -> bool:
|
|
380
364
|
"""Return True if the path is an absolute pathname."""
|
|
365
|
+
raise NotImplementedError(
|
|
366
|
+
'method "is_absolute" not implemented: %r' % self)
|
|
381
367
|
|
|
382
|
-
|
|
383
|
-
def is_mount(self) -> bool: # type: ignore
|
|
368
|
+
def is_mount(self) -> bool:
|
|
384
369
|
"""Return True if the path is a mount point."""
|
|
370
|
+
raise NotImplementedError(
|
|
371
|
+
'method "is_mount" not implemented: %r' % self)
|
|
385
372
|
|
|
386
|
-
|
|
387
|
-
def resolve(self): # type: ignore
|
|
373
|
+
def resolve(self):
|
|
388
374
|
"""Alias of realpath."""
|
|
375
|
+
raise NotImplementedError('method "resolve" not implemented: %r' % self)
|
|
389
376
|
|
|
390
377
|
def touch(self):
|
|
391
378
|
with self.open('w'):
|
|
392
379
|
pass
|
|
393
380
|
|
|
394
|
-
# will be deleted in next version
|
|
381
|
+
# TODO: will be deleted in next version
|
|
395
382
|
def is_link(self) -> bool:
|
|
396
383
|
return self.is_symlink()
|
|
397
384
|
|
|
@@ -401,8 +388,6 @@ class BasePath:
|
|
|
401
388
|
'''
|
|
402
389
|
self.mkdir(parents=True, exist_ok=exist_ok)
|
|
403
390
|
|
|
404
|
-
# pytype: enable=bad-return-type
|
|
405
|
-
|
|
406
391
|
|
|
407
392
|
PathLike = Union[str, BasePath, _PathLike]
|
|
408
393
|
|
|
@@ -410,14 +395,14 @@ PathLike = Union[str, BasePath, _PathLike]
|
|
|
410
395
|
class BaseURIPath(BasePath):
|
|
411
396
|
|
|
412
397
|
# #####
|
|
413
|
-
# Backwards compatible API, will be removed in megfile 1.0
|
|
398
|
+
# TODO: Backwards compatible API, will be removed in megfile 1.0
|
|
414
399
|
@classmethod
|
|
415
400
|
def get_protocol(self) -> Optional[str]:
|
|
416
401
|
pass # pragma: no cover
|
|
417
402
|
|
|
418
403
|
@classproperty
|
|
419
404
|
def protocol(cls) -> str:
|
|
420
|
-
return cls.get_protocol()
|
|
405
|
+
return cls.get_protocol() or ""
|
|
421
406
|
|
|
422
407
|
def make_uri(self) -> str:
|
|
423
408
|
return self.path_with_protocol
|
|
@@ -427,20 +412,20 @@ class BaseURIPath(BasePath):
|
|
|
427
412
|
|
|
428
413
|
# #####
|
|
429
414
|
|
|
430
|
-
@
|
|
415
|
+
@cached_property
|
|
431
416
|
def path_with_protocol(self) -> str:
|
|
432
417
|
'''Return path with protocol, like file:///root, s3://bucket/key'''
|
|
433
418
|
path = self.path
|
|
434
|
-
protocol_prefix = self.protocol + "://"
|
|
419
|
+
protocol_prefix = self.protocol + "://" # pyre-ignore[58]
|
|
435
420
|
if path.startswith(protocol_prefix):
|
|
436
421
|
return path
|
|
437
422
|
return protocol_prefix + path.lstrip('/')
|
|
438
423
|
|
|
439
|
-
@
|
|
424
|
+
@cached_property
|
|
440
425
|
def path_without_protocol(self) -> str:
|
|
441
426
|
'''Return path without protocol, example: if path is s3://bucket/key, return bucket/key'''
|
|
442
427
|
path = self.path
|
|
443
|
-
protocol_prefix = self.protocol + "://"
|
|
428
|
+
protocol_prefix = self.protocol + "://" # pyre-ignore[58]
|
|
444
429
|
if path.startswith(protocol_prefix):
|
|
445
430
|
path = path[len(protocol_prefix):]
|
|
446
431
|
return path
|
|
@@ -449,25 +434,6 @@ class BaseURIPath(BasePath):
|
|
|
449
434
|
'''Return a string representation of the path with forward slashes (/)'''
|
|
450
435
|
return self.path_with_protocol
|
|
451
436
|
|
|
452
|
-
@classmethod
|
|
453
|
-
def from_path(cls, path) -> "BaseURIPath":
|
|
454
|
-
"""Return new instance of this class
|
|
455
|
-
|
|
456
|
-
:param path: new path
|
|
457
|
-
:return: new instance of new path
|
|
458
|
-
:rtype: BaseURIPath
|
|
459
|
-
"""
|
|
460
|
-
return cls(path)
|
|
461
|
-
|
|
462
|
-
@classmethod
|
|
463
|
-
def from_uri(cls, path: str) -> "BaseURIPath":
|
|
464
|
-
protocol_prefix = cls.protocol + "://"
|
|
465
|
-
if path[:len(protocol_prefix)] != protocol_prefix:
|
|
466
|
-
raise ValueError(
|
|
467
|
-
"protocol not match, expected: %r, got: %r" %
|
|
468
|
-
(cls.protocol, path))
|
|
469
|
-
return cls.from_path(path[len(protocol_prefix):])
|
|
470
|
-
|
|
471
437
|
def __fspath__(self) -> str:
|
|
472
438
|
return self.as_uri()
|
|
473
439
|
|
|
@@ -513,11 +479,11 @@ class BaseURIPath(BasePath):
|
|
|
513
479
|
|
|
514
480
|
@classproperty
|
|
515
481
|
def root(self) -> str:
|
|
516
|
-
return self.protocol + '://'
|
|
482
|
+
return self.protocol + '://' # pyre-ignore[58]
|
|
517
483
|
|
|
518
484
|
@classproperty
|
|
519
485
|
def anchor(self) -> str:
|
|
520
|
-
return self.root
|
|
486
|
+
return self.root # pyre-ignore[7]
|
|
521
487
|
|
|
522
488
|
|
|
523
489
|
class URIPath(BaseURIPath):
|
|
@@ -527,7 +493,28 @@ class URIPath(BaseURIPath):
|
|
|
527
493
|
path = self.from_path(path).joinpath(*other_paths)
|
|
528
494
|
self.path = str(path)
|
|
529
495
|
|
|
530
|
-
|
|
496
|
+
@classmethod
|
|
497
|
+
def from_path(cls: Type[Self], path: PathLike) -> Self:
|
|
498
|
+
"""Return new instance of this class
|
|
499
|
+
|
|
500
|
+
:param path: new path
|
|
501
|
+
|
|
502
|
+
:return: new instance of new path
|
|
503
|
+
:rtype: Self
|
|
504
|
+
"""
|
|
505
|
+
return cls(path) # pyre-ignore[19]
|
|
506
|
+
|
|
507
|
+
@classmethod
|
|
508
|
+
def from_uri(cls: Type[Self], path: PathLike) -> Self:
|
|
509
|
+
path = fspath(path)
|
|
510
|
+
protocol_prefix = cls.protocol + "://"
|
|
511
|
+
if path[:len(protocol_prefix)] != protocol_prefix:
|
|
512
|
+
raise ValueError(
|
|
513
|
+
"protocol not match, expected: %r, got: %r" %
|
|
514
|
+
(cls.protocol, path))
|
|
515
|
+
return cls.from_path(path[len(protocol_prefix):])
|
|
516
|
+
|
|
517
|
+
def __truediv__(self: Self, other_path: PathLike) -> Self:
|
|
531
518
|
if isinstance(other_path, BaseURIPath):
|
|
532
519
|
if self.protocol != other_path.protocol:
|
|
533
520
|
raise TypeError(
|
|
@@ -537,27 +524,27 @@ class URIPath(BaseURIPath):
|
|
|
537
524
|
raise TypeError("%r is not 'str' nor 'URIPath'" % other_path)
|
|
538
525
|
return self.joinpath(other_path)
|
|
539
526
|
|
|
540
|
-
def joinpath(self, *other_paths: PathLike) ->
|
|
527
|
+
def joinpath(self: Self, *other_paths: PathLike) -> Self:
|
|
541
528
|
'''Calling this method is equivalent to combining the path with each of the other arguments in turn'''
|
|
542
529
|
return self.from_path(uri_join(str(self), *map(str, other_paths)))
|
|
543
530
|
|
|
544
|
-
@
|
|
545
|
-
def parts(self) -> Tuple[str]:
|
|
531
|
+
@cached_property
|
|
532
|
+
def parts(self) -> Tuple[str, ...]:
|
|
546
533
|
'''A tuple giving access to the path’s various components'''
|
|
547
534
|
parts = [self.root]
|
|
548
535
|
path = self.path_without_protocol
|
|
549
536
|
path = path.lstrip('/')
|
|
550
537
|
if path != '':
|
|
551
538
|
parts.extend(path.split('/'))
|
|
552
|
-
return tuple(parts)
|
|
539
|
+
return tuple(parts) # pyre-ignore[7]
|
|
553
540
|
|
|
554
|
-
@
|
|
541
|
+
@cached_property
|
|
555
542
|
def parents(self) -> "URIPathParents":
|
|
556
543
|
'''An immutable sequence providing access to the logical ancestors of the path'''
|
|
557
544
|
return URIPathParents(self)
|
|
558
545
|
|
|
559
|
-
@
|
|
560
|
-
def parent(self) ->
|
|
546
|
+
@cached_property
|
|
547
|
+
def parent(self: Self) -> Self:
|
|
561
548
|
'''The logical parent of the path'''
|
|
562
549
|
if self.path_without_protocol == "/":
|
|
563
550
|
return self
|
|
@@ -565,15 +552,16 @@ class URIPath(BaseURIPath):
|
|
|
565
552
|
return self.parents[0]
|
|
566
553
|
return self.from_path("")
|
|
567
554
|
|
|
568
|
-
@
|
|
555
|
+
@cached_property
|
|
569
556
|
def name(self) -> str:
|
|
570
557
|
'''A string representing the final path component, excluding the drive and root'''
|
|
571
558
|
parts = self.parts
|
|
572
|
-
if len(parts
|
|
559
|
+
if len(parts
|
|
560
|
+
) == 1 and parts[0] == self.protocol + "://": # pyre-ignore[58]
|
|
573
561
|
return ''
|
|
574
562
|
return parts[-1]
|
|
575
563
|
|
|
576
|
-
@
|
|
564
|
+
@cached_property
|
|
577
565
|
def suffix(self) -> str:
|
|
578
566
|
'''The file extension of the final component'''
|
|
579
567
|
name = self.name
|
|
@@ -582,7 +570,7 @@ class URIPath(BaseURIPath):
|
|
|
582
570
|
return name[i:]
|
|
583
571
|
return ''
|
|
584
572
|
|
|
585
|
-
@
|
|
573
|
+
@cached_property
|
|
586
574
|
def suffixes(self) -> List[str]:
|
|
587
575
|
'''A list of the path’s file extensions'''
|
|
588
576
|
name = self.name
|
|
@@ -591,7 +579,7 @@ class URIPath(BaseURIPath):
|
|
|
591
579
|
name = name.lstrip('.')
|
|
592
580
|
return ['.' + suffix for suffix in name.split('.')[1:]]
|
|
593
581
|
|
|
594
|
-
@
|
|
582
|
+
@cached_property
|
|
595
583
|
def stem(self) -> str:
|
|
596
584
|
'''The final path component, without its suffix'''
|
|
597
585
|
name = self.name
|
|
@@ -620,7 +608,7 @@ class URIPath(BaseURIPath):
|
|
|
620
608
|
except Exception:
|
|
621
609
|
return False
|
|
622
610
|
|
|
623
|
-
def relative_to(self, *other) ->
|
|
611
|
+
def relative_to(self: Self, *other: str) -> Self:
|
|
624
612
|
'''
|
|
625
613
|
Compute a version of this path relative to the path represented by other.
|
|
626
614
|
If it’s impossible, ValueError is raised.
|
|
@@ -637,21 +625,21 @@ class URIPath(BaseURIPath):
|
|
|
637
625
|
if path.startswith(other_path):
|
|
638
626
|
relative = path[len(other_path):]
|
|
639
627
|
relative = relative.lstrip('/')
|
|
640
|
-
return type(self)(relative)
|
|
628
|
+
return type(self)(relative) # pyre-ignore[19]
|
|
641
629
|
else:
|
|
642
630
|
raise ValueError("%r does not start with %r" % (path, other))
|
|
643
631
|
|
|
644
|
-
def with_name(self, name) ->
|
|
632
|
+
def with_name(self: Self, name: str) -> Self:
|
|
645
633
|
'''Return a new path with the name changed'''
|
|
646
634
|
path = str(self)
|
|
647
635
|
raw_name = self.name
|
|
648
636
|
return self.from_path(path[:len(path) - len(raw_name)] + name)
|
|
649
637
|
|
|
650
|
-
def with_stem(self, stem) ->
|
|
638
|
+
def with_stem(self: Self, stem: str) -> Self:
|
|
651
639
|
'''Return a new path with the stem changed'''
|
|
652
640
|
return self.with_name("".join([stem, self.suffix]))
|
|
653
641
|
|
|
654
|
-
def with_suffix(self, suffix) ->
|
|
642
|
+
def with_suffix(self: Self, suffix: str) -> Self:
|
|
655
643
|
'''Return a new path with the suffix changed'''
|
|
656
644
|
path = str(self)
|
|
657
645
|
raw_suffix = self.suffix
|
|
@@ -700,7 +688,7 @@ class URIPath(BaseURIPath):
|
|
|
700
688
|
return False
|
|
701
689
|
|
|
702
690
|
def abspath(self) -> str:
|
|
703
|
-
"""Return a normalized
|
|
691
|
+
"""Return a normalized absolute version of the path."""
|
|
704
692
|
return self.path_with_protocol
|
|
705
693
|
|
|
706
694
|
def realpath(self) -> str:
|
|
@@ -723,14 +711,14 @@ class URIPath(BaseURIPath):
|
|
|
723
711
|
def read_bytes(self) -> bytes:
|
|
724
712
|
'''Return the binary contents of the pointed-to file as a bytes object'''
|
|
725
713
|
with self.open(mode='rb') as f:
|
|
726
|
-
return f.read()
|
|
714
|
+
return f.read() # pytype: disable=bad-return-type
|
|
727
715
|
|
|
728
716
|
def read_text(self) -> str:
|
|
729
717
|
'''Return the decoded contents of the pointed-to file as a string'''
|
|
730
718
|
with self.open(mode='r') as f:
|
|
731
|
-
return f.read()
|
|
719
|
+
return f.read() # pytype: disable=bad-return-type
|
|
732
720
|
|
|
733
|
-
def rename(self, dst_path: PathLike, overwrite: bool = True) ->
|
|
721
|
+
def rename(self: Self, dst_path: PathLike, overwrite: bool = True) -> Self:
|
|
734
722
|
'''
|
|
735
723
|
rename file
|
|
736
724
|
|
|
@@ -739,7 +727,7 @@ class URIPath(BaseURIPath):
|
|
|
739
727
|
'''
|
|
740
728
|
raise NotImplementedError(f"'rename' is unsupported on '{type(self)}'")
|
|
741
729
|
|
|
742
|
-
def replace(self, dst_path: PathLike, overwrite: bool = True) ->
|
|
730
|
+
def replace(self: Self, dst_path: PathLike, overwrite: bool = True) -> Self:
|
|
743
731
|
'''
|
|
744
732
|
move file
|
|
745
733
|
|
|
@@ -748,7 +736,7 @@ class URIPath(BaseURIPath):
|
|
|
748
736
|
'''
|
|
749
737
|
return self.rename(dst_path=dst_path, overwrite=overwrite)
|
|
750
738
|
|
|
751
|
-
def rglob(self, pattern) -> List[
|
|
739
|
+
def rglob(self: Self, pattern) -> List[Self]:
|
|
752
740
|
'''
|
|
753
741
|
This is like calling Path.glob() with “**/” added in front of the given relative pattern
|
|
754
742
|
'''
|
|
@@ -785,9 +773,7 @@ class URIPath(BaseURIPath):
|
|
|
785
773
|
symlink_to's arguments is the reverse of symlink's.
|
|
786
774
|
Target_is_directory’s value is ignored, only be compatible with pathlib.Path
|
|
787
775
|
'''
|
|
788
|
-
return self.from_path(
|
|
789
|
-
target).symlink( # type: ignore
|
|
790
|
-
dst_path=self.path)
|
|
776
|
+
return self.from_path(target).symlink(dst_path=self.path)
|
|
791
777
|
|
|
792
778
|
def hardlink_to(self, target):
|
|
793
779
|
'''
|
|
@@ -831,18 +817,18 @@ class URIPath(BaseURIPath):
|
|
|
831
817
|
raise NotImplementedError(
|
|
832
818
|
f"'expanduser' is unsupported on '{type(self)}'")
|
|
833
819
|
|
|
834
|
-
def cwd(self) ->
|
|
820
|
+
def cwd(self: Self) -> Self:
|
|
835
821
|
'''Return current working directory
|
|
836
822
|
|
|
837
823
|
returns: Current working directory
|
|
838
824
|
'''
|
|
839
825
|
raise NotImplementedError(f"'cwd' is unsupported on '{type(self)}'")
|
|
840
826
|
|
|
841
|
-
def iterdir(self) -> Iterator[
|
|
827
|
+
def iterdir(self: Self) -> Iterator[Self]:
|
|
842
828
|
'''
|
|
843
|
-
Get all contents of given fs path. The result is in
|
|
829
|
+
Get all contents of given fs path. The result is in ascending alphabetical order.
|
|
844
830
|
|
|
845
|
-
:returns: All contents have in the path in
|
|
831
|
+
:returns: All contents have in the path in ascending alphabetical order
|
|
846
832
|
'''
|
|
847
833
|
raise NotImplementedError(f"'iterdir' is unsupported on '{type(self)}'")
|
|
848
834
|
|
|
@@ -852,7 +838,7 @@ class URIPath(BaseURIPath):
|
|
|
852
838
|
'''
|
|
853
839
|
raise NotImplementedError(f"'owner' is unsupported on '{type(self)}'")
|
|
854
840
|
|
|
855
|
-
def absolute(self) ->
|
|
841
|
+
def absolute(self: Self) -> Self:
|
|
856
842
|
'''
|
|
857
843
|
Make the path absolute, without normalization or resolving symlinks. Returns a new path object
|
|
858
844
|
'''
|