pathlibutil 0.1.7__tar.gz → 0.1.9__tar.gz
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.
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/PKG-INFO +42 -1
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/README.md +41 -0
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/pathlibutil/path.py +138 -5
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/pathlibutil/types.py +20 -1
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/pyproject.toml +2 -1
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/LICENSE +0 -0
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/pathlibutil/__init__.py +0 -0
- {pathlibutil-0.1.7 → pathlibutil-0.1.9}/pathlibutil/base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pathlibutil
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: inherits from pathlib.Path with methods for hashing, copying, deleting and more
|
|
5
5
|
Home-page: https://d-chris.github.io
|
|
6
6
|
License: MIT
|
|
@@ -51,6 +51,10 @@ Description-Content-Type: text/markdown
|
|
|
51
51
|
- `Path.stat()` returns a `StatResult` object to get file or directory information containing
|
|
52
52
|
- `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`
|
|
53
53
|
- `ByteInt` object for `size`
|
|
54
|
+
- `Path.relative_to()` to get relative path from a file or directory, `walk_up` to walk up the directory tree.
|
|
55
|
+
- `Path.with_suffix()` to change the multiple suffixes of a file
|
|
56
|
+
- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`
|
|
57
|
+
- `Path.resolve()` to resolve a unc path to a mapped windows drive.
|
|
54
58
|
|
|
55
59
|
## Installation
|
|
56
60
|
|
|
@@ -260,3 +264,40 @@ backup = archive.move("./backup/")
|
|
|
260
264
|
print(f"archive created: {archive.name} and moved to: {backup.parent}")
|
|
261
265
|
```
|
|
262
266
|
|
|
267
|
+
## Example 6
|
|
268
|
+
|
|
269
|
+
Access the current working directory with optional parameter `frozen` to determine
|
|
270
|
+
different directories when script is bundled to an executable,
|
|
271
|
+
e.g. with `pyinstaller`.
|
|
272
|
+
> `Path.cwd()`
|
|
273
|
+
|
|
274
|
+
```cmd
|
|
275
|
+
>>> poetry run examples/example6.py -b
|
|
276
|
+
Building frozen: K:/pathlibutil/examples/example6.exe
|
|
277
|
+
Build succeeded: 0
|
|
278
|
+
|
|
279
|
+
>>> poetry run examples/example6.py
|
|
280
|
+
we are not frozen
|
|
281
|
+
|
|
282
|
+
bundle dir is K:/pathlibutil/examples
|
|
283
|
+
sys.argv[0] is K:/pathlibutil/examples/example6.py
|
|
284
|
+
sys.executable is K:/pathlibutil/.venv/Scripts/python.exe
|
|
285
|
+
os.getcwd is K:/pathlibutil
|
|
286
|
+
|
|
287
|
+
Path.cwd(frozen=True) is K:/pathlibutil
|
|
288
|
+
Path.cwd(frozen=False) is K:/pathlibutil
|
|
289
|
+
Path.cwd(frozen=_MEIPASS) is K:/pathlibutil
|
|
290
|
+
|
|
291
|
+
>>> examples/example6.exe
|
|
292
|
+
we are ever so frozen
|
|
293
|
+
|
|
294
|
+
bundle dir is C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
|
|
295
|
+
sys.argv[0] is examples/example6.exe
|
|
296
|
+
sys.executable is K:/pathlibutil/examples/example6.exe
|
|
297
|
+
os.getcwd is K:/pathlibutil
|
|
298
|
+
|
|
299
|
+
Path.cwd(frozen=True) is K:/pathlibutil/examples
|
|
300
|
+
Path.cwd(frozen=False) is K:/pathlibutil
|
|
301
|
+
Path.cwd(frozen=_MEIPASS) is C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
|
|
302
|
+
```
|
|
303
|
+
|
|
@@ -27,6 +27,10 @@
|
|
|
27
27
|
- `Path.stat()` returns a `StatResult` object to get file or directory information containing
|
|
28
28
|
- `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`
|
|
29
29
|
- `ByteInt` object for `size`
|
|
30
|
+
- `Path.relative_to()` to get relative path from a file or directory, `walk_up` to walk up the directory tree.
|
|
31
|
+
- `Path.with_suffix()` to change the multiple suffixes of a file
|
|
32
|
+
- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`
|
|
33
|
+
- `Path.resolve()` to resolve a unc path to a mapped windows drive.
|
|
30
34
|
|
|
31
35
|
## Installation
|
|
32
36
|
|
|
@@ -235,3 +239,40 @@ backup = archive.move("./backup/")
|
|
|
235
239
|
|
|
236
240
|
print(f"archive created: {archive.name} and moved to: {backup.parent}")
|
|
237
241
|
```
|
|
242
|
+
|
|
243
|
+
## Example 6
|
|
244
|
+
|
|
245
|
+
Access the current working directory with optional parameter `frozen` to determine
|
|
246
|
+
different directories when script is bundled to an executable,
|
|
247
|
+
e.g. with `pyinstaller`.
|
|
248
|
+
> `Path.cwd()`
|
|
249
|
+
|
|
250
|
+
```cmd
|
|
251
|
+
>>> poetry run examples/example6.py -b
|
|
252
|
+
Building frozen: K:/pathlibutil/examples/example6.exe
|
|
253
|
+
Build succeeded: 0
|
|
254
|
+
|
|
255
|
+
>>> poetry run examples/example6.py
|
|
256
|
+
we are not frozen
|
|
257
|
+
|
|
258
|
+
bundle dir is K:/pathlibutil/examples
|
|
259
|
+
sys.argv[0] is K:/pathlibutil/examples/example6.py
|
|
260
|
+
sys.executable is K:/pathlibutil/.venv/Scripts/python.exe
|
|
261
|
+
os.getcwd is K:/pathlibutil
|
|
262
|
+
|
|
263
|
+
Path.cwd(frozen=True) is K:/pathlibutil
|
|
264
|
+
Path.cwd(frozen=False) is K:/pathlibutil
|
|
265
|
+
Path.cwd(frozen=_MEIPASS) is K:/pathlibutil
|
|
266
|
+
|
|
267
|
+
>>> examples/example6.exe
|
|
268
|
+
we are ever so frozen
|
|
269
|
+
|
|
270
|
+
bundle dir is C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
|
|
271
|
+
sys.argv[0] is examples/example6.exe
|
|
272
|
+
sys.executable is K:/pathlibutil/examples/example6.exe
|
|
273
|
+
os.getcwd is K:/pathlibutil
|
|
274
|
+
|
|
275
|
+
Path.cwd(frozen=True) is K:/pathlibutil/examples
|
|
276
|
+
Path.cwd(frozen=False) is K:/pathlibutil
|
|
277
|
+
Path.cwd(frozen=_MEIPASS) is C:/Users/CHRIST~1.DOE/AppData/Local/Temp/_MEI106042
|
|
278
|
+
```
|
|
@@ -2,8 +2,11 @@ import errno
|
|
|
2
2
|
import hashlib
|
|
3
3
|
import itertools
|
|
4
4
|
import os
|
|
5
|
+
import re
|
|
5
6
|
import shutil
|
|
6
|
-
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import Callable, Dict, Generator, List, Literal, Set, Union
|
|
7
10
|
|
|
8
11
|
from pathlibutil.base import BasePath, _Path
|
|
9
12
|
from pathlibutil.types import ByteInt, StatResult, _stat_result, byteint
|
|
@@ -401,15 +404,15 @@ class Path(BasePath):
|
|
|
401
404
|
Return a new `Path` with changed suffix or remove it when its an empty
|
|
402
405
|
string.
|
|
403
406
|
|
|
407
|
+
Multiple suffixes can be changed at once by passing a list of suffixes.
|
|
408
|
+
With a empty list all suffixes will be removed.
|
|
409
|
+
|
|
404
410
|
>>> Path('test.a.b').with_suffix('.c')
|
|
405
411
|
Path('test.a.c')
|
|
406
412
|
|
|
407
413
|
>>> Path('test.a.b').with_suffix('')
|
|
408
414
|
Path('test.a')
|
|
409
415
|
|
|
410
|
-
Multiple suffixes can be changed at once by passing a list of suffixes.
|
|
411
|
-
With a empty list all suffixes will be removed.
|
|
412
|
-
|
|
413
416
|
>>> Path('test.a.b').with_suffix(['.c', '.d'])
|
|
414
417
|
Path('test.c.d')
|
|
415
418
|
|
|
@@ -432,7 +435,137 @@ class Path(BasePath):
|
|
|
432
435
|
end = -1 * len(self.suffixes) or None
|
|
433
436
|
name = self.name.split(".")[0:end]
|
|
434
437
|
stem = self.parent.joinpath("".join(name))
|
|
435
|
-
return super(
|
|
438
|
+
return super(self.__class__, stem).with_suffix(suffix)
|
|
439
|
+
|
|
440
|
+
def relative_to(
|
|
441
|
+
self, *other: Union[str, _Path], walk_up: Union[bool, int] = False
|
|
442
|
+
) -> _Path:
|
|
443
|
+
"""
|
|
444
|
+
Return the relative path to another path identified by the passed
|
|
445
|
+
arguments. If the operation is not possible (because this is not
|
|
446
|
+
related to the other path), raise `ValueError`.
|
|
447
|
+
|
|
448
|
+
The `walk_up` parameter controls whether `..` may be used to resolve
|
|
449
|
+
the path.
|
|
450
|
+
|
|
451
|
+
If `walk_up` is a integer it specifies the maximum number of `..` to resolve, if
|
|
452
|
+
max is reached a `ValueError` is raised.
|
|
453
|
+
|
|
454
|
+
>>> Path('a/b/c/d').relative_to('a/b')
|
|
455
|
+
Path('c/d')
|
|
456
|
+
|
|
457
|
+
>>> Path('a/b/c/d').relative_to('a/b/e/f/g', walk_up=True)
|
|
458
|
+
Path('../../../c/d')
|
|
459
|
+
|
|
460
|
+
>>> Path('a/b/c/d').relative_to('a/b/e/f/g', walk_up=2)
|
|
461
|
+
ValueError: '../../../c/d' is outside of the relative deepth of '2'
|
|
462
|
+
"""
|
|
463
|
+
if not walk_up:
|
|
464
|
+
return super().relative_to(*other)
|
|
465
|
+
|
|
466
|
+
try:
|
|
467
|
+
relative = super().relative_to(*other, walk_up=walk_up)
|
|
468
|
+
except TypeError:
|
|
469
|
+
relative = self.__class__(os.path.relpath(self, Path(*other)))
|
|
470
|
+
|
|
471
|
+
if type(walk_up) is not int:
|
|
472
|
+
return relative
|
|
473
|
+
|
|
474
|
+
if relative.parts.count("..") > walk_up:
|
|
475
|
+
raise ValueError(
|
|
476
|
+
f"'{relative}' is outside of the relative deepth of '{walk_up}'"
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
return relative
|
|
480
|
+
|
|
481
|
+
@classmethod
|
|
482
|
+
def cwd(cls, *, frozen: Literal[True, False, "_MEIPASS"] = False) -> _Path:
|
|
483
|
+
"""
|
|
484
|
+
Return a `Path` object representing the current working directory.
|
|
485
|
+
|
|
486
|
+
The `frozen` parameter takes only effect when the script is bundled to a
|
|
487
|
+
executable, e.g. with `pyinstaller`.
|
|
488
|
+
|
|
489
|
+
- `False`: Returns the current working directory, this is the default.
|
|
490
|
+
- `True`: Returns the directory of the executable.
|
|
491
|
+
- `"_MEIPASS"`: Returns the directory of the bundled resources.
|
|
492
|
+
"""
|
|
493
|
+
if getattr(sys, "frozen", False):
|
|
494
|
+
if frozen is True:
|
|
495
|
+
return cls(sys.executable).parent
|
|
496
|
+
elif isinstance(frozen, str):
|
|
497
|
+
return cls(getattr(sys, frozen))
|
|
498
|
+
|
|
499
|
+
return super().cwd()
|
|
500
|
+
|
|
501
|
+
@classmethod
|
|
502
|
+
def _net_use(cls) -> Dict[str, str]:
|
|
503
|
+
"""
|
|
504
|
+
Return a dictionary of mapped network drives. Keys are UNC paths and values
|
|
505
|
+
are drive letters.
|
|
506
|
+
"""
|
|
507
|
+
|
|
508
|
+
def run(cmd: str) -> str:
|
|
509
|
+
"""execute `command` and return stdout with cp850 encoding."""
|
|
510
|
+
result = subprocess.run(
|
|
511
|
+
cmd,
|
|
512
|
+
capture_output=True,
|
|
513
|
+
shell=True,
|
|
514
|
+
encoding="cp850",
|
|
515
|
+
)
|
|
516
|
+
|
|
517
|
+
return result.stdout
|
|
518
|
+
|
|
519
|
+
try:
|
|
520
|
+
mapped_drives = re.finditer(
|
|
521
|
+
r"^OK\s+(?P<drive>[A-Z]):\s+(?P<unc>\S+)",
|
|
522
|
+
run("net use"),
|
|
523
|
+
re.IGNORECASE | re.MULTILINE,
|
|
524
|
+
)
|
|
525
|
+
return {
|
|
526
|
+
match.group("unc") + "\\": match.group("drive") + ":\\"
|
|
527
|
+
for match in mapped_drives
|
|
528
|
+
}
|
|
529
|
+
except Exception:
|
|
530
|
+
return {}
|
|
531
|
+
|
|
532
|
+
def _resolve_unc(self) -> _Path:
|
|
533
|
+
"""
|
|
534
|
+
Resolve UNC paths to mapped network drives.
|
|
535
|
+
"""
|
|
536
|
+
if not hasattr(self.__class__, "_netuse"):
|
|
537
|
+
self.__class__._netuse = self._net_use()
|
|
538
|
+
|
|
539
|
+
try:
|
|
540
|
+
drive = self._netuse[self.anchor]
|
|
541
|
+
return self.__class__(drive).joinpath(self.relative_to(self.anchor))
|
|
542
|
+
except KeyError:
|
|
543
|
+
return self
|
|
544
|
+
|
|
545
|
+
def resolve(self, strict: bool = False, unc: bool = True) -> _Path:
|
|
546
|
+
"""
|
|
547
|
+
Make the path absolute, resolving all symlinks on the way and also normalizing
|
|
548
|
+
it.
|
|
549
|
+
|
|
550
|
+
If `strict` is `True`, a `FileNotFoundError` will be raised if the path does
|
|
551
|
+
not exist.
|
|
552
|
+
|
|
553
|
+
On Windows if `unc` is `False`, UNC paths will be resolved to mapped network
|
|
554
|
+
drives.
|
|
555
|
+
|
|
556
|
+
>>> Path("T:/file.txt").resolve()
|
|
557
|
+
Path("\\\\server\\temp\\file.txt")
|
|
558
|
+
|
|
559
|
+
>>> Path("//server/temp/file.txt").resolve(unc=False)
|
|
560
|
+
Path("T:\\file.txt")
|
|
561
|
+
"""
|
|
562
|
+
|
|
563
|
+
p = super().resolve(strict)
|
|
564
|
+
|
|
565
|
+
if unc is True or os.name != "nt":
|
|
566
|
+
return p
|
|
567
|
+
|
|
568
|
+
return p._resolve_unc()
|
|
436
569
|
|
|
437
570
|
|
|
438
571
|
class Register7zFormat(Path, archive="7z"):
|
|
@@ -2,7 +2,7 @@ import functools
|
|
|
2
2
|
import os
|
|
3
3
|
import re
|
|
4
4
|
from datetime import datetime, tzinfo
|
|
5
|
-
from typing import Set, Tuple, TypeVar
|
|
5
|
+
from typing import Set, Tuple, TypeVar, Iterable
|
|
6
6
|
|
|
7
7
|
_ByteInt = TypeVar("_ByteInt", bound="ByteInt")
|
|
8
8
|
_stat_result = TypeVar("_stat_result", bound="os.stat_result")
|
|
@@ -332,3 +332,22 @@ class StatResult:
|
|
|
332
332
|
Return representation of `os.stat_result` object.
|
|
333
333
|
"""
|
|
334
334
|
return repr(self._obj)
|
|
335
|
+
|
|
336
|
+
def __dir__(self) -> Iterable[str]:
|
|
337
|
+
"""
|
|
338
|
+
Return a list of attributes of `os.stat_result` object.
|
|
339
|
+
"""
|
|
340
|
+
return dir(self._obj)
|
|
341
|
+
|
|
342
|
+
def __len__(self) -> int:
|
|
343
|
+
"""
|
|
344
|
+
Return length of `os.stat_result` object.
|
|
345
|
+
"""
|
|
346
|
+
return len(self._obj)
|
|
347
|
+
|
|
348
|
+
@property
|
|
349
|
+
def stat_result(self) -> os.stat_result:
|
|
350
|
+
"""
|
|
351
|
+
Return the wrapped `os.stat_result` object.
|
|
352
|
+
"""
|
|
353
|
+
return self._obj
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "pathlibutil"
|
|
3
|
-
version = "v0.1.
|
|
3
|
+
version = "v0.1.9"
|
|
4
4
|
description = "inherits from pathlib.Path with methods for hashing, copying, deleting and more"
|
|
5
5
|
authors = ["Christoph Dörrer <d-chris@web.de>"]
|
|
6
6
|
readme = "README.md"
|
|
@@ -44,6 +44,7 @@ docformatter = "^1.7.5"
|
|
|
44
44
|
jinja2-pdoc = "^0.2.0"
|
|
45
45
|
pdoc = "^14.3.0"
|
|
46
46
|
click = "^8.1.7"
|
|
47
|
+
pyinstaller = "^6.5.0"
|
|
47
48
|
|
|
48
49
|
[[tool.poetry.source]]
|
|
49
50
|
name = "PyPI"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|