pyutilkit 0.1.0__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.
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/PKG-INFO +1 -1
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/pyproject.toml +2 -1
- pyutilkit-0.2.0/src/pyutilkit/__init__.py +1 -0
- pyutilkit-0.2.0/src/pyutilkit/files.py +54 -0
- pyutilkit-0.2.0/src/pyutilkit/term.py +116 -0
- pyutilkit-0.1.0/src/pyutilkit/__init__.py +0 -1
- pyutilkit-0.1.0/src/pyutilkit/term.py +0 -96
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/LICENSE.md +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/docs/README.md +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/classes.py +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/data/__init__.py +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/data/timezones.py +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/date_utils.py +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/py.typed +0 -0
- {pyutilkit-0.1.0 → pyutilkit-0.2.0}/src/pyutilkit/timing.py +0 -0
|
@@ -126,7 +126,7 @@ skip_empty = true
|
|
|
126
126
|
|
|
127
127
|
[tool.poetry]
|
|
128
128
|
name = "pyutilkit"
|
|
129
|
-
version = "0.
|
|
129
|
+
version = "0.2.0"
|
|
130
130
|
description = "python's missing batteries"
|
|
131
131
|
authors = [
|
|
132
132
|
"Stephanos Kuma <stephanos@kuma.ai>",
|
|
@@ -161,6 +161,7 @@ pipdeptree = "^2.13"
|
|
|
161
161
|
black = "^24.1"
|
|
162
162
|
mypy = "^1.8"
|
|
163
163
|
ruff = "^0.4"
|
|
164
|
+
typing_extensions = "^4.11"
|
|
164
165
|
|
|
165
166
|
[tool.poetry.group.test.dependencies]
|
|
166
167
|
freezegun = "^1.5"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.0"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import hashlib
|
|
4
|
+
import logging
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from functools import wraps
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, TypeVar
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from typing_extensions import ParamSpec # py3.9: import from typing
|
|
12
|
+
|
|
13
|
+
P = ParamSpec("P")
|
|
14
|
+
|
|
15
|
+
logger = logging.getLogger(__name__)
|
|
16
|
+
INGEST_ERROR = "Function `%s` threw `%s` when called with args=%s and kwargs=%s"
|
|
17
|
+
R_co = TypeVar("R_co", covariant=True)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def handle_exceptions(
|
|
21
|
+
*,
|
|
22
|
+
exceptions: tuple[type[Exception], ...] = (Exception,),
|
|
23
|
+
default: R_co | None = None,
|
|
24
|
+
log_level: str = "info",
|
|
25
|
+
) -> Callable[[Callable[P, R_co]], Callable[P, R_co | None]]:
|
|
26
|
+
def decorator(func: Callable[P, R_co]) -> Callable[P, R_co | None]:
|
|
27
|
+
@wraps(func)
|
|
28
|
+
def wrapper(*args: P.args, **kwargs: P.kwargs) -> R_co | None:
|
|
29
|
+
try:
|
|
30
|
+
return func(*args, **kwargs)
|
|
31
|
+
except exceptions as exc:
|
|
32
|
+
getattr(logger, log_level)(
|
|
33
|
+
INGEST_ERROR,
|
|
34
|
+
func.__name__,
|
|
35
|
+
exc.__class__.__name__,
|
|
36
|
+
args,
|
|
37
|
+
kwargs,
|
|
38
|
+
exc_info=True,
|
|
39
|
+
)
|
|
40
|
+
return default
|
|
41
|
+
|
|
42
|
+
return wrapper
|
|
43
|
+
|
|
44
|
+
return decorator
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def hash_file(path: Path, buffer_size: int = 2**16) -> str:
|
|
48
|
+
sha256 = hashlib.sha256()
|
|
49
|
+
|
|
50
|
+
with path.open("rb") as f:
|
|
51
|
+
while data := f.read(buffer_size):
|
|
52
|
+
sha256.update(data)
|
|
53
|
+
|
|
54
|
+
return sha256.hexdigest()
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from enum import IntEnum, unique
|
|
6
|
+
from math import ceil, floor
|
|
7
|
+
from typing import TYPE_CHECKING, Any
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from typing_extensions import Self # py3.10: import from typing
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@unique
|
|
14
|
+
class SGRCodes(IntEnum):
|
|
15
|
+
RESET = 0
|
|
16
|
+
|
|
17
|
+
BOLD = 1
|
|
18
|
+
ITALIC = 3
|
|
19
|
+
UNDERLINE = 4
|
|
20
|
+
BLINK = 5
|
|
21
|
+
REVERSE = 7
|
|
22
|
+
CONCEAL = 8
|
|
23
|
+
|
|
24
|
+
BLACK = 30
|
|
25
|
+
RED = 31
|
|
26
|
+
GREEN = 32
|
|
27
|
+
YELLOW = 33
|
|
28
|
+
BLUE = 34
|
|
29
|
+
MAGENTA = 35
|
|
30
|
+
CYAN = 36
|
|
31
|
+
GREY = 37
|
|
32
|
+
|
|
33
|
+
BG_BLACK = 40
|
|
34
|
+
BG_RED = 41
|
|
35
|
+
BG_GREEN = 42
|
|
36
|
+
BG_YELLOW = 43
|
|
37
|
+
BG_BLUE = 44
|
|
38
|
+
BG_MAGENTA = 45
|
|
39
|
+
BG_CYAN = 46
|
|
40
|
+
BG_GREY = 47
|
|
41
|
+
|
|
42
|
+
BLACK_BRIGHT = 90
|
|
43
|
+
RED_BRIGHT = 91
|
|
44
|
+
GREEN_BRIGHT = 92
|
|
45
|
+
YELLOW_BRIGHT = 93
|
|
46
|
+
BLUE_BRIGHT = 94
|
|
47
|
+
MAGENTA_BRIGHT = 95
|
|
48
|
+
CYAN_BRIGHT = 96
|
|
49
|
+
WHITE_BRIGHT = 97
|
|
50
|
+
|
|
51
|
+
BG_BLACK_BRIGHT = 100
|
|
52
|
+
BG_RED_BRIGHT = 101
|
|
53
|
+
BG_GREEN_BRIGHT = 102
|
|
54
|
+
BG_YELLOW_BRIGHT = 103
|
|
55
|
+
BG_BLUE_BRIGHT = 104
|
|
56
|
+
BG_MAGENTA_BRIGHT = 105
|
|
57
|
+
BG_CYAN_BRIGHT = 106
|
|
58
|
+
BG_WHITE_BRIGHT = 107
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def sequence(self) -> str:
|
|
62
|
+
return f"\033[{self.value}m"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class SGRString(str):
|
|
66
|
+
_sgr: tuple[SGRCodes, ...]
|
|
67
|
+
_string: str
|
|
68
|
+
__slots__ = ("_sgr", "_string")
|
|
69
|
+
|
|
70
|
+
def __new__(cls, obj: Any, *, params: Iterable[SGRCodes] = ()) -> Self:
|
|
71
|
+
string = super().__new__(cls, obj)
|
|
72
|
+
object.__setattr__(string, "_string", str(obj))
|
|
73
|
+
object.__setattr__(string, "_sgr", tuple(params))
|
|
74
|
+
return string
|
|
75
|
+
|
|
76
|
+
def __setattr__(self, name: str, value: Any) -> None:
|
|
77
|
+
msg = "SGRString is immutable"
|
|
78
|
+
raise AttributeError(msg)
|
|
79
|
+
|
|
80
|
+
def __delattr__(self, name: str) -> None:
|
|
81
|
+
msg = "SGRString is immutable"
|
|
82
|
+
raise AttributeError(msg)
|
|
83
|
+
|
|
84
|
+
def __str__(self) -> str:
|
|
85
|
+
if not self._sgr:
|
|
86
|
+
return self._string
|
|
87
|
+
prefix = "".join(code.sequence for code in self._sgr)
|
|
88
|
+
return f"{prefix}{self._string}{SGRCodes.RESET.sequence}"
|
|
89
|
+
|
|
90
|
+
def __mul__(self, other: Any) -> Self:
|
|
91
|
+
if not isinstance(other, int):
|
|
92
|
+
return NotImplemented
|
|
93
|
+
return type(self)(self._string * other, params=self._sgr)
|
|
94
|
+
|
|
95
|
+
def __rmul__(self, other: Any) -> Self:
|
|
96
|
+
if not isinstance(other, int):
|
|
97
|
+
return NotImplemented
|
|
98
|
+
return type(self)(self._string * other, params=self._sgr)
|
|
99
|
+
|
|
100
|
+
def header(
|
|
101
|
+
self,
|
|
102
|
+
*,
|
|
103
|
+
padding: str = " ",
|
|
104
|
+
left_spaces: int = 1,
|
|
105
|
+
right_spaces: int = 1,
|
|
106
|
+
space: str = " ",
|
|
107
|
+
) -> None:
|
|
108
|
+
columns = os.get_terminal_size().columns
|
|
109
|
+
text = f"{space * left_spaces}{self}{space * right_spaces}"
|
|
110
|
+
title_length = left_spaces + len(self) + right_spaces
|
|
111
|
+
if title_length >= columns:
|
|
112
|
+
print(text.strip())
|
|
113
|
+
return
|
|
114
|
+
|
|
115
|
+
half = (columns - title_length) / 2
|
|
116
|
+
print(f"{padding * ceil(half)}{text}{padding * floor(half)}")
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.0"
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from collections.abc import Iterable
|
|
5
|
-
from enum import IntEnum, unique
|
|
6
|
-
from math import ceil, floor
|
|
7
|
-
from typing import Any
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@unique
|
|
11
|
-
class SGRCodes(IntEnum):
|
|
12
|
-
RESET = 0
|
|
13
|
-
|
|
14
|
-
BOLD = 1
|
|
15
|
-
ITALIC = 3
|
|
16
|
-
UNDERLINE = 4
|
|
17
|
-
BLINK = 5
|
|
18
|
-
REVERSE = 7
|
|
19
|
-
CONCEAL = 8
|
|
20
|
-
|
|
21
|
-
BLACK = 30
|
|
22
|
-
RED = 31
|
|
23
|
-
GREEN = 32
|
|
24
|
-
YELLOW = 33
|
|
25
|
-
BLUE = 34
|
|
26
|
-
MAGENTA = 35
|
|
27
|
-
CYAN = 36
|
|
28
|
-
GREY = 37
|
|
29
|
-
|
|
30
|
-
BG_BLACK = 40
|
|
31
|
-
BG_RED = 41
|
|
32
|
-
BG_GREEN = 42
|
|
33
|
-
BG_YELLOW = 43
|
|
34
|
-
BG_BLUE = 44
|
|
35
|
-
BG_MAGENTA = 45
|
|
36
|
-
BG_CYAN = 46
|
|
37
|
-
BG_GREY = 47
|
|
38
|
-
|
|
39
|
-
BLACK_BRIGHT = 90
|
|
40
|
-
RED_BRIGHT = 91
|
|
41
|
-
GREEN_BRIGHT = 92
|
|
42
|
-
YELLOW_BRIGHT = 93
|
|
43
|
-
BLUE_BRIGHT = 94
|
|
44
|
-
MAGENTA_BRIGHT = 95
|
|
45
|
-
CYAN_BRIGHT = 96
|
|
46
|
-
WHITE_BRIGHT = 97
|
|
47
|
-
|
|
48
|
-
BG_BLACK_BRIGHT = 100
|
|
49
|
-
BG_RED_BRIGHT = 101
|
|
50
|
-
BG_GREEN_BRIGHT = 102
|
|
51
|
-
BG_YELLOW_BRIGHT = 103
|
|
52
|
-
BG_BLUE_BRIGHT = 104
|
|
53
|
-
BG_MAGENTA_BRIGHT = 105
|
|
54
|
-
BG_CYAN_BRIGHT = 106
|
|
55
|
-
BG_WHITE_BRIGHT = 107
|
|
56
|
-
|
|
57
|
-
@property
|
|
58
|
-
def sequence(self) -> str:
|
|
59
|
-
return f"\033[{self.value}m"
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
class SGRString(str):
|
|
63
|
-
_sgr: str
|
|
64
|
-
__slots__ = ("_sgr",)
|
|
65
|
-
|
|
66
|
-
def __new__(cls, value: Any, *, params: Iterable[SGRCodes] = ()) -> SGRString:
|
|
67
|
-
string = super().__new__(cls, value)
|
|
68
|
-
suffix = SGRCodes.RESET.sequence
|
|
69
|
-
prefix = "".join(param.sequence for param in params) or SGRCodes.RESET.sequence
|
|
70
|
-
object.__setattr__(string, "_sgr", f"{prefix}{value}{suffix}")
|
|
71
|
-
return string
|
|
72
|
-
|
|
73
|
-
def __setattr__(self, name: str, value: Any) -> None:
|
|
74
|
-
msg = "SGRString is immutable"
|
|
75
|
-
raise AttributeError(msg)
|
|
76
|
-
|
|
77
|
-
def __delattr__(self, name: str) -> None:
|
|
78
|
-
msg = "SGRString is immutable"
|
|
79
|
-
raise AttributeError(msg)
|
|
80
|
-
|
|
81
|
-
def __str__(self) -> str:
|
|
82
|
-
return self._sgr
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def header(
|
|
86
|
-
text: str, *, padding: str = " ", left_spaces: int = 1, right_spaces: int = 1
|
|
87
|
-
) -> None:
|
|
88
|
-
columns = os.get_terminal_size().columns
|
|
89
|
-
text = f"{' ' * left_spaces}{text.strip()}{' ' * right_spaces}"
|
|
90
|
-
title_length = len(text)
|
|
91
|
-
if title_length >= columns:
|
|
92
|
-
print(text.strip())
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
half = (columns - len(text)) / 2
|
|
96
|
-
print(f"{padding * ceil(half)}{text}{padding * floor(half)}")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|