megfile 3.1.6.post1__py3-none-any.whl → 4.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/cli.py +12 -7
- megfile/config.py +27 -39
- megfile/fs.py +169 -11
- megfile/fs_path.py +183 -259
- megfile/hdfs.py +106 -5
- megfile/hdfs_path.py +34 -90
- megfile/http.py +50 -1
- megfile/http_path.py +27 -65
- megfile/interfaces.py +1 -8
- megfile/lib/base_prefetch_reader.py +62 -78
- megfile/lib/combine_reader.py +5 -0
- megfile/lib/glob.py +3 -6
- megfile/lib/hdfs_prefetch_reader.py +7 -7
- megfile/lib/http_prefetch_reader.py +6 -6
- megfile/lib/s3_buffered_writer.py +67 -64
- megfile/lib/s3_cached_handler.py +1 -2
- megfile/lib/s3_limited_seekable_writer.py +3 -7
- megfile/lib/s3_memory_handler.py +1 -2
- megfile/lib/s3_pipe_handler.py +1 -2
- megfile/lib/s3_prefetch_reader.py +10 -19
- megfile/lib/s3_share_cache_reader.py +8 -5
- megfile/pathlike.py +397 -401
- megfile/s3.py +118 -17
- megfile/s3_path.py +126 -209
- megfile/sftp.py +300 -10
- megfile/sftp_path.py +46 -322
- megfile/smart.py +33 -27
- megfile/smart_path.py +9 -14
- megfile/stdio.py +1 -1
- megfile/stdio_path.py +2 -2
- megfile/utils/__init__.py +3 -4
- megfile/version.py +1 -1
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/METADATA +7 -7
- megfile-4.0.0.dist-info/RECORD +52 -0
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/WHEEL +1 -1
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/top_level.txt +0 -2
- docs/conf.py +0 -65
- megfile-3.1.6.post1.dist-info/RECORD +0 -55
- scripts/convert_results_to_sarif.py +0 -91
- scripts/generate_file.py +0 -344
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/LICENSE +0 -0
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/LICENSE.pyre +0 -0
- {megfile-3.1.6.post1.dist-info → megfile-4.0.0.dist-info}/entry_points.txt +0 -0
megfile/pathlike.py
CHANGED
|
@@ -216,6 +216,8 @@ class FileEntry(NamedTuple):
|
|
|
216
216
|
|
|
217
217
|
|
|
218
218
|
class BasePath:
|
|
219
|
+
protocol = ""
|
|
220
|
+
|
|
219
221
|
def __init__(self, path: "PathLike"):
|
|
220
222
|
self.path = str(path)
|
|
221
223
|
|
|
@@ -229,7 +231,7 @@ class BasePath:
|
|
|
229
231
|
return str(self).encode()
|
|
230
232
|
|
|
231
233
|
def __fspath__(self) -> str:
|
|
232
|
-
return self.
|
|
234
|
+
return self.as_uri()
|
|
233
235
|
|
|
234
236
|
def __hash__(self) -> int:
|
|
235
237
|
return hash(fspath(self))
|
|
@@ -237,207 +239,9 @@ class BasePath:
|
|
|
237
239
|
def __eq__(self, other_path: "BasePath") -> bool:
|
|
238
240
|
return fspath(self) == fspath(other_path)
|
|
239
241
|
|
|
240
|
-
def
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
def is_file(self, followlinks: bool = False) -> bool:
|
|
245
|
-
"""Return True if the path points to a regular file."""
|
|
246
|
-
raise NotImplementedError('method "is_file" not implemented: %r' % self)
|
|
247
|
-
|
|
248
|
-
def is_symlink(self) -> bool:
|
|
249
|
-
return False
|
|
250
|
-
|
|
251
|
-
def access(self, mode: Access) -> bool:
|
|
252
|
-
"""Return True if the path has access permission described by mode."""
|
|
253
|
-
raise NotImplementedError('method "access" not implemented: %r' % self)
|
|
254
|
-
|
|
255
|
-
def exists(self, followlinks: bool = False) -> bool:
|
|
256
|
-
"""Whether the path points to an existing file or directory."""
|
|
257
|
-
raise NotImplementedError('method "exists" not implemented: %r' % self)
|
|
258
|
-
|
|
259
|
-
def listdir(self) -> List[str]:
|
|
260
|
-
"""Return the names of the entries in the directory the path points to."""
|
|
261
|
-
raise NotImplementedError('method "listdir" not implemented: %r' % self)
|
|
262
|
-
|
|
263
|
-
def scandir(self) -> Iterator[FileEntry]:
|
|
264
|
-
"""
|
|
265
|
-
Return an iterator of FileEntry objects corresponding to the entries
|
|
266
|
-
in the directory.
|
|
267
|
-
"""
|
|
268
|
-
raise NotImplementedError('method "scandir" not implemented: %r' % self)
|
|
269
|
-
|
|
270
|
-
def getsize(self, follow_symlinks: bool = True) -> int:
|
|
271
|
-
"""Return the size, in bytes."""
|
|
272
|
-
raise NotImplementedError('method "getsize" not implemented: %r' % self)
|
|
273
|
-
|
|
274
|
-
def getmtime(self, follow_symlinks: bool = True) -> float:
|
|
275
|
-
"""Return the time of last modification."""
|
|
276
|
-
raise NotImplementedError('method "getmtime" not implemented: %r' % self)
|
|
277
|
-
|
|
278
|
-
def stat(self, follow_symlinks=True) -> StatResult:
|
|
279
|
-
"""Get the status of the path."""
|
|
280
|
-
raise NotImplementedError('method "stat" not implemented: %r' % self)
|
|
281
|
-
|
|
282
|
-
def remove(self, missing_ok: bool = False) -> None:
|
|
283
|
-
"""Remove (delete) the file."""
|
|
284
|
-
raise NotImplementedError('method "remove" not implemented: %r' % self)
|
|
285
|
-
|
|
286
|
-
def unlink(self, missing_ok: bool = False) -> None:
|
|
287
|
-
"""Remove (delete) the file."""
|
|
288
|
-
raise NotImplementedError('method "unlink" not implemented: %r' % self)
|
|
289
|
-
|
|
290
|
-
def mkdir(self, mode=0o777, parents: bool = False, exist_ok: bool = False) -> None:
|
|
291
|
-
"""Create a directory."""
|
|
292
|
-
raise NotImplementedError('method "mkdir" not implemented: %r' % self)
|
|
293
|
-
|
|
294
|
-
def rmdir(self) -> None:
|
|
295
|
-
"""Remove (delete) the directory."""
|
|
296
|
-
raise NotImplementedError('method "rmdir" not implemented: %r' % self)
|
|
297
|
-
|
|
298
|
-
def open(self, mode: str = "r", **kwargs) -> IO:
|
|
299
|
-
"""Open the file with mode."""
|
|
300
|
-
raise NotImplementedError('method "open" not implemented: %r' % self)
|
|
301
|
-
|
|
302
|
-
def walk(
|
|
303
|
-
self, followlinks: bool = False
|
|
304
|
-
) -> Iterator[Tuple[str, List[str], List[str]]]:
|
|
305
|
-
"""Generate the file names in a directory tree by walking the tree."""
|
|
306
|
-
raise NotImplementedError('method "walk" not implemented: %r' % self)
|
|
307
|
-
|
|
308
|
-
def scan(self, missing_ok: bool = True, followlinks: bool = False) -> Iterator[str]:
|
|
309
|
-
"""Iterate through the files in the directory."""
|
|
310
|
-
raise NotImplementedError('method "scan" not implemented: %r' % self)
|
|
311
|
-
|
|
312
|
-
def scan_stat(
|
|
313
|
-
self, missing_ok: bool = True, followlinks: bool = False
|
|
314
|
-
) -> Iterator[FileEntry]:
|
|
315
|
-
"""Iterate through the files in the directory, with file stat."""
|
|
316
|
-
raise NotImplementedError('method "scan_stat" not implemented: %r' % self)
|
|
317
|
-
|
|
318
|
-
def glob(
|
|
319
|
-
self: Self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
320
|
-
) -> List[Self]:
|
|
321
|
-
"""Return files whose paths match the glob pattern."""
|
|
322
|
-
raise NotImplementedError('method "glob" not implemented: %r' % self)
|
|
323
|
-
|
|
324
|
-
def iglob(
|
|
325
|
-
self: Self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
326
|
-
) -> Iterator[Self]:
|
|
327
|
-
"""Return an iterator of files whose paths match the glob pattern."""
|
|
328
|
-
raise NotImplementedError('method "iglob" not implemented: %r' % self)
|
|
329
|
-
|
|
330
|
-
def glob_stat(
|
|
331
|
-
self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
332
|
-
) -> Iterator[FileEntry]:
|
|
333
|
-
"""Return an iterator of files with stat whose paths match the glob pattern."""
|
|
334
|
-
raise NotImplementedError('method "glob_stat" not implemented: %r' % self)
|
|
335
|
-
|
|
336
|
-
def load(self) -> BinaryIO:
|
|
337
|
-
"""Read all content in binary."""
|
|
338
|
-
raise NotImplementedError('method "load" not implemented: %r' % self)
|
|
339
|
-
|
|
340
|
-
def save(self, file_object: BinaryIO):
|
|
341
|
-
"""Write the opened binary stream to the path."""
|
|
342
|
-
raise NotImplementedError('method "save" not implemented: %r' % self)
|
|
343
|
-
|
|
344
|
-
def joinpath(self: Self, *other_paths: "PathLike") -> Self:
|
|
345
|
-
"""Join or or more path."""
|
|
346
|
-
raise NotImplementedError('method "joinpath" not implemented: %r' % self)
|
|
347
|
-
|
|
348
|
-
def abspath(self):
|
|
349
|
-
"""Return a normalized absolute version of the path."""
|
|
350
|
-
raise NotImplementedError('method "abspath" not implemented: %r' % self)
|
|
351
|
-
|
|
352
|
-
def realpath(self):
|
|
353
|
-
"""Return the canonical path of the path."""
|
|
354
|
-
raise NotImplementedError('method "realpath" not implemented: %r' % self)
|
|
355
|
-
|
|
356
|
-
def relpath(self, start=None):
|
|
357
|
-
"""Return the relative path."""
|
|
358
|
-
raise NotImplementedError('method "relpath" not implemented: %r' % self)
|
|
359
|
-
|
|
360
|
-
def is_absolute(self) -> bool:
|
|
361
|
-
"""Return True if the path is an absolute pathname."""
|
|
362
|
-
raise NotImplementedError('method "is_absolute" not implemented: %r' % self)
|
|
363
|
-
|
|
364
|
-
def is_mount(self) -> bool:
|
|
365
|
-
"""Return True if the path is a mount point."""
|
|
366
|
-
raise NotImplementedError('method "is_mount" not implemented: %r' % self)
|
|
367
|
-
|
|
368
|
-
def resolve(self):
|
|
369
|
-
"""Alias of realpath."""
|
|
370
|
-
raise NotImplementedError('method "resolve" not implemented: %r' % self)
|
|
371
|
-
|
|
372
|
-
def touch(self):
|
|
373
|
-
with self.open("w"):
|
|
374
|
-
pass
|
|
375
|
-
|
|
376
|
-
# TODO: will be deleted in next version
|
|
377
|
-
def is_link(self) -> bool:
|
|
378
|
-
return self.is_symlink()
|
|
379
|
-
|
|
380
|
-
def makedirs(self, exist_ok: bool = False) -> None:
|
|
381
|
-
"""
|
|
382
|
-
Recursive directory creation function. Like mkdir(), but makes all
|
|
383
|
-
intermediate-level directories needed to contain the leaf directory.
|
|
384
|
-
"""
|
|
385
|
-
self.mkdir(parents=True, exist_ok=exist_ok)
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
PathLike = Union[str, BasePath, _PathLike]
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
class BaseURIPath(BasePath):
|
|
392
|
-
# #####
|
|
393
|
-
# TODO: Backwards compatible API, will be removed in megfile 1.0
|
|
394
|
-
@classmethod
|
|
395
|
-
def get_protocol(self) -> Optional[str]:
|
|
396
|
-
pass # pragma: no cover
|
|
397
|
-
|
|
398
|
-
@classproperty
|
|
399
|
-
def protocol(cls) -> str:
|
|
400
|
-
return cls.get_protocol() or ""
|
|
401
|
-
|
|
402
|
-
def make_uri(self) -> str:
|
|
403
|
-
return self.path_with_protocol
|
|
404
|
-
|
|
405
|
-
def as_uri(self) -> str:
|
|
406
|
-
return self.make_uri()
|
|
407
|
-
|
|
408
|
-
# #####
|
|
409
|
-
|
|
410
|
-
@cached_property
|
|
411
|
-
def path_with_protocol(self) -> str:
|
|
412
|
-
"""Return path with protocol, like file:///root, s3://bucket/key"""
|
|
413
|
-
path = self.path
|
|
414
|
-
protocol_prefix = self.protocol + "://" # pyre-ignore[58]
|
|
415
|
-
if path.startswith(protocol_prefix):
|
|
416
|
-
return path
|
|
417
|
-
return protocol_prefix + path.lstrip("/")
|
|
418
|
-
|
|
419
|
-
@cached_property
|
|
420
|
-
def path_without_protocol(self) -> str:
|
|
421
|
-
"""
|
|
422
|
-
Return path without protocol, example: if path is s3://bucket/key,
|
|
423
|
-
return bucket/key
|
|
424
|
-
"""
|
|
425
|
-
path = self.path
|
|
426
|
-
protocol_prefix = self.protocol + "://" # pyre-ignore[58]
|
|
427
|
-
if path.startswith(protocol_prefix):
|
|
428
|
-
path = path[len(protocol_prefix) :]
|
|
429
|
-
return path
|
|
430
|
-
|
|
431
|
-
def as_posix(self) -> str:
|
|
432
|
-
"""Return a string representation of the path with forward slashes (/)"""
|
|
433
|
-
return self.path_with_protocol
|
|
434
|
-
|
|
435
|
-
def __fspath__(self) -> str:
|
|
436
|
-
return self.as_uri()
|
|
437
|
-
|
|
438
|
-
def __lt__(self, other_path: "BaseURIPath") -> bool:
|
|
439
|
-
if not isinstance(other_path, BaseURIPath):
|
|
440
|
-
raise TypeError("%r is not 'URIPath'" % other_path)
|
|
242
|
+
def __lt__(self, other_path: "BasePath") -> bool:
|
|
243
|
+
if not isinstance(other_path, BasePath):
|
|
244
|
+
raise TypeError("%r is not 'BasePath'" % other_path)
|
|
441
245
|
if self.protocol != other_path.protocol:
|
|
442
246
|
raise TypeError(
|
|
443
247
|
"'<' not supported between instances of %r and %r"
|
|
@@ -445,9 +249,9 @@ class BaseURIPath(BasePath):
|
|
|
445
249
|
)
|
|
446
250
|
return fspath(self) < fspath(other_path)
|
|
447
251
|
|
|
448
|
-
def __le__(self, other_path: "
|
|
449
|
-
if not isinstance(other_path,
|
|
450
|
-
raise TypeError("%r is not '
|
|
252
|
+
def __le__(self, other_path: "BasePath") -> bool:
|
|
253
|
+
if not isinstance(other_path, BasePath):
|
|
254
|
+
raise TypeError("%r is not 'BasePath'" % other_path)
|
|
451
255
|
if self.protocol != other_path.protocol:
|
|
452
256
|
raise TypeError(
|
|
453
257
|
"'<=' not supported between instances of %r and %r"
|
|
@@ -455,9 +259,9 @@ class BaseURIPath(BasePath):
|
|
|
455
259
|
)
|
|
456
260
|
return str(self) <= str(other_path)
|
|
457
261
|
|
|
458
|
-
def __gt__(self, other_path: "
|
|
459
|
-
if not isinstance(other_path,
|
|
460
|
-
raise TypeError("%r is not '
|
|
262
|
+
def __gt__(self, other_path: "BasePath") -> bool:
|
|
263
|
+
if not isinstance(other_path, BasePath):
|
|
264
|
+
raise TypeError("%r is not 'BasePath'" % other_path)
|
|
461
265
|
if self.protocol != other_path.protocol:
|
|
462
266
|
raise TypeError(
|
|
463
267
|
"'>' not supported between instances of %r and %r"
|
|
@@ -465,9 +269,9 @@ class BaseURIPath(BasePath):
|
|
|
465
269
|
)
|
|
466
270
|
return str(self) > str(other_path)
|
|
467
271
|
|
|
468
|
-
def __ge__(self, other_path: "
|
|
469
|
-
if not isinstance(other_path,
|
|
470
|
-
raise TypeError("%r is not '
|
|
272
|
+
def __ge__(self, other_path: "BasePath") -> bool:
|
|
273
|
+
if not isinstance(other_path, BasePath):
|
|
274
|
+
raise TypeError("%r is not 'BasePath'" % other_path)
|
|
471
275
|
if self.protocol != other_path.protocol:
|
|
472
276
|
raise TypeError(
|
|
473
277
|
"'>=' not supported between instances of %r and %r"
|
|
@@ -475,27 +279,39 @@ class BaseURIPath(BasePath):
|
|
|
475
279
|
)
|
|
476
280
|
return str(self) >= str(other_path)
|
|
477
281
|
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return ""
|
|
282
|
+
def __truediv__(self: Self, other_path: "PathLike") -> Self:
|
|
283
|
+
raise NotImplementedError('method "__truediv__" not implemented: %r' % self)
|
|
481
284
|
|
|
482
|
-
@
|
|
483
|
-
def
|
|
484
|
-
|
|
285
|
+
@cached_property
|
|
286
|
+
def path_with_protocol(self) -> str:
|
|
287
|
+
"""Return path with protocol, like file:///root, s3://bucket/key"""
|
|
288
|
+
path = self.path
|
|
289
|
+
protocol_prefix = self.protocol + "://"
|
|
290
|
+
if path.startswith(protocol_prefix):
|
|
291
|
+
return path
|
|
292
|
+
return protocol_prefix + path.lstrip("/")
|
|
485
293
|
|
|
486
|
-
@
|
|
487
|
-
def
|
|
488
|
-
|
|
294
|
+
@cached_property
|
|
295
|
+
def path_without_protocol(self) -> str:
|
|
296
|
+
"""
|
|
297
|
+
Return path without protocol, example: if path is s3://bucket/key,
|
|
298
|
+
return bucket/key
|
|
299
|
+
"""
|
|
300
|
+
path = self.path
|
|
301
|
+
protocol_prefix = self.protocol + "://"
|
|
302
|
+
if path.startswith(protocol_prefix):
|
|
303
|
+
path = path[len(protocol_prefix) :]
|
|
304
|
+
return path
|
|
489
305
|
|
|
306
|
+
def as_uri(self) -> str:
|
|
307
|
+
return self.path_with_protocol
|
|
490
308
|
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
path = self.from_path(path).joinpath(*other_paths)
|
|
495
|
-
self.path = str(path)
|
|
309
|
+
def as_posix(self) -> str:
|
|
310
|
+
"""Return a string representation of the path with forward slashes (/)"""
|
|
311
|
+
return self.path_with_protocol
|
|
496
312
|
|
|
497
313
|
@classmethod
|
|
498
|
-
def from_path(cls: Type[Self], path: PathLike) -> Self:
|
|
314
|
+
def from_path(cls: Type[Self], path: "PathLike") -> Self:
|
|
499
315
|
"""Return new instance of this class
|
|
500
316
|
|
|
501
317
|
:param path: new path
|
|
@@ -506,7 +322,7 @@ class URIPath(BaseURIPath):
|
|
|
506
322
|
return cls(path) # pyre-ignore[19]
|
|
507
323
|
|
|
508
324
|
@classmethod
|
|
509
|
-
def from_uri(cls: Type[Self], path: PathLike) -> Self:
|
|
325
|
+
def from_uri(cls: Type[Self], path: "PathLike") -> Self:
|
|
510
326
|
path = fspath(path)
|
|
511
327
|
protocol_prefix = cls.protocol + "://"
|
|
512
328
|
if path[: len(protocol_prefix)] != protocol_prefix:
|
|
@@ -515,59 +331,12 @@ class URIPath(BaseURIPath):
|
|
|
515
331
|
)
|
|
516
332
|
return cls.from_path(path[len(protocol_prefix) :])
|
|
517
333
|
|
|
518
|
-
def __truediv__(self: Self, other_path: PathLike) -> Self:
|
|
519
|
-
if isinstance(other_path, BaseURIPath):
|
|
520
|
-
if self.protocol != other_path.protocol:
|
|
521
|
-
raise TypeError(
|
|
522
|
-
"'/' not supported between instances of %r and %r"
|
|
523
|
-
% (type(self), type(other_path))
|
|
524
|
-
)
|
|
525
|
-
elif not isinstance(other_path, str):
|
|
526
|
-
raise TypeError("%r is not 'str' nor 'URIPath'" % other_path)
|
|
527
|
-
return self.joinpath(other_path)
|
|
528
|
-
|
|
529
|
-
def joinpath(self: Self, *other_paths: PathLike) -> Self:
|
|
530
|
-
"""
|
|
531
|
-
Calling this method is equivalent to combining the path
|
|
532
|
-
with each of the other arguments in turn
|
|
533
|
-
"""
|
|
534
|
-
return self.from_path(uri_join(str(self), *map(str, other_paths)))
|
|
535
|
-
|
|
536
|
-
@cached_property
|
|
537
|
-
def parts(self) -> Tuple[str, ...]:
|
|
538
|
-
"""A tuple giving access to the path’s various components"""
|
|
539
|
-
parts = [self.root]
|
|
540
|
-
path = self.path_without_protocol
|
|
541
|
-
path = path.lstrip("/")
|
|
542
|
-
if path != "":
|
|
543
|
-
parts.extend(path.split("/"))
|
|
544
|
-
return tuple(parts) # pyre-ignore[7]
|
|
545
|
-
|
|
546
|
-
@cached_property
|
|
547
|
-
def parents(self) -> "URIPathParents":
|
|
548
|
-
"""
|
|
549
|
-
An immutable sequence providing access to the logical ancestors of the path
|
|
550
|
-
"""
|
|
551
|
-
return URIPathParents(self)
|
|
552
|
-
|
|
553
|
-
@cached_property
|
|
554
|
-
def parent(self: Self) -> Self:
|
|
555
|
-
"""The logical parent of the path"""
|
|
556
|
-
if self.path_without_protocol == "/":
|
|
557
|
-
return self
|
|
558
|
-
elif len(self.parents) > 0:
|
|
559
|
-
return self.parents[0]
|
|
560
|
-
return self.from_path("")
|
|
561
|
-
|
|
562
334
|
@cached_property
|
|
563
335
|
def name(self) -> str:
|
|
564
336
|
"""
|
|
565
337
|
A string representing the final path component, excluding the drive and root
|
|
566
338
|
"""
|
|
567
|
-
|
|
568
|
-
if len(parts) == 1 and parts[0] == self.protocol + "://": # pyre-ignore[58]
|
|
569
|
-
return ""
|
|
570
|
-
return parts[-1]
|
|
339
|
+
return self.path_without_protocol
|
|
571
340
|
|
|
572
341
|
@cached_property
|
|
573
342
|
def suffix(self) -> str:
|
|
@@ -599,18 +368,6 @@ class URIPath(BaseURIPath):
|
|
|
599
368
|
def is_reserved(self) -> bool:
|
|
600
369
|
return False
|
|
601
370
|
|
|
602
|
-
def match(self, pattern) -> bool:
|
|
603
|
-
"""
|
|
604
|
-
Match this path against the provided glob-style pattern.
|
|
605
|
-
Return True if matching is successful, False otherwise
|
|
606
|
-
"""
|
|
607
|
-
match = _compile_pattern(pattern)
|
|
608
|
-
for index in range(len(self.parts), 0, -1):
|
|
609
|
-
path = "/".join(self.parts[index:])
|
|
610
|
-
if match(path) is not None:
|
|
611
|
-
return True
|
|
612
|
-
return match(self.path_with_protocol) is not None
|
|
613
|
-
|
|
614
371
|
def is_relative_to(self, *other) -> bool:
|
|
615
372
|
try:
|
|
616
373
|
self.relative_to(*other)
|
|
@@ -663,74 +420,295 @@ class URIPath(BaseURIPath):
|
|
|
663
420
|
other_path = self.from_path(start).path_with_protocol
|
|
664
421
|
path = self.path_with_protocol
|
|
665
422
|
|
|
666
|
-
if path.startswith(other_path):
|
|
667
|
-
relative = path[len(other_path) :]
|
|
668
|
-
relative = relative.lstrip("/")
|
|
669
|
-
return relative
|
|
423
|
+
if path.startswith(other_path):
|
|
424
|
+
relative = path[len(other_path) :]
|
|
425
|
+
relative = relative.lstrip("/")
|
|
426
|
+
return relative
|
|
427
|
+
|
|
428
|
+
raise ValueError("%r does not start with %r" % (path, other_path))
|
|
429
|
+
|
|
430
|
+
def is_absolute(self) -> bool:
|
|
431
|
+
return True
|
|
432
|
+
|
|
433
|
+
def is_mount(self) -> bool:
|
|
434
|
+
"""Test whether a path is a mount point
|
|
435
|
+
|
|
436
|
+
:returns: True if a path is a mount point, else False
|
|
437
|
+
"""
|
|
438
|
+
return False
|
|
439
|
+
|
|
440
|
+
def is_socket(self) -> bool:
|
|
441
|
+
"""
|
|
442
|
+
Return True if the path points to a Unix socket (or a symbolic link pointing
|
|
443
|
+
to a Unix socket), False if it points to another kind of file.
|
|
444
|
+
|
|
445
|
+
False is also returned if the path doesn’t exist or is a broken symlink;
|
|
446
|
+
other errors (such as permission errors) are propagated.
|
|
447
|
+
"""
|
|
448
|
+
return False
|
|
449
|
+
|
|
450
|
+
def is_fifo(self) -> bool:
|
|
451
|
+
"""
|
|
452
|
+
Return True if the path points to a FIFO (or a symbolic link pointing to a
|
|
453
|
+
FIFO), False if it points to another kind of file.
|
|
454
|
+
|
|
455
|
+
False is also returned if the path doesn’t exist or is a broken symlink;
|
|
456
|
+
other errors (such as permission errors) are propagated.
|
|
457
|
+
"""
|
|
458
|
+
return False
|
|
459
|
+
|
|
460
|
+
def is_block_device(self) -> bool:
|
|
461
|
+
"""
|
|
462
|
+
Return True if the path points to a block device (or a symbolic link pointing
|
|
463
|
+
to a block device), False if it points to another kind of file.
|
|
464
|
+
|
|
465
|
+
False is also returned if the path doesn’t exist or is a broken symlink;
|
|
466
|
+
other errors (such as permission errors) are propagated.
|
|
467
|
+
"""
|
|
468
|
+
return False
|
|
469
|
+
|
|
470
|
+
def is_char_device(self) -> bool:
|
|
471
|
+
"""
|
|
472
|
+
Return True if the path points to a character device (or a symbolic link
|
|
473
|
+
pointing to a character device), False if it points to another kind of file.
|
|
474
|
+
|
|
475
|
+
False is also returned if the path doesn’t exist or is a broken symlink;
|
|
476
|
+
other errors (such as permission errors) are propagated.
|
|
477
|
+
"""
|
|
478
|
+
return False
|
|
479
|
+
|
|
480
|
+
def abspath(self) -> str:
|
|
481
|
+
"""Return a normalized absolute version of the path."""
|
|
482
|
+
return self.path_with_protocol
|
|
483
|
+
|
|
484
|
+
def realpath(self) -> str:
|
|
485
|
+
"""Return the canonical path of the path."""
|
|
486
|
+
return self.path_with_protocol
|
|
487
|
+
|
|
488
|
+
def resolve(self):
|
|
489
|
+
"""Alias of realpath."""
|
|
490
|
+
return self.path_with_protocol
|
|
491
|
+
|
|
492
|
+
def read_bytes(self) -> bytes:
|
|
493
|
+
"""Return the binary contents of the pointed-to file as a bytes object"""
|
|
494
|
+
with self.open(mode="rb") as f:
|
|
495
|
+
return f.read() # pytype: disable=bad-return-type
|
|
496
|
+
|
|
497
|
+
def read_text(self) -> str:
|
|
498
|
+
"""Return the decoded contents of the pointed-to file as a string"""
|
|
499
|
+
with self.open(mode="r") as f:
|
|
500
|
+
return f.read() # pytype: disable=bad-return-type
|
|
501
|
+
|
|
502
|
+
def rglob(self: Self, pattern) -> List[Self]:
|
|
503
|
+
"""
|
|
504
|
+
This is like calling Path.glob() with “**/” added in front of
|
|
505
|
+
the given relative pattern
|
|
506
|
+
"""
|
|
507
|
+
if not pattern:
|
|
508
|
+
pattern = ""
|
|
509
|
+
pattern = "**/" + pattern.lstrip("/")
|
|
510
|
+
return self.glob(pattern=pattern)
|
|
511
|
+
|
|
512
|
+
def samefile(self, other_path) -> bool:
|
|
513
|
+
"""
|
|
514
|
+
Return whether this path points to the same file
|
|
515
|
+
"""
|
|
516
|
+
if hasattr(other_path, "protocol"):
|
|
517
|
+
if other_path.protocol != self.protocol:
|
|
518
|
+
return False
|
|
519
|
+
|
|
520
|
+
stat = self.stat()
|
|
521
|
+
if hasattr(other_path, "stat"):
|
|
522
|
+
other_path_stat = other_path.stat()
|
|
523
|
+
else:
|
|
524
|
+
other_path_stat = self.from_path(other_path).stat()
|
|
525
|
+
|
|
526
|
+
return (
|
|
527
|
+
stat.st_ino == other_path_stat.st_ino
|
|
528
|
+
and stat.st_dev == other_path_stat.st_dev
|
|
529
|
+
)
|
|
530
|
+
|
|
531
|
+
def touch(self):
|
|
532
|
+
with self.open("w"):
|
|
533
|
+
pass
|
|
534
|
+
|
|
535
|
+
def makedirs(self, exist_ok: bool = False) -> None:
|
|
536
|
+
"""
|
|
537
|
+
Recursive directory creation function. Like mkdir(), but makes all
|
|
538
|
+
intermediate-level directories needed to contain the leaf directory.
|
|
539
|
+
"""
|
|
540
|
+
self.mkdir(parents=True, exist_ok=exist_ok)
|
|
541
|
+
|
|
542
|
+
def write_bytes(self, data: bytes):
|
|
543
|
+
"""
|
|
544
|
+
Open the file pointed to in bytes mode, write data to it, and close the file
|
|
545
|
+
"""
|
|
546
|
+
with self.open(mode="wb") as f:
|
|
547
|
+
return f.write(data)
|
|
548
|
+
|
|
549
|
+
def write_text(self, data: str, encoding=None, errors=None, newline=None):
|
|
550
|
+
"""
|
|
551
|
+
Open the file pointed to in text mode, write data to it, and close the file.
|
|
552
|
+
The optional parameters have the same meaning as in open().
|
|
553
|
+
"""
|
|
554
|
+
with self.open(
|
|
555
|
+
mode="w", encoding=encoding, errors=errors, newline=newline
|
|
556
|
+
) as f:
|
|
557
|
+
return f.write(data)
|
|
558
|
+
|
|
559
|
+
@classproperty
|
|
560
|
+
def drive(self) -> str:
|
|
561
|
+
return ""
|
|
562
|
+
|
|
563
|
+
@classproperty
|
|
564
|
+
def root(self) -> str:
|
|
565
|
+
return self.protocol + "://"
|
|
566
|
+
|
|
567
|
+
@classproperty
|
|
568
|
+
def anchor(self) -> str:
|
|
569
|
+
return self.root # pyre-ignore[7]
|
|
570
|
+
|
|
571
|
+
def joinpath(self: Self, *other_paths: "PathLike") -> Self:
|
|
572
|
+
"""
|
|
573
|
+
Calling this method is equivalent to combining the path
|
|
574
|
+
with each of the other arguments in turn
|
|
575
|
+
"""
|
|
576
|
+
raise NotImplementedError('method "joinpath" not implemented: %r' % self)
|
|
577
|
+
|
|
578
|
+
@cached_property
|
|
579
|
+
def parts(self) -> Tuple[str, ...]:
|
|
580
|
+
"""A tuple giving access to the path’s various components"""
|
|
581
|
+
raise NotImplementedError('property "parts" not implemented: %r' % self)
|
|
582
|
+
|
|
583
|
+
@cached_property
|
|
584
|
+
def parents(self) -> "URIPathParents":
|
|
585
|
+
"""
|
|
586
|
+
An immutable sequence providing access to the logical ancestors of the path
|
|
587
|
+
"""
|
|
588
|
+
raise NotImplementedError('property "parents" not implemented: %r' % self)
|
|
589
|
+
|
|
590
|
+
@cached_property
|
|
591
|
+
def parent(self: Self) -> Self:
|
|
592
|
+
"""The logical parent of the path"""
|
|
593
|
+
raise NotImplementedError('property "parent" not implemented: %r' % self)
|
|
594
|
+
|
|
595
|
+
def is_dir(self, followlinks: bool = False) -> bool:
|
|
596
|
+
"""Return True if the path points to a directory."""
|
|
597
|
+
raise NotImplementedError('method "is_dir" not implemented: %r' % self)
|
|
598
|
+
|
|
599
|
+
def is_file(self, followlinks: bool = False) -> bool:
|
|
600
|
+
"""Return True if the path points to a regular file."""
|
|
601
|
+
raise NotImplementedError('method "is_file" not implemented: %r' % self)
|
|
602
|
+
|
|
603
|
+
def is_symlink(self) -> bool:
|
|
604
|
+
return False
|
|
605
|
+
|
|
606
|
+
def access(self, mode: Access) -> bool:
|
|
607
|
+
"""Return True if the path has access permission described by mode."""
|
|
608
|
+
raise NotImplementedError('method "access" not implemented: %r' % self)
|
|
609
|
+
|
|
610
|
+
def exists(self, followlinks: bool = False) -> bool:
|
|
611
|
+
"""Whether the path points to an existing file or directory."""
|
|
612
|
+
raise NotImplementedError('method "exists" not implemented: %r' % self)
|
|
613
|
+
|
|
614
|
+
def listdir(self) -> List[str]:
|
|
615
|
+
"""Return the names of the entries in the directory the path points to."""
|
|
616
|
+
raise NotImplementedError('method "listdir" not implemented: %r' % self)
|
|
617
|
+
|
|
618
|
+
def scandir(self) -> Iterator[FileEntry]:
|
|
619
|
+
"""
|
|
620
|
+
Return an iterator of FileEntry objects corresponding to the entries
|
|
621
|
+
in the directory.
|
|
622
|
+
"""
|
|
623
|
+
raise NotImplementedError('method "scandir" not implemented: %r' % self)
|
|
670
624
|
|
|
671
|
-
|
|
625
|
+
def getsize(self, follow_symlinks: bool = True) -> int:
|
|
626
|
+
"""Return the size, in bytes."""
|
|
627
|
+
raise NotImplementedError('method "getsize" not implemented: %r' % self)
|
|
672
628
|
|
|
673
|
-
def
|
|
674
|
-
|
|
629
|
+
def getmtime(self, follow_symlinks: bool = True) -> float:
|
|
630
|
+
"""Return the time of last modification."""
|
|
631
|
+
raise NotImplementedError('method "getmtime" not implemented: %r' % self)
|
|
675
632
|
|
|
676
|
-
def
|
|
677
|
-
"""
|
|
633
|
+
def stat(self, follow_symlinks=True) -> StatResult:
|
|
634
|
+
"""Get the status of the path."""
|
|
635
|
+
raise NotImplementedError('method "stat" not implemented: %r' % self)
|
|
678
636
|
|
|
679
|
-
|
|
637
|
+
def lstat(self) -> StatResult:
|
|
680
638
|
"""
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
def is_socket(self) -> bool:
|
|
639
|
+
Like stat() but, if the path points to a symbolic link,
|
|
640
|
+
return the symbolic link’s information rather than its target’s.
|
|
684
641
|
"""
|
|
685
|
-
|
|
686
|
-
to a Unix socket), False if it points to another kind of file.
|
|
642
|
+
return self.stat(follow_symlinks=False)
|
|
687
643
|
|
|
688
|
-
|
|
689
|
-
other errors (such as permission errors) are propagated.
|
|
644
|
+
def match(self, pattern) -> bool:
|
|
690
645
|
"""
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
def is_fifo(self) -> bool:
|
|
646
|
+
Match this path against the provided glob-style pattern.
|
|
647
|
+
Return True if matching is successful, False otherwise
|
|
694
648
|
"""
|
|
695
|
-
|
|
696
|
-
FIFO), False if it points to another kind of file.
|
|
649
|
+
raise NotImplementedError('method "match" not implemented: %r' % self)
|
|
697
650
|
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
""
|
|
701
|
-
return False
|
|
651
|
+
def remove(self, missing_ok: bool = False) -> None:
|
|
652
|
+
"""Remove (delete) the file."""
|
|
653
|
+
raise NotImplementedError('method "remove" not implemented: %r' % self)
|
|
702
654
|
|
|
703
|
-
def
|
|
704
|
-
"""
|
|
705
|
-
|
|
706
|
-
to a block device), False if it points to another kind of file.
|
|
655
|
+
def unlink(self, missing_ok: bool = False) -> None:
|
|
656
|
+
"""Remove (delete) the file."""
|
|
657
|
+
raise NotImplementedError('method "unlink" not implemented: %r' % self)
|
|
707
658
|
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
""
|
|
711
|
-
return False
|
|
659
|
+
def mkdir(self, mode=0o777, parents: bool = False, exist_ok: bool = False) -> None:
|
|
660
|
+
"""Create a directory."""
|
|
661
|
+
raise NotImplementedError('method "mkdir" not implemented: %r' % self)
|
|
712
662
|
|
|
713
|
-
def
|
|
714
|
-
"""
|
|
715
|
-
|
|
716
|
-
pointing to a character device), False if it points to another kind of file.
|
|
663
|
+
def rmdir(self) -> None:
|
|
664
|
+
"""Remove (delete) the directory."""
|
|
665
|
+
raise NotImplementedError('method "rmdir" not implemented: %r' % self)
|
|
717
666
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
""
|
|
721
|
-
return False
|
|
667
|
+
def open(self, mode: str = "r", **kwargs) -> IO:
|
|
668
|
+
"""Open the file with mode."""
|
|
669
|
+
raise NotImplementedError('method "open" not implemented: %r' % self)
|
|
722
670
|
|
|
723
|
-
def
|
|
724
|
-
|
|
725
|
-
|
|
671
|
+
def walk(
|
|
672
|
+
self, followlinks: bool = False
|
|
673
|
+
) -> Iterator[Tuple[str, List[str], List[str]]]:
|
|
674
|
+
"""Generate the file names in a directory tree by walking the tree."""
|
|
675
|
+
raise NotImplementedError('method "walk" not implemented: %r' % self)
|
|
726
676
|
|
|
727
|
-
def
|
|
728
|
-
"""
|
|
729
|
-
|
|
677
|
+
def scan(self, missing_ok: bool = True, followlinks: bool = False) -> Iterator[str]:
|
|
678
|
+
"""Iterate through the files in the directory."""
|
|
679
|
+
raise NotImplementedError('method "scan" not implemented: %r' % self)
|
|
730
680
|
|
|
731
|
-
def
|
|
732
|
-
|
|
733
|
-
|
|
681
|
+
def scan_stat(
|
|
682
|
+
self, missing_ok: bool = True, followlinks: bool = False
|
|
683
|
+
) -> Iterator[FileEntry]:
|
|
684
|
+
"""Iterate through the files in the directory, with file stat."""
|
|
685
|
+
raise NotImplementedError('method "scan_stat" not implemented: %r' % self)
|
|
686
|
+
|
|
687
|
+
def glob(
|
|
688
|
+
self: Self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
689
|
+
) -> List[Self]:
|
|
690
|
+
"""Return files whose paths match the glob pattern."""
|
|
691
|
+
raise NotImplementedError('method "glob" not implemented: %r' % self)
|
|
692
|
+
|
|
693
|
+
def iglob(
|
|
694
|
+
self: Self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
695
|
+
) -> Iterator[Self]:
|
|
696
|
+
"""Return an iterator of files whose paths match the glob pattern."""
|
|
697
|
+
raise NotImplementedError('method "iglob" not implemented: %r' % self)
|
|
698
|
+
|
|
699
|
+
def glob_stat(
|
|
700
|
+
self, pattern: str, recursive: bool = True, missing_ok: bool = True
|
|
701
|
+
) -> Iterator[FileEntry]:
|
|
702
|
+
"""Return an iterator of files with stat whose paths match the glob pattern."""
|
|
703
|
+
raise NotImplementedError('method "glob_stat" not implemented: %r' % self)
|
|
704
|
+
|
|
705
|
+
def load(self) -> BinaryIO:
|
|
706
|
+
"""Read all content in binary."""
|
|
707
|
+
raise NotImplementedError('method "load" not implemented: %r' % self)
|
|
708
|
+
|
|
709
|
+
def save(self, file_object: BinaryIO):
|
|
710
|
+
"""Write the opened binary stream to the path."""
|
|
711
|
+
raise NotImplementedError('method "save" not implemented: %r' % self)
|
|
734
712
|
|
|
735
713
|
def chmod(self, mode: int, *, follow_symlinks: bool = True):
|
|
736
714
|
raise NotImplementedError(f"'chmod' is unsupported on '{type(self)}'")
|
|
@@ -742,17 +720,7 @@ class URIPath(BaseURIPath):
|
|
|
742
720
|
"""
|
|
743
721
|
return self.chmod(mode=mode, follow_symlinks=False)
|
|
744
722
|
|
|
745
|
-
def
|
|
746
|
-
"""Return the binary contents of the pointed-to file as a bytes object"""
|
|
747
|
-
with self.open(mode="rb") as f:
|
|
748
|
-
return f.read() # pytype: disable=bad-return-type
|
|
749
|
-
|
|
750
|
-
def read_text(self) -> str:
|
|
751
|
-
"""Return the decoded contents of the pointed-to file as a string"""
|
|
752
|
-
with self.open(mode="r") as f:
|
|
753
|
-
return f.read() # pytype: disable=bad-return-type
|
|
754
|
-
|
|
755
|
-
def rename(self: Self, dst_path: PathLike, overwrite: bool = True) -> Self:
|
|
723
|
+
def rename(self: Self, dst_path: "PathLike", overwrite: bool = True) -> Self:
|
|
756
724
|
"""
|
|
757
725
|
rename file
|
|
758
726
|
|
|
@@ -761,7 +729,7 @@ class URIPath(BaseURIPath):
|
|
|
761
729
|
"""
|
|
762
730
|
raise NotImplementedError(f"'rename' is unsupported on '{type(self)}'")
|
|
763
731
|
|
|
764
|
-
def replace(self: Self, dst_path: PathLike, overwrite: bool = True) -> Self:
|
|
732
|
+
def replace(self: Self, dst_path: "PathLike", overwrite: bool = True) -> Self:
|
|
765
733
|
"""
|
|
766
734
|
move file
|
|
767
735
|
|
|
@@ -770,39 +738,10 @@ class URIPath(BaseURIPath):
|
|
|
770
738
|
"""
|
|
771
739
|
return self.rename(dst_path=dst_path, overwrite=overwrite)
|
|
772
740
|
|
|
773
|
-
def rglob(self: Self, pattern) -> List[Self]:
|
|
774
|
-
"""
|
|
775
|
-
This is like calling Path.glob() with “**/” added in front of
|
|
776
|
-
the given relative pattern
|
|
777
|
-
"""
|
|
778
|
-
if not pattern:
|
|
779
|
-
pattern = ""
|
|
780
|
-
pattern = "**/" + pattern.lstrip("/")
|
|
781
|
-
return self.glob(pattern=pattern)
|
|
782
|
-
|
|
783
741
|
def md5(self, recalculate: bool = False, followlinks: bool = False) -> str:
|
|
784
742
|
raise NotImplementedError(f"'md5' is unsupported on '{type(self)}'")
|
|
785
743
|
|
|
786
|
-
def
|
|
787
|
-
"""
|
|
788
|
-
Return whether this path points to the same file
|
|
789
|
-
"""
|
|
790
|
-
if hasattr(other_path, "protocol"):
|
|
791
|
-
if other_path.protocol != self.protocol:
|
|
792
|
-
return False
|
|
793
|
-
|
|
794
|
-
stat = self.stat()
|
|
795
|
-
if hasattr(other_path, "stat"):
|
|
796
|
-
other_path_stat = other_path.stat()
|
|
797
|
-
else:
|
|
798
|
-
other_path_stat = self.from_path(other_path).stat()
|
|
799
|
-
|
|
800
|
-
return (
|
|
801
|
-
stat.st_ino == other_path_stat.st_ino
|
|
802
|
-
and stat.st_dev == other_path_stat.st_dev
|
|
803
|
-
)
|
|
804
|
-
|
|
805
|
-
def symlink(self, dst_path: PathLike) -> None:
|
|
744
|
+
def symlink(self, dst_path: "PathLike") -> None:
|
|
806
745
|
raise NotImplementedError(f"'symlink' is unsupported on '{type(self)}'")
|
|
807
746
|
|
|
808
747
|
def symlink_to(self, target, target_is_directory=False):
|
|
@@ -819,23 +758,6 @@ class URIPath(BaseURIPath):
|
|
|
819
758
|
"""
|
|
820
759
|
raise NotImplementedError(f"'hardlink_to' is unsupported on '{type(self)}'")
|
|
821
760
|
|
|
822
|
-
def write_bytes(self, data: bytes):
|
|
823
|
-
"""
|
|
824
|
-
Open the file pointed to in bytes mode, write data to it, and close the file
|
|
825
|
-
"""
|
|
826
|
-
with self.open(mode="wb") as f:
|
|
827
|
-
return f.write(data)
|
|
828
|
-
|
|
829
|
-
def write_text(self, data: str, encoding=None, errors=None, newline=None):
|
|
830
|
-
"""
|
|
831
|
-
Open the file pointed to in text mode, write data to it, and close the file.
|
|
832
|
-
The optional parameters have the same meaning as in open().
|
|
833
|
-
"""
|
|
834
|
-
with self.open(
|
|
835
|
-
mode="w", encoding=encoding, errors=errors, newline=newline
|
|
836
|
-
) as f:
|
|
837
|
-
return f.write(data)
|
|
838
|
-
|
|
839
761
|
def home(self):
|
|
840
762
|
"""Return the home directory
|
|
841
763
|
|
|
@@ -901,12 +823,86 @@ class URIPath(BaseURIPath):
|
|
|
901
823
|
"""
|
|
902
824
|
raise NotImplementedError(f"'utime' is unsupported on '{type(self)}'")
|
|
903
825
|
|
|
904
|
-
|
|
826
|
+
|
|
827
|
+
PathLike = Union[str, BasePath, _PathLike]
|
|
828
|
+
|
|
829
|
+
|
|
830
|
+
class URIPath(BasePath):
|
|
831
|
+
def __init__(self, path: PathLike, *other_paths: PathLike):
|
|
832
|
+
# if not isinstance(path, PathLike):
|
|
833
|
+
# raise TypeError(f"expected PathLike object, not {type(path)}")
|
|
834
|
+
|
|
835
|
+
if len(other_paths) > 0:
|
|
836
|
+
path = self.from_path(path).joinpath(*other_paths)
|
|
837
|
+
super().__init__(path)
|
|
838
|
+
|
|
839
|
+
def __truediv__(self: Self, other_path: PathLike) -> Self:
|
|
840
|
+
if isinstance(other_path, BasePath):
|
|
841
|
+
if self.protocol != other_path.protocol:
|
|
842
|
+
raise TypeError(
|
|
843
|
+
"'/' not supported between instances of %r and %r"
|
|
844
|
+
% (type(self), type(other_path))
|
|
845
|
+
)
|
|
846
|
+
elif isinstance(other_path, _PathLike):
|
|
847
|
+
other_path = fspath(other_path)
|
|
848
|
+
elif not isinstance(other_path, str):
|
|
849
|
+
raise TypeError("%r is not 'PathLike' object" % other_path)
|
|
850
|
+
return self.joinpath(other_path)
|
|
851
|
+
|
|
852
|
+
def joinpath(self: Self, *other_paths: PathLike) -> Self:
|
|
905
853
|
"""
|
|
906
|
-
|
|
907
|
-
|
|
854
|
+
Calling this method is equivalent to combining the path
|
|
855
|
+
with each of the other arguments in turn
|
|
908
856
|
"""
|
|
909
|
-
return self.
|
|
857
|
+
return self.from_path(uri_join(str(self), *map(str, other_paths)))
|
|
858
|
+
|
|
859
|
+
@cached_property
|
|
860
|
+
def parts(self) -> Tuple[str, ...]:
|
|
861
|
+
"""A tuple giving access to the path’s various components"""
|
|
862
|
+
parts = [self.root]
|
|
863
|
+
path = self.path_without_protocol
|
|
864
|
+
path = path.lstrip("/")
|
|
865
|
+
if path != "":
|
|
866
|
+
parts.extend(path.split("/"))
|
|
867
|
+
return tuple(parts) # pyre-ignore[7]
|
|
868
|
+
|
|
869
|
+
@cached_property
|
|
870
|
+
def parents(self) -> "URIPathParents":
|
|
871
|
+
"""
|
|
872
|
+
An immutable sequence providing access to the logical ancestors of the path
|
|
873
|
+
"""
|
|
874
|
+
return URIPathParents(self)
|
|
875
|
+
|
|
876
|
+
@cached_property
|
|
877
|
+
def parent(self: Self) -> Self:
|
|
878
|
+
"""The logical parent of the path"""
|
|
879
|
+
if self.path_without_protocol == "/":
|
|
880
|
+
return self
|
|
881
|
+
elif len(self.parents) > 0:
|
|
882
|
+
return self.parents[0]
|
|
883
|
+
return self.from_path("")
|
|
884
|
+
|
|
885
|
+
@cached_property
|
|
886
|
+
def name(self) -> str:
|
|
887
|
+
"""
|
|
888
|
+
A string representing the final path component, excluding the drive and root
|
|
889
|
+
"""
|
|
890
|
+
parts = self.parts
|
|
891
|
+
if len(parts) == 1 and parts[0] == self.protocol + "://":
|
|
892
|
+
return ""
|
|
893
|
+
return parts[-1]
|
|
894
|
+
|
|
895
|
+
def match(self, pattern) -> bool:
|
|
896
|
+
"""
|
|
897
|
+
Match this path against the provided glob-style pattern.
|
|
898
|
+
Return True if matching is successful, False otherwise
|
|
899
|
+
"""
|
|
900
|
+
match = _compile_pattern(pattern)
|
|
901
|
+
for index in range(len(self.parts), 0, -1):
|
|
902
|
+
path = "/".join(self.parts[index:])
|
|
903
|
+
if match(path) is not None:
|
|
904
|
+
return True
|
|
905
|
+
return match(self.path_with_protocol) is not None
|
|
910
906
|
|
|
911
907
|
|
|
912
908
|
class URIPathParents(Sequence):
|