pathlibutil 0.1.6__tar.gz → 0.1.8__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.6 → pathlibutil-0.1.8}/PKG-INFO +7 -3
- {pathlibutil-0.1.6 → pathlibutil-0.1.8}/README.md +6 -2
- pathlibutil-0.1.8/pathlibutil/__init__.py +8 -0
- pathlibutil-0.1.8/pathlibutil/base.py +19 -0
- {pathlibutil-0.1.6 → pathlibutil-0.1.8}/pathlibutil/path.py +152 -20
- {pathlibutil-0.1.6 → pathlibutil-0.1.8}/pathlibutil/types.py +163 -2
- {pathlibutil-0.1.6 → pathlibutil-0.1.8}/pyproject.toml +6 -4
- pathlibutil-0.1.6/pathlibutil/__init__.py +0 -8
- {pathlibutil-0.1.6 → pathlibutil-0.1.8}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pathlibutil
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.8
|
|
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
|
|
@@ -40,6 +40,7 @@ Description-Content-Type: text/markdown
|
|
|
40
40
|
- `Path.hexdigest()` to calculate and `Path.verify()` for verification of hexdigest from a file
|
|
41
41
|
- `Path.default_hash` to configurate default hash algorithm for `Path` class (default: *'md5'*)
|
|
42
42
|
- `Path.size()` to get size in bytes of a file or directory
|
|
43
|
+
- `byteint` function decorator converts the return value of `int` to a `ByteInt` object
|
|
43
44
|
- `Path.read_lines()` to yield over all lines from a file until EOF
|
|
44
45
|
- `contextmanager` to change current working directory with `with` statement
|
|
45
46
|
- `Path.copy()` copy a file or directory to a new path destination
|
|
@@ -47,8 +48,11 @@ Description-Content-Type: text/markdown
|
|
|
47
48
|
- `Path.move()` move a file or directory to a new path destination
|
|
48
49
|
- `Path.make_archive()` creates and `Path.unpack_archive()` uncompresses an archive from a file or directory
|
|
49
50
|
- `Path.archive_formats` to get all available archive formats
|
|
50
|
-
- `
|
|
51
|
-
|
|
51
|
+
- `Path.stat()` returns a `StatResult` object to get file or directory information containing
|
|
52
|
+
- `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`
|
|
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
|
+
|
|
52
56
|
## Installation
|
|
53
57
|
|
|
54
58
|
```bash
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
- `Path.hexdigest()` to calculate and `Path.verify()` for verification of hexdigest from a file
|
|
17
17
|
- `Path.default_hash` to configurate default hash algorithm for `Path` class (default: *'md5'*)
|
|
18
18
|
- `Path.size()` to get size in bytes of a file or directory
|
|
19
|
+
- `byteint` function decorator converts the return value of `int` to a `ByteInt` object
|
|
19
20
|
- `Path.read_lines()` to yield over all lines from a file until EOF
|
|
20
21
|
- `contextmanager` to change current working directory with `with` statement
|
|
21
22
|
- `Path.copy()` copy a file or directory to a new path destination
|
|
@@ -23,8 +24,11 @@
|
|
|
23
24
|
- `Path.move()` move a file or directory to a new path destination
|
|
24
25
|
- `Path.make_archive()` creates and `Path.unpack_archive()` uncompresses an archive from a file or directory
|
|
25
26
|
- `Path.archive_formats` to get all available archive formats
|
|
26
|
-
- `
|
|
27
|
-
|
|
27
|
+
- `Path.stat()` returns a `StatResult` object to get file or directory information containing
|
|
28
|
+
- `TimeInt` objects for `atime`, `ctime`, `mtime` and `birthtime`
|
|
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
|
+
|
|
28
32
|
## Installation
|
|
29
33
|
|
|
30
34
|
```bash
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import pathlib
|
|
3
|
+
import sys
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
_Path = TypeVar("_Path", bound=pathlib.Path)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class BasePath(pathlib.Path):
|
|
10
|
+
"""
|
|
11
|
+
Baseclass to inherit from `pathlib.Path`.
|
|
12
|
+
|
|
13
|
+
This class is only needed for python versions < 3.12.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
if sys.version_info < (3, 12):
|
|
17
|
+
_flavour = (
|
|
18
|
+
pathlib._windows_flavour if os.name == "nt" else pathlib._posix_flavour
|
|
19
|
+
)
|
|
@@ -2,17 +2,14 @@ import errno
|
|
|
2
2
|
import hashlib
|
|
3
3
|
import itertools
|
|
4
4
|
import os
|
|
5
|
-
import pathlib
|
|
6
5
|
import shutil
|
|
7
|
-
import
|
|
8
|
-
from typing import Callable, Dict, Generator, Set, TypeVar
|
|
6
|
+
from typing import Callable, Dict, Generator, List, Set, Union
|
|
9
7
|
|
|
10
|
-
from pathlibutil.
|
|
8
|
+
from pathlibutil.base import BasePath, _Path
|
|
9
|
+
from pathlibutil.types import ByteInt, StatResult, _stat_result, byteint
|
|
11
10
|
|
|
12
|
-
_Path = TypeVar("_Path", bound="Path")
|
|
13
11
|
|
|
14
|
-
|
|
15
|
-
class Path(pathlib.Path):
|
|
12
|
+
class Path(BasePath):
|
|
16
13
|
"""
|
|
17
14
|
Path inherites from `pathlib.Path` and adds some methods to built-in python
|
|
18
15
|
functions.
|
|
@@ -40,11 +37,6 @@ class Path(pathlib.Path):
|
|
|
40
37
|
be the default
|
|
41
38
|
"""
|
|
42
39
|
|
|
43
|
-
if sys.version_info < (3, 12):
|
|
44
|
-
_flavour = (
|
|
45
|
-
pathlib._windows_flavour if os.name == "nt" else pathlib._posix_flavour
|
|
46
|
-
)
|
|
47
|
-
|
|
48
40
|
def __init_subclass__(cls, **kwargs) -> None:
|
|
49
41
|
"""
|
|
50
42
|
Register archive formats from subclasses.
|
|
@@ -172,7 +164,7 @@ class Path(pathlib.Path):
|
|
|
172
164
|
if self.is_dir():
|
|
173
165
|
return sum([p.size(**kwargs) for p in self.iterdir()])
|
|
174
166
|
|
|
175
|
-
return
|
|
167
|
+
return super().stat(**kwargs).st_size
|
|
176
168
|
|
|
177
169
|
def copy(self, dst: str, exist_ok: bool = True, **kwargs) -> _Path:
|
|
178
170
|
"""
|
|
@@ -246,11 +238,11 @@ class Path(pathlib.Path):
|
|
|
246
238
|
return self.__class__(_path)
|
|
247
239
|
|
|
248
240
|
@staticmethod
|
|
249
|
-
def _find_archive_format(filename:
|
|
241
|
+
def _find_archive_format(filename: _Path) -> str:
|
|
250
242
|
"""
|
|
251
243
|
Searches for a file the correct archive format.
|
|
252
244
|
"""
|
|
253
|
-
ext = "
|
|
245
|
+
ext = "".join(filename.suffixes)
|
|
254
246
|
|
|
255
247
|
for name, extensions, _ in shutil.get_unpack_formats():
|
|
256
248
|
if ext in extensions:
|
|
@@ -270,19 +262,61 @@ class Path(pathlib.Path):
|
|
|
270
262
|
else:
|
|
271
263
|
register_format()
|
|
272
264
|
|
|
273
|
-
def make_archive(
|
|
265
|
+
def make_archive(
|
|
266
|
+
self, archivename: str, *, exists_ok: bool = False, **kwargs
|
|
267
|
+
) -> _Path:
|
|
274
268
|
"""
|
|
275
269
|
Creates an archive file (eg. zip) and returns the path to the archive.
|
|
276
270
|
|
|
271
|
+
If `exists_ok` is `False` a `FileExistsError` is raised if the archive file
|
|
272
|
+
already exists.
|
|
273
|
+
|
|
274
|
+
If `exists_ok` is `True` the existing archive file will be deleted before
|
|
275
|
+
creating the new one.
|
|
276
|
+
|
|
277
277
|
For `**kwargs` see `shutil.make_archive()`.
|
|
278
278
|
- `root_dir` and `base_dir` will be resolved automatically
|
|
279
279
|
- `format` will be determined by the file suffix
|
|
280
280
|
- It can be overwritten with an `format` keyword-argument.
|
|
281
281
|
- a `ValueError` is raised if the `format` is unknown.
|
|
282
|
+
|
|
283
|
+
>>> Path(__file__).make_archive('test.tar.gz')
|
|
284
|
+
Path('test.tar.gz')
|
|
285
|
+
|
|
286
|
+
>>> Path(__file__).make_archive('test.zpy', format='zip')
|
|
287
|
+
Path('test.zpy')
|
|
282
288
|
"""
|
|
289
|
+
|
|
290
|
+
def _archive_exists(file: str, exists_ok: bool) -> _Path:
|
|
291
|
+
"""
|
|
292
|
+
Returns a `Path` object of the archive file or raises a `FileExistsError`
|
|
293
|
+
If `exists_ok` is `True` the file will be deleted.
|
|
294
|
+
"""
|
|
295
|
+
file = self.__class__(file).resolve()
|
|
296
|
+
|
|
297
|
+
if file.exists():
|
|
298
|
+
if not exists_ok:
|
|
299
|
+
raise FileExistsError(f"{file} already exists")
|
|
300
|
+
|
|
301
|
+
file.unlink()
|
|
302
|
+
|
|
303
|
+
return file
|
|
304
|
+
|
|
305
|
+
def _archive_filename(expect: Path, real: str) -> _Path:
|
|
306
|
+
"""
|
|
307
|
+
Check if the expected archive filename matches the real filename.
|
|
308
|
+
If not try to rename the real filename.
|
|
309
|
+
"""
|
|
310
|
+
file = self.__class__(real).resolve(True)
|
|
311
|
+
|
|
312
|
+
if file.suffixes != expect.suffixes:
|
|
313
|
+
return file.rename(expect)
|
|
314
|
+
|
|
315
|
+
return file
|
|
316
|
+
|
|
283
317
|
_self = self.resolve(strict=True)
|
|
284
|
-
|
|
285
|
-
_format = kwargs.pop("format", self._find_archive_format(
|
|
318
|
+
_filename = _archive_exists(archivename, exists_ok)
|
|
319
|
+
_format = kwargs.pop("format", self._find_archive_format(_filename))
|
|
286
320
|
|
|
287
321
|
_ = kwargs.pop("root_dir", None)
|
|
288
322
|
_ = kwargs.pop("base_dir", None)
|
|
@@ -290,17 +324,19 @@ class Path(pathlib.Path):
|
|
|
290
324
|
for _ in range(2):
|
|
291
325
|
try:
|
|
292
326
|
_archive = shutil.make_archive(
|
|
293
|
-
base_name=
|
|
327
|
+
base_name=_filename.with_suffix([]),
|
|
294
328
|
format=_format,
|
|
295
329
|
root_dir=_self.parent,
|
|
296
330
|
base_dir=_self.relative_to(_self.parent),
|
|
297
331
|
**kwargs,
|
|
298
332
|
)
|
|
299
333
|
|
|
300
|
-
|
|
334
|
+
break
|
|
301
335
|
except ValueError:
|
|
302
336
|
self._register_format(_format)
|
|
303
337
|
|
|
338
|
+
return _archive_filename(_filename, _archive)
|
|
339
|
+
|
|
304
340
|
def unpack_archive(self, extract_dir: str, **kwargs) -> _Path:
|
|
305
341
|
"""
|
|
306
342
|
Unpacks an archive file (eg. zip) into a directory and returns the path to the
|
|
@@ -310,6 +346,12 @@ class Path(pathlib.Path):
|
|
|
310
346
|
- `format` will be determined by the file suffix
|
|
311
347
|
- It can be overwritten with an `format` keyword-argument.
|
|
312
348
|
- a `ValueError` is raised if the `format` is unknown.
|
|
349
|
+
|
|
350
|
+
>>> Path('test.tar.gz').unpack_archive('test')
|
|
351
|
+
Path('test')
|
|
352
|
+
|
|
353
|
+
>>> Path('test.zpy').unpack_archive('test', format='zip')
|
|
354
|
+
Path('test')
|
|
313
355
|
"""
|
|
314
356
|
|
|
315
357
|
_format = kwargs.pop("format", self._find_archive_format(self))
|
|
@@ -343,6 +385,96 @@ class Path(pathlib.Path):
|
|
|
343
385
|
|
|
344
386
|
return set(formats)
|
|
345
387
|
|
|
388
|
+
def stat(self, **kwargs) -> _stat_result:
|
|
389
|
+
"""
|
|
390
|
+
Returns a `StatResult` object which modifies following attributes:
|
|
391
|
+
|
|
392
|
+
- `st_size` is wrapped in `ByteInt`
|
|
393
|
+
- `st_atime`, `st_mtime`, `st_ctime`, `st_birthtime` are wrapped in `TimeInt`
|
|
394
|
+
|
|
395
|
+
For `**kwargs` see `pathlib.Path.stat()`.
|
|
396
|
+
"""
|
|
397
|
+
return StatResult(super().stat(**kwargs))
|
|
398
|
+
|
|
399
|
+
def with_suffix(self, suffix: Union[str, List[str]]) -> _Path:
|
|
400
|
+
"""
|
|
401
|
+
Return a new `Path` with changed suffix or remove it when its an empty
|
|
402
|
+
string.
|
|
403
|
+
|
|
404
|
+
Multiple suffixes can be changed at once by passing a list of suffixes.
|
|
405
|
+
With a empty list all suffixes will be removed.
|
|
406
|
+
|
|
407
|
+
>>> Path('test.a.b').with_suffix('.c')
|
|
408
|
+
Path('test.a.c')
|
|
409
|
+
|
|
410
|
+
>>> Path('test.a.b').with_suffix('')
|
|
411
|
+
Path('test.a')
|
|
412
|
+
|
|
413
|
+
>>> Path('test.a.b').with_suffix(['.c', '.d'])
|
|
414
|
+
Path('test.c.d')
|
|
415
|
+
|
|
416
|
+
>>> Path('test.a.b').with_suffix([])
|
|
417
|
+
Path('test')
|
|
418
|
+
"""
|
|
419
|
+
|
|
420
|
+
try:
|
|
421
|
+
return super().with_suffix(suffix)
|
|
422
|
+
except (AttributeError, TypeError):
|
|
423
|
+
if isinstance(suffix, list) and not suffix:
|
|
424
|
+
suffix = ""
|
|
425
|
+
elif all(s.startswith(".") for s in suffix):
|
|
426
|
+
suffix = "".join(suffix)
|
|
427
|
+
elif all(not s for s in suffix):
|
|
428
|
+
suffix = ""
|
|
429
|
+
else:
|
|
430
|
+
raise ValueError(f"Invalid suffix '{suffix}'")
|
|
431
|
+
|
|
432
|
+
end = -1 * len(self.suffixes) or None
|
|
433
|
+
name = self.name.split(".")[0:end]
|
|
434
|
+
stem = self.parent.joinpath("".join(name))
|
|
435
|
+
return super(self.__class__, stem).with_suffix(suffix)
|
|
436
|
+
|
|
437
|
+
def relative_to(
|
|
438
|
+
self, *other: Union[str, _Path], walk_up: Union[bool, int] = False
|
|
439
|
+
) -> _Path:
|
|
440
|
+
"""
|
|
441
|
+
Return the relative path to another path identified by the passed
|
|
442
|
+
arguments. If the operation is not possible (because this is not
|
|
443
|
+
related to the other path), raise `ValueError`.
|
|
444
|
+
|
|
445
|
+
The `walk_up` parameter controls whether `..` may be used to resolve
|
|
446
|
+
the path.
|
|
447
|
+
|
|
448
|
+
If `walk_up` is a integer it specifies the maximum number of `..` to resolve, if
|
|
449
|
+
max is reached a `ValueError` is raised.
|
|
450
|
+
|
|
451
|
+
>>> Path('a/b/c/d').relative_to('a/b')
|
|
452
|
+
Path('c/d')
|
|
453
|
+
|
|
454
|
+
>>> Path('a/b/c/d').relative_to('a/b/e/f/g', walk_up=True)
|
|
455
|
+
Path('../../../c/d')
|
|
456
|
+
|
|
457
|
+
>>> Path('a/b/c/d').relative_to('a/b/e/f/g', walk_up=2)
|
|
458
|
+
ValueError: '../../../c/d' is outside of the relative deepth of '2'
|
|
459
|
+
"""
|
|
460
|
+
if not walk_up:
|
|
461
|
+
return super().relative_to(*other)
|
|
462
|
+
|
|
463
|
+
try:
|
|
464
|
+
relative = super().relative_to(*other, walk_up=walk_up)
|
|
465
|
+
except TypeError:
|
|
466
|
+
relative = self.__class__(os.path.relpath(self, Path(*other)))
|
|
467
|
+
|
|
468
|
+
if type(walk_up) is not int:
|
|
469
|
+
return relative
|
|
470
|
+
|
|
471
|
+
if relative.parts.count("..") > walk_up:
|
|
472
|
+
raise ValueError(
|
|
473
|
+
f"'{relative}' is outside of the relative deepth of '{walk_up}'"
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
return relative
|
|
477
|
+
|
|
346
478
|
|
|
347
479
|
class Register7zFormat(Path, archive="7z"):
|
|
348
480
|
"""
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
import functools
|
|
2
|
+
import os
|
|
2
3
|
import re
|
|
3
|
-
from
|
|
4
|
+
from datetime import datetime, tzinfo
|
|
5
|
+
from typing import Set, Tuple, TypeVar, Iterable
|
|
4
6
|
|
|
5
7
|
_ByteInt = TypeVar("_ByteInt", bound="ByteInt")
|
|
8
|
+
_stat_result = TypeVar("_stat_result", bound="os.stat_result")
|
|
6
9
|
|
|
7
10
|
|
|
8
11
|
class ByteInt(int):
|
|
9
12
|
"""
|
|
10
13
|
Inherit from `int` with attributes to convert bytes to decimal or binary `units` for
|
|
11
|
-
measuring storage data. These attributes will return a `float
|
|
14
|
+
measuring storage data. These attributes will return a `float`.
|
|
12
15
|
|
|
13
16
|
>>> ByteInt(1234).kb
|
|
14
17
|
1.234
|
|
@@ -17,6 +20,11 @@ class ByteInt(int):
|
|
|
17
20
|
|
|
18
21
|
>>> f"{ByteInt(6543210):.2mib} mebibytes"
|
|
19
22
|
'6.24 mebibytes'
|
|
23
|
+
|
|
24
|
+
String representation of `ByteInt` will return the most appropriate decimal unit.
|
|
25
|
+
|
|
26
|
+
>>> str(ByteInt(987654))
|
|
27
|
+
'987.7 kb'
|
|
20
28
|
"""
|
|
21
29
|
|
|
22
30
|
__regex = re.compile(r"(?P<unit>[kmgtpezy]i?b)")
|
|
@@ -62,6 +70,36 @@ class ByteInt(int):
|
|
|
62
70
|
"""
|
|
63
71
|
return set(self.__bytes.keys())
|
|
64
72
|
|
|
73
|
+
def __str__(self) -> str:
|
|
74
|
+
return self.string()
|
|
75
|
+
|
|
76
|
+
def string(self, decimal=True) -> str:
|
|
77
|
+
"""
|
|
78
|
+
Return a string representation of `self` in the most appropriate unit.
|
|
79
|
+
|
|
80
|
+
If `decimal` is `False` then binary units will be used instead of `decimal`.
|
|
81
|
+
|
|
82
|
+
>>> ByteInt(12346789).string(False)
|
|
83
|
+
'11.77 mib'
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def _decimal(x):
|
|
87
|
+
return "i" not in x[0]
|
|
88
|
+
|
|
89
|
+
def _binary(x):
|
|
90
|
+
return x[0].endswith("ib")
|
|
91
|
+
|
|
92
|
+
query = _decimal if decimal else _binary
|
|
93
|
+
|
|
94
|
+
for unit, (byte, _) in filter(query, self.__bytes.items()):
|
|
95
|
+
value = self / byte
|
|
96
|
+
|
|
97
|
+
if 1 <= value < 1000:
|
|
98
|
+
dec = 2 if value < 100 else 1
|
|
99
|
+
return value.__format__(f".{dec}f") + f" {unit}"
|
|
100
|
+
|
|
101
|
+
return f"{int(self)} b"
|
|
102
|
+
|
|
65
103
|
@classmethod
|
|
66
104
|
def info(cls, unit: str) -> Tuple[int, str]:
|
|
67
105
|
"""
|
|
@@ -190,3 +228,126 @@ def byteint(func):
|
|
|
190
228
|
return value
|
|
191
229
|
|
|
192
230
|
return wrapper
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class TimeInt(float):
|
|
234
|
+
"""
|
|
235
|
+
Inherit from `float` with attributes to convert seconds to `datetime` objects.
|
|
236
|
+
|
|
237
|
+
>>> TimeInt(0).datetime
|
|
238
|
+
datetime.datetime(1970, 1, 1, 0, 0)
|
|
239
|
+
|
|
240
|
+
>>> TimeInt(0).string('%d.%m.%Y')
|
|
241
|
+
'01.01.1970'
|
|
242
|
+
|
|
243
|
+
Return a string representation using `TimeInt.format`.
|
|
244
|
+
|
|
245
|
+
>>> str(TimeInt(0))
|
|
246
|
+
'1970-01-01 00:00:00'
|
|
247
|
+
"""
|
|
248
|
+
|
|
249
|
+
format = "%Y-%m-%d %H:%M:%S"
|
|
250
|
+
"""
|
|
251
|
+
Format string to which is uesed to convert `self` to a string. Default: 'isoformat'.
|
|
252
|
+
For more information see `datetime.datetime.strftime`.
|
|
253
|
+
"""
|
|
254
|
+
|
|
255
|
+
def __new__(cls, value: int, tz: tzinfo = None) -> float:
|
|
256
|
+
"""
|
|
257
|
+
Create a new instance from baseclass `int`.
|
|
258
|
+
"""
|
|
259
|
+
return super().__new__(cls, value)
|
|
260
|
+
|
|
261
|
+
def __init__(self, value: int, tz: tzinfo = None) -> None:
|
|
262
|
+
"""
|
|
263
|
+
Create a new instance from baseclass `int` with optional `timezone` info.
|
|
264
|
+
"""
|
|
265
|
+
self.timezone = tz
|
|
266
|
+
"""
|
|
267
|
+
property for `datetime.timezone` object is set with `__init__` or can be
|
|
268
|
+
changed to different timezones to get the correct string reprensentation. If
|
|
269
|
+
timezone is `None` then the local timezone is used.
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
@functools.cached_property
|
|
273
|
+
def datetime(self) -> datetime:
|
|
274
|
+
"""
|
|
275
|
+
property returns a `datetime.datetime` object.
|
|
276
|
+
"""
|
|
277
|
+
return datetime.fromtimestamp(self, self.timezone)
|
|
278
|
+
|
|
279
|
+
def __str__(self) -> str:
|
|
280
|
+
"""
|
|
281
|
+
Return a string representation of `datetime` using `self.format`.
|
|
282
|
+
"""
|
|
283
|
+
return self.string()
|
|
284
|
+
|
|
285
|
+
def string(self, format_string: str = None) -> str:
|
|
286
|
+
"""
|
|
287
|
+
Return a string representation of `datetime` using the `format_string`.
|
|
288
|
+
|
|
289
|
+
If `format_string` is `None` then `TimeInt.format` is used.
|
|
290
|
+
"""
|
|
291
|
+
return self.datetime.strftime(format_string or self.format)
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class StatResult:
|
|
295
|
+
"""
|
|
296
|
+
Object converts `st_size` to `ByteInt` and
|
|
297
|
+
`st_atime`, `st_mtime`, `st_ctime` and `st_birthtime` to `TimeInt`.
|
|
298
|
+
|
|
299
|
+
Inheritance was not possible due `@final` decorator is applied to `os.stat_result`
|
|
300
|
+
to prevent subclassing.
|
|
301
|
+
"""
|
|
302
|
+
|
|
303
|
+
def __init__(self, stat):
|
|
304
|
+
"""
|
|
305
|
+
Wrapper for `os.stat_result`.
|
|
306
|
+
"""
|
|
307
|
+
self._obj = stat
|
|
308
|
+
|
|
309
|
+
def __getattr__(self, name):
|
|
310
|
+
"""
|
|
311
|
+
Forward all unknown attributes to `self._obj`.
|
|
312
|
+
"""
|
|
313
|
+
attr = getattr(self._obj, name)
|
|
314
|
+
|
|
315
|
+
if isinstance(attr, int):
|
|
316
|
+
if name == "st_size":
|
|
317
|
+
return ByteInt(attr)
|
|
318
|
+
elif isinstance(attr, float):
|
|
319
|
+
if name in ("st_atime", "st_mtime", "st_ctime", "st_birthtime"):
|
|
320
|
+
return TimeInt(attr)
|
|
321
|
+
|
|
322
|
+
return attr
|
|
323
|
+
|
|
324
|
+
def __str__(self) -> str:
|
|
325
|
+
"""
|
|
326
|
+
Return a string of `os.stat_result` object.
|
|
327
|
+
"""
|
|
328
|
+
return str(self._obj)
|
|
329
|
+
|
|
330
|
+
def __repr__(self) -> str:
|
|
331
|
+
"""
|
|
332
|
+
Return representation of `os.stat_result` object.
|
|
333
|
+
"""
|
|
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.8"
|
|
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"
|
|
@@ -33,6 +33,7 @@ tox = "^4.11.4"
|
|
|
33
33
|
pytest-random-order = "^1.1.0"
|
|
34
34
|
pytest-cov = "^4.1.0"
|
|
35
35
|
pytest-mock = "^3.12.0"
|
|
36
|
+
exrex = "^0.11.0"
|
|
36
37
|
|
|
37
38
|
[tool.poetry.group.code.dependencies]
|
|
38
39
|
flake8 = "^7.0.0"
|
|
@@ -63,8 +64,9 @@ testpaths = "tests"
|
|
|
63
64
|
addopts = [
|
|
64
65
|
"--random-order",
|
|
65
66
|
"--color=yes",
|
|
66
|
-
"
|
|
67
|
-
"--cov
|
|
68
|
-
"--cov-
|
|
67
|
+
"-s",
|
|
68
|
+
# "--cov=pathlibutil",
|
|
69
|
+
# "--cov-report=term-missing:skip-covered",
|
|
70
|
+
# "--cov-append",
|
|
69
71
|
# "--cov-report=html",
|
|
70
72
|
]
|
|
File without changes
|