pathlibutil 0.1.9__tar.gz → 0.2.0__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.9 → pathlibutil-0.2.0}/PKG-INFO +11 -3
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/README.md +10 -2
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/pathlibutil/__init__.py +1 -1
- pathlibutil-0.2.0/pathlibutil/json.py +25 -0
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/pathlibutil/path.py +83 -18
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/pathlibutil/types.py +2 -1
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/pyproject.toml +26 -23
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/LICENSE +0 -0
- {pathlibutil-0.1.9 → pathlibutil-0.2.0}/pathlibutil/base.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pathlibutil
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.2.0
|
|
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
|
|
@@ -32,6 +32,7 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
[](https://d-chris.github.io/pathlibutil)
|
|
33
33
|
[](https://github.com/d-chris/pathlibutil)
|
|
34
34
|
[](https://d-chris.github.io/pathlibutil/htmlcov)
|
|
35
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
35
36
|
|
|
36
37
|
---
|
|
37
38
|
|
|
@@ -55,6 +56,12 @@ Description-Content-Type: text/markdown
|
|
|
55
56
|
- `Path.with_suffix()` to change the multiple suffixes of a file
|
|
56
57
|
- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`
|
|
57
58
|
- `Path.resolve()` to resolve a unc path to a mapped windows drive.
|
|
59
|
+
- `Path.walk()` to walk over a directory tree like `os.walk()`
|
|
60
|
+
- `Path.iterdir()` with `recursive` all files from the directory tree will be yielded and `exclude_dirs` via callable.
|
|
61
|
+
|
|
62
|
+
JSON serialization of `Path` objects is supported in `pathlibutil.json`.
|
|
63
|
+
|
|
64
|
+
- `pathlibutil.json.dumps()` and `pathlibutil.json.dump()` to serialize `Path` objects as posix paths.
|
|
58
65
|
|
|
59
66
|
## Installation
|
|
60
67
|
|
|
@@ -176,7 +183,7 @@ deleted directories and the amount of memory freed in MB.
|
|
|
176
183
|
> `Path.delete()`, `Path.size()` and `ByteInt`
|
|
177
184
|
|
|
178
185
|
```python
|
|
179
|
-
from pathlibutil import
|
|
186
|
+
from pathlibutil import ByteInt, Path
|
|
180
187
|
|
|
181
188
|
mem = ByteInt(0)
|
|
182
189
|
i = 0
|
|
@@ -203,9 +210,10 @@ to register new archive formats.
|
|
|
203
210
|
> Path.make_archive(), Path.archive_formats and Path.move()
|
|
204
211
|
|
|
205
212
|
```python
|
|
206
|
-
import pathlibutil
|
|
207
213
|
import shutil
|
|
208
214
|
|
|
215
|
+
import pathlibutil
|
|
216
|
+
|
|
209
217
|
|
|
210
218
|
class RegisterFooBarFormat(pathlibutil.Path, archive="foobar"):
|
|
211
219
|
@classmethod
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
[](https://d-chris.github.io/pathlibutil)
|
|
9
9
|
[](https://github.com/d-chris/pathlibutil)
|
|
10
10
|
[](https://d-chris.github.io/pathlibutil/htmlcov)
|
|
11
|
+
[](https://github.com/pre-commit/pre-commit)
|
|
11
12
|
|
|
12
13
|
---
|
|
13
14
|
|
|
@@ -31,6 +32,12 @@
|
|
|
31
32
|
- `Path.with_suffix()` to change the multiple suffixes of a file
|
|
32
33
|
- `Path.cwd()` to get the current working directory or executable path when script is bundled, e.g. with `pyinstaller`
|
|
33
34
|
- `Path.resolve()` to resolve a unc path to a mapped windows drive.
|
|
35
|
+
- `Path.walk()` to walk over a directory tree like `os.walk()`
|
|
36
|
+
- `Path.iterdir()` with `recursive` all files from the directory tree will be yielded and `exclude_dirs` via callable.
|
|
37
|
+
|
|
38
|
+
JSON serialization of `Path` objects is supported in `pathlibutil.json`.
|
|
39
|
+
|
|
40
|
+
- `pathlibutil.json.dumps()` and `pathlibutil.json.dump()` to serialize `Path` objects as posix paths.
|
|
34
41
|
|
|
35
42
|
## Installation
|
|
36
43
|
|
|
@@ -152,7 +159,7 @@ deleted directories and the amount of memory freed in MB.
|
|
|
152
159
|
> `Path.delete()`, `Path.size()` and `ByteInt`
|
|
153
160
|
|
|
154
161
|
```python
|
|
155
|
-
from pathlibutil import
|
|
162
|
+
from pathlibutil import ByteInt, Path
|
|
156
163
|
|
|
157
164
|
mem = ByteInt(0)
|
|
158
165
|
i = 0
|
|
@@ -179,9 +186,10 @@ to register new archive formats.
|
|
|
179
186
|
> Path.make_archive(), Path.archive_formats and Path.move()
|
|
180
187
|
|
|
181
188
|
```python
|
|
182
|
-
import pathlibutil
|
|
183
189
|
import shutil
|
|
184
190
|
|
|
191
|
+
import pathlibutil
|
|
192
|
+
|
|
185
193
|
|
|
186
194
|
class RegisterFooBarFormat(pathlibutil.Path, archive="foobar"):
|
|
187
195
|
@classmethod
|
|
@@ -3,6 +3,6 @@
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from pathlibutil.path import Path, Register7zFormat
|
|
6
|
-
from pathlibutil.types import ByteInt, TimeInt, byteint
|
|
6
|
+
from pathlibutil.types import ByteInt, StatResult, TimeInt, byteint
|
|
7
7
|
|
|
8
8
|
__all__ = ["Path", "Register7zFormat", "ByteInt", "byteint", "TimeInt", "StatResult"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import importlib
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
json = importlib.import_module("json")
|
|
6
|
+
|
|
7
|
+
from json import load, loads
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PathEncoder(json.JSONEncoder):
|
|
11
|
+
def default(self, obj):
|
|
12
|
+
if isinstance(obj, pathlib.Path):
|
|
13
|
+
return obj.as_posix()
|
|
14
|
+
|
|
15
|
+
return super().default(obj)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@functools.wraps(json.dump)
|
|
19
|
+
def dump(obj, fp, *, cls=PathEncoder, **kwargs):
|
|
20
|
+
return json.dump(obj, fp, cls=cls, **kwargs)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@functools.wraps(json.dumps)
|
|
24
|
+
def dumps(obj, *, cls=PathEncoder, **kwargs):
|
|
25
|
+
return json.dumps(obj, cls=cls, **kwargs)
|
|
@@ -6,7 +6,7 @@ import re
|
|
|
6
6
|
import shutil
|
|
7
7
|
import subprocess
|
|
8
8
|
import sys
|
|
9
|
-
from typing import Callable, Dict, Generator, List, Literal, Set, Union
|
|
9
|
+
from typing import Callable, Dict, Generator, List, Literal, Set, Tuple, Union
|
|
10
10
|
|
|
11
11
|
from pathlibutil.base import BasePath, _Path
|
|
12
12
|
from pathlibutil.types import ByteInt, StatResult, _stat_result, byteint
|
|
@@ -22,8 +22,8 @@ class Path(BasePath):
|
|
|
22
22
|
|
|
23
23
|
- Contextmanger lets you change the current working directory.
|
|
24
24
|
```python
|
|
25
|
-
with Path(
|
|
26
|
-
print(f
|
|
25
|
+
with Path("path/to/directory") as cwd:
|
|
26
|
+
print(f"current working directory: {cwd}")
|
|
27
27
|
```
|
|
28
28
|
"""
|
|
29
29
|
|
|
@@ -84,15 +84,20 @@ class Path(BasePath):
|
|
|
84
84
|
if not self.is_file():
|
|
85
85
|
raise FileNotFoundError(f"'{self}' is not an existing file")
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
args = (kwargs.pop("length"),)
|
|
89
|
-
except KeyError:
|
|
90
|
-
args = ()
|
|
91
|
-
|
|
92
|
-
return hashlib.new(
|
|
87
|
+
hash = hashlib.new(
|
|
93
88
|
name=algorithm or self.default_hash,
|
|
94
89
|
data=self.read_bytes(),
|
|
95
|
-
)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
try:
|
|
93
|
+
return hash.hexdigest()
|
|
94
|
+
except TypeError as e:
|
|
95
|
+
try:
|
|
96
|
+
length = kwargs["length"]
|
|
97
|
+
except KeyError:
|
|
98
|
+
raise e
|
|
99
|
+
|
|
100
|
+
return hash.hexdigest(length)
|
|
96
101
|
|
|
97
102
|
def verify(
|
|
98
103
|
self, digest: str, algorithm: str = None, *, strict: bool = True, **kwargs
|
|
@@ -512,6 +517,7 @@ class Path(BasePath):
|
|
|
512
517
|
capture_output=True,
|
|
513
518
|
shell=True,
|
|
514
519
|
encoding="cp850",
|
|
520
|
+
check=True,
|
|
515
521
|
)
|
|
516
522
|
|
|
517
523
|
return result.stdout
|
|
@@ -523,7 +529,7 @@ class Path(BasePath):
|
|
|
523
529
|
re.IGNORECASE | re.MULTILINE,
|
|
524
530
|
)
|
|
525
531
|
return {
|
|
526
|
-
match.group("unc") + "\\": match.group("drive") + ":\\"
|
|
532
|
+
match.group("unc") + "\\": cls(match.group("drive") + ":\\")
|
|
527
533
|
for match in mapped_drives
|
|
528
534
|
}
|
|
529
535
|
except Exception:
|
|
@@ -538,7 +544,7 @@ class Path(BasePath):
|
|
|
538
544
|
|
|
539
545
|
try:
|
|
540
546
|
drive = self._netuse[self.anchor]
|
|
541
|
-
return
|
|
547
|
+
return drive.joinpath(self.relative_to(self.anchor))
|
|
542
548
|
except KeyError:
|
|
543
549
|
return self
|
|
544
550
|
|
|
@@ -567,6 +573,67 @@ class Path(BasePath):
|
|
|
567
573
|
|
|
568
574
|
return p._resolve_unc()
|
|
569
575
|
|
|
576
|
+
def walk(
|
|
577
|
+
self,
|
|
578
|
+
top_down: bool = True,
|
|
579
|
+
on_error: Callable[[OSError], object] = None,
|
|
580
|
+
follow_symlinks: bool = False,
|
|
581
|
+
) -> Generator[Tuple[_Path, List[str], List[str]], None, None]:
|
|
582
|
+
"""
|
|
583
|
+
Walks the directory tree and yields a 3-tuple of (dirpath, dirnames, filenames).
|
|
584
|
+
"""
|
|
585
|
+
try:
|
|
586
|
+
yield from super().walk(
|
|
587
|
+
top_down,
|
|
588
|
+
on_error,
|
|
589
|
+
follow_symlinks,
|
|
590
|
+
)
|
|
591
|
+
except AttributeError:
|
|
592
|
+
for dirpath, dirnames, filenames in os.walk(
|
|
593
|
+
self,
|
|
594
|
+
top_down,
|
|
595
|
+
on_error,
|
|
596
|
+
follow_symlinks,
|
|
597
|
+
):
|
|
598
|
+
yield self.__class__(dirpath), dirnames, filenames
|
|
599
|
+
|
|
600
|
+
def iterdir(
|
|
601
|
+
self,
|
|
602
|
+
*,
|
|
603
|
+
recursive: Union[bool, int] = False,
|
|
604
|
+
exclude_dirs: Callable[[_Path], bool] = None,
|
|
605
|
+
**kwargs,
|
|
606
|
+
) -> Generator[_Path, None, None]:
|
|
607
|
+
"""
|
|
608
|
+
Iterates over the files in the directory.
|
|
609
|
+
|
|
610
|
+
If `recursive` is `True` all files from the directory tree will
|
|
611
|
+
be yielded if it is an `integer` files are yielded to this max. directory depth
|
|
612
|
+
optional` **kwargs` are passed to `Path.walk()`.
|
|
613
|
+
|
|
614
|
+
When recursing, folders can be excluded by passing a callable for `exclude_dirs`, e.g.
|
|
615
|
+
|
|
616
|
+
```python
|
|
617
|
+
def exclude_version_control(dirpath: _Path) -> bool:
|
|
618
|
+
return dirpath.name in (".git", ".svn", ".hg", ".bzr", "CVS")
|
|
619
|
+
```
|
|
620
|
+
"""
|
|
621
|
+
if recursive is not False:
|
|
622
|
+
if exclude_dirs and not callable(exclude_dirs):
|
|
623
|
+
raise TypeError("exclude_dirs must be a callable")
|
|
624
|
+
|
|
625
|
+
depth = recursive if type(recursive) == int else None
|
|
626
|
+
|
|
627
|
+
for root, dirs, files in self.walk(**kwargs):
|
|
628
|
+
if depth is not None and len(root.relative_to(self).parts) >= depth:
|
|
629
|
+
dirs[:] = []
|
|
630
|
+
elif exclude_dirs:
|
|
631
|
+
dirs[:] = [d for d in dirs if not exclude_dirs(root.joinpath(d))]
|
|
632
|
+
|
|
633
|
+
yield from (root.joinpath(file) for file in files)
|
|
634
|
+
else:
|
|
635
|
+
yield from super().iterdir()
|
|
636
|
+
|
|
570
637
|
|
|
571
638
|
class Register7zFormat(Path, archive="7z"):
|
|
572
639
|
"""
|
|
@@ -583,20 +650,18 @@ class Register7zFormat(Path, archive="7z"):
|
|
|
583
650
|
|
|
584
651
|
Example:
|
|
585
652
|
```python
|
|
586
|
-
class Register7zArchive(pathlibutil.Path, archive=
|
|
653
|
+
class Register7zArchive(pathlibutil.Path, archive="7z"):
|
|
587
654
|
@classmethod
|
|
588
655
|
def _register_archive_format(cls):
|
|
589
656
|
try:
|
|
590
657
|
from py7zr import pack_7zarchive, unpack_7zarchive
|
|
591
658
|
except ModuleNotFoundError:
|
|
592
|
-
raise ModuleNotFoundError(
|
|
659
|
+
raise ModuleNotFoundError("pip install pathlibutil[7z]")
|
|
593
660
|
else:
|
|
594
661
|
shutil.register_archive_format(
|
|
595
|
-
|
|
596
|
-
)
|
|
597
|
-
shutil.register_unpack_format(
|
|
598
|
-
'7z', ['.7z'], unpack_7zarchive
|
|
662
|
+
"7z", pack_7zarchive, description="7zip archive"
|
|
599
663
|
)
|
|
664
|
+
shutil.register_unpack_format("7z", [".7z"], unpack_7zarchive)
|
|
600
665
|
```
|
|
601
666
|
"""
|
|
602
667
|
|
|
@@ -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 Iterable, Set, Tuple, TypeVar
|
|
6
6
|
|
|
7
7
|
_ByteInt = TypeVar("_ByteInt", bound="ByteInt")
|
|
8
8
|
_stat_result = TypeVar("_stat_result", bound="os.stat_result")
|
|
@@ -206,6 +206,7 @@ def byteint(func):
|
|
|
206
206
|
```python
|
|
207
207
|
randbyte = byteint(random.randint)
|
|
208
208
|
|
|
209
|
+
|
|
209
210
|
@byteint
|
|
210
211
|
def randhexbyte():
|
|
211
212
|
return hex(random.randint(0, 2**32))
|
|
@@ -1,31 +1,38 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
build-backend = "poetry.core.masonry.api"
|
|
3
|
+
|
|
4
|
+
requires = [
|
|
5
|
+
"poetry-core",
|
|
6
|
+
]
|
|
7
|
+
|
|
1
8
|
[tool.poetry]
|
|
2
9
|
name = "pathlibutil"
|
|
3
|
-
version = "v0.
|
|
10
|
+
version = "v0.2.0"
|
|
4
11
|
description = "inherits from pathlib.Path with methods for hashing, copying, deleting and more"
|
|
5
|
-
authors = ["Christoph Dörrer <d-chris@web.de>"]
|
|
12
|
+
authors = [ "Christoph Dörrer <d-chris@web.de>" ]
|
|
6
13
|
readme = "README.md"
|
|
7
14
|
license = "MIT"
|
|
8
15
|
classifiers = [
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
"Programming Language :: Python :: 3.8",
|
|
17
|
+
"Programming Language :: Python :: 3.9",
|
|
18
|
+
"Programming Language :: Python :: 3.10",
|
|
19
|
+
"Programming Language :: Python :: 3.11",
|
|
20
|
+
"Programming Language :: Python :: 3.12",
|
|
21
|
+
"License :: OSI Approved :: MIT License",
|
|
22
|
+
"Operating System :: OS Independent",
|
|
16
23
|
]
|
|
17
|
-
keywords = ["pathlib", "hashlib", "shutil"]
|
|
24
|
+
keywords = [ "pathlib", "hashlib", "shutil" ]
|
|
18
25
|
homepage = "https://d-chris.github.io"
|
|
19
26
|
repository = "https://github.com/d-chris/pathlibutil"
|
|
20
27
|
documentation = "https://d-chris.github.io/pathlibutil"
|
|
21
|
-
include = ["LICENSE"]
|
|
28
|
+
include = [ "LICENSE" ]
|
|
22
29
|
|
|
23
30
|
[tool.poetry.dependencies]
|
|
24
31
|
python = ">=3.8.1,<3.13"
|
|
25
32
|
py7zr = { version = "^0.20.2", optional = true }
|
|
26
33
|
|
|
27
34
|
[tool.poetry.extras]
|
|
28
|
-
7z = ["py7zr"]
|
|
35
|
+
7z = [ "py7zr" ]
|
|
29
36
|
|
|
30
37
|
[tool.poetry.group.dev.dependencies]
|
|
31
38
|
pytest = "^7.4.3"
|
|
@@ -55,19 +62,15 @@ name = "testpypi"
|
|
|
55
62
|
url = "https://test.pypi.org/legacy/"
|
|
56
63
|
priority = "explicit"
|
|
57
64
|
|
|
58
|
-
[build-system]
|
|
59
|
-
requires = ["poetry-core"]
|
|
60
|
-
build-backend = "poetry.core.masonry.api"
|
|
61
|
-
|
|
62
65
|
[tool.pytest.ini_options]
|
|
63
66
|
minversion = "6.0"
|
|
64
67
|
testpaths = "tests"
|
|
65
68
|
addopts = [
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
"--random-order",
|
|
70
|
+
"--color=yes",
|
|
71
|
+
"-s",
|
|
72
|
+
# "--cov=pathlibutil",
|
|
73
|
+
# "--cov-report=term-missing:skip-covered",
|
|
74
|
+
# "--cov-append",
|
|
75
|
+
# "--cov-report=html",
|
|
73
76
|
]
|
|
File without changes
|
|
File without changes
|