pyutilkit 0.2.0__tar.gz → 0.4.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.2.0 → pyutilkit-0.4.0}/PKG-INFO +7 -4
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/docs/README.md +5 -1
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/pyproject.toml +4 -4
- pyutilkit-0.4.0/src/pyutilkit/data/__init__.py +0 -0
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/src/pyutilkit/date_utils.py +0 -1
- pyutilkit-0.4.0/src/pyutilkit/py.typed +0 -0
- pyutilkit-0.4.0/src/pyutilkit/timing.py +123 -0
- pyutilkit-0.2.0/src/pyutilkit/__init__.py +0 -1
- pyutilkit-0.2.0/src/pyutilkit/timing.py +0 -38
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/LICENSE.md +0 -0
- {pyutilkit-0.2.0/src/pyutilkit/data → pyutilkit-0.4.0/src/pyutilkit}/__init__.py +0 -0
- /pyutilkit-0.2.0/src/pyutilkit/py.typed → /pyutilkit-0.4.0/src/pyutilkit/cache.py +0 -0
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/src/pyutilkit/classes.py +0 -0
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/src/pyutilkit/data/timezones.py +0 -0
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/src/pyutilkit/files.py +0 -0
- {pyutilkit-0.2.0 → pyutilkit-0.4.0}/src/pyutilkit/term.py +0 -0
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyutilkit
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.0
|
|
4
4
|
Summary: python's missing batteries
|
|
5
5
|
Home-page: https://pyutilkit.readthedocs.io/en/stable/
|
|
6
6
|
License: LGPL-3.0+
|
|
7
7
|
Keywords: utils
|
|
8
8
|
Author: Stephanos Kuma
|
|
9
9
|
Author-email: stephanos@kuma.ai
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.9,<4.0
|
|
11
11
|
Classifier: Development Status :: 4 - Beta
|
|
12
12
|
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
13
13
|
Classifier: Operating System :: OS Independent
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
15
|
Classifier: Programming Language :: Python :: 3.9
|
|
17
16
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.11
|
|
@@ -31,7 +30,11 @@ Description-Content-Type: text/markdown
|
|
|
31
30
|
[![build automation: yam][yam_badge]][yam_url]
|
|
32
31
|
[![Lint: ruff][ruff_badge]][ruff_url]
|
|
33
32
|
|
|
34
|
-
|
|
33
|
+
The Python has long maintained the philosophy of "batteries included", giving the user
|
|
34
|
+
a rich standard library, avoiding the need for third party tools for most work. Some packages
|
|
35
|
+
are so common, that the have a similar status to the standard library. Still, some code seems
|
|
36
|
+
to be written time and again, with every project. This small library, with minimal requirements,
|
|
37
|
+
hopes to stop this repetition.
|
|
35
38
|
|
|
36
39
|
## Links
|
|
37
40
|
|
|
@@ -8,7 +8,11 @@
|
|
|
8
8
|
[![build automation: yam][yam_badge]][yam_url]
|
|
9
9
|
[![Lint: ruff][ruff_badge]][ruff_url]
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
The Python has long maintained the philosophy of "batteries included", giving the user
|
|
12
|
+
a rich standard library, avoiding the need for third party tools for most work. Some packages
|
|
13
|
+
are so common, that the have a similar status to the standard library. Still, some code seems
|
|
14
|
+
to be written time and again, with every project. This small library, with minimal requirements,
|
|
15
|
+
hopes to stop this repetition.
|
|
12
16
|
|
|
13
17
|
## Links
|
|
14
18
|
|
|
@@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
6
6
|
|
|
7
7
|
[tool.black]
|
|
8
8
|
target-version = [
|
|
9
|
-
"
|
|
9
|
+
"py39",
|
|
10
10
|
]
|
|
11
11
|
|
|
12
12
|
[tool.mypy]
|
|
@@ -32,7 +32,7 @@ warn_unused_configs = true
|
|
|
32
32
|
src = [
|
|
33
33
|
"src",
|
|
34
34
|
]
|
|
35
|
-
target-version = "
|
|
35
|
+
target-version = "py39"
|
|
36
36
|
|
|
37
37
|
[tool.ruff.lint]
|
|
38
38
|
select = [
|
|
@@ -126,7 +126,7 @@ skip_empty = true
|
|
|
126
126
|
|
|
127
127
|
[tool.poetry]
|
|
128
128
|
name = "pyutilkit"
|
|
129
|
-
version = "0.
|
|
129
|
+
version = "0.4.0"
|
|
130
130
|
description = "python's missing batteries"
|
|
131
131
|
authors = [
|
|
132
132
|
"Stephanos Kuma <stephanos@kuma.ai>",
|
|
@@ -149,7 +149,7 @@ classifiers = [
|
|
|
149
149
|
|
|
150
150
|
[tool.poetry.dependencies]
|
|
151
151
|
# python version
|
|
152
|
-
python = "^3.
|
|
152
|
+
python = "^3.9"
|
|
153
153
|
|
|
154
154
|
[tool.poetry.group.dev.dependencies]
|
|
155
155
|
ipdb = { version = "^0.13", python = "^3.10" }
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
from __future__ import annotations # py3.9: remove this line
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from time import perf_counter_ns
|
|
5
|
+
from types import TracebackType
|
|
6
|
+
from typing import TYPE_CHECKING, Any
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
from typing_extensions import Self # py3.10: import Self from typing
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass(frozen=True, order=True)
|
|
13
|
+
class Timing:
|
|
14
|
+
__slots__ = ("nanoseconds",) # py3.9: remove this line
|
|
15
|
+
|
|
16
|
+
nanoseconds: int
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
*,
|
|
21
|
+
days: int = 0,
|
|
22
|
+
seconds: int = 0,
|
|
23
|
+
milliseconds: int = 0,
|
|
24
|
+
microseconds: int = 0,
|
|
25
|
+
nanoseconds: int = 0,
|
|
26
|
+
) -> None:
|
|
27
|
+
total_nanoseconds = (
|
|
28
|
+
nanoseconds
|
|
29
|
+
+ 1000 * microseconds
|
|
30
|
+
+ 1_000_000 * milliseconds
|
|
31
|
+
+ 1_000_000_000 * seconds
|
|
32
|
+
+ 86_400_000_000_000 * days
|
|
33
|
+
)
|
|
34
|
+
object.__setattr__(self, "nanoseconds", total_nanoseconds)
|
|
35
|
+
|
|
36
|
+
def __str__(self) -> str:
|
|
37
|
+
if self.nanoseconds < 1000:
|
|
38
|
+
return f"{self.nanoseconds}ns"
|
|
39
|
+
microseconds = self.nanoseconds / 1000
|
|
40
|
+
if microseconds < 1000:
|
|
41
|
+
return f"{microseconds:.1f}µs"
|
|
42
|
+
milliseconds = microseconds / 1000
|
|
43
|
+
if milliseconds < 1000:
|
|
44
|
+
return f"{milliseconds:.1f}ms"
|
|
45
|
+
seconds = milliseconds / 1000
|
|
46
|
+
if seconds < 60:
|
|
47
|
+
return f"{seconds:.2f}s"
|
|
48
|
+
round_seconds = int(seconds)
|
|
49
|
+
minutes, seconds = divmod(round_seconds, 60)
|
|
50
|
+
hours, minutes = divmod(minutes, 60)
|
|
51
|
+
if hours < 24:
|
|
52
|
+
return f"{hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
53
|
+
days, hours = divmod(hours, 24)
|
|
54
|
+
return f"{days:,}d {hours:02d}:{minutes:02d}:{seconds:02d}"
|
|
55
|
+
|
|
56
|
+
def __bool__(self) -> bool:
|
|
57
|
+
return bool(self.nanoseconds)
|
|
58
|
+
|
|
59
|
+
def __add__(self, other: Any) -> Timing:
|
|
60
|
+
if not isinstance(other, Timing):
|
|
61
|
+
return NotImplemented
|
|
62
|
+
return Timing(nanoseconds=self.nanoseconds + other.nanoseconds)
|
|
63
|
+
|
|
64
|
+
def __radd__(self, other: Any) -> Timing:
|
|
65
|
+
if not isinstance(other, Timing):
|
|
66
|
+
return NotImplemented
|
|
67
|
+
return Timing(nanoseconds=self.nanoseconds + other.nanoseconds)
|
|
68
|
+
|
|
69
|
+
def __mul__(self, other: Any) -> Timing:
|
|
70
|
+
if not isinstance(other, int):
|
|
71
|
+
return NotImplemented
|
|
72
|
+
return Timing(nanoseconds=self.nanoseconds * other)
|
|
73
|
+
|
|
74
|
+
def __rmul__(self, other: Any) -> Timing:
|
|
75
|
+
if not isinstance(other, int):
|
|
76
|
+
return NotImplemented
|
|
77
|
+
return Timing(nanoseconds=self.nanoseconds * other)
|
|
78
|
+
|
|
79
|
+
def __floordiv__(self, other: Any) -> Timing:
|
|
80
|
+
if not isinstance(other, int):
|
|
81
|
+
return NotImplemented
|
|
82
|
+
return Timing(nanoseconds=self.nanoseconds // other)
|
|
83
|
+
|
|
84
|
+
def __truediv__(self, other: Any) -> Timing:
|
|
85
|
+
if not isinstance(other, int):
|
|
86
|
+
return NotImplemented
|
|
87
|
+
return Timing(nanoseconds=self.nanoseconds // other)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Stopwatch:
|
|
91
|
+
_start: int
|
|
92
|
+
laps: list[Timing]
|
|
93
|
+
|
|
94
|
+
def __init__(self) -> None:
|
|
95
|
+
self._start = 0
|
|
96
|
+
self.laps: list[Timing] = []
|
|
97
|
+
|
|
98
|
+
def __enter__(self) -> Self:
|
|
99
|
+
self._start = perf_counter_ns()
|
|
100
|
+
return self
|
|
101
|
+
|
|
102
|
+
def __exit__(
|
|
103
|
+
self,
|
|
104
|
+
exc_type: type[Exception] | None,
|
|
105
|
+
exc_value: Exception | None,
|
|
106
|
+
traceback: TracebackType | None,
|
|
107
|
+
) -> None:
|
|
108
|
+
_end = perf_counter_ns()
|
|
109
|
+
self.laps.append(Timing(nanoseconds=_end - self._start))
|
|
110
|
+
|
|
111
|
+
def __bool__(self) -> bool:
|
|
112
|
+
return bool(self.elapsed)
|
|
113
|
+
|
|
114
|
+
@property
|
|
115
|
+
def elapsed(self) -> Timing:
|
|
116
|
+
return sum(self.laps, Timing())
|
|
117
|
+
|
|
118
|
+
@property
|
|
119
|
+
def average(self) -> Timing:
|
|
120
|
+
if not self.laps:
|
|
121
|
+
msg = "No laps recorded"
|
|
122
|
+
raise ZeroDivisionError(msg)
|
|
123
|
+
return self.elapsed // len(self.laps)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.2.0"
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
from dataclasses import dataclass
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@dataclass(frozen=True, order=True)
|
|
5
|
-
class Timing:
|
|
6
|
-
__slots__ = ("nanoseconds",) # py3.9: remove this line
|
|
7
|
-
|
|
8
|
-
nanoseconds: int
|
|
9
|
-
|
|
10
|
-
def __init__(
|
|
11
|
-
self,
|
|
12
|
-
*,
|
|
13
|
-
days: int = 0,
|
|
14
|
-
seconds: int = 0,
|
|
15
|
-
milliseconds: int = 0,
|
|
16
|
-
microseconds: int = 0,
|
|
17
|
-
nanoseconds: int = 0,
|
|
18
|
-
) -> None:
|
|
19
|
-
total_nanoseconds = (
|
|
20
|
-
nanoseconds
|
|
21
|
-
+ 1000 * microseconds
|
|
22
|
-
+ 1_000_000 * milliseconds
|
|
23
|
-
+ 1_000_000_000 * seconds
|
|
24
|
-
+ 86_400_000_000_000 * days
|
|
25
|
-
)
|
|
26
|
-
object.__setattr__(self, "nanoseconds", total_nanoseconds)
|
|
27
|
-
|
|
28
|
-
def __str__(self) -> str:
|
|
29
|
-
if self.nanoseconds < 1000:
|
|
30
|
-
return f"{self.nanoseconds}ns"
|
|
31
|
-
microseconds = self.nanoseconds / 1000
|
|
32
|
-
if microseconds < 1000:
|
|
33
|
-
return f"{microseconds:.1f}µs"
|
|
34
|
-
milliseconds = microseconds / 1000
|
|
35
|
-
if milliseconds < 1000:
|
|
36
|
-
return f"{milliseconds:.1f}ms"
|
|
37
|
-
seconds = milliseconds / 1000
|
|
38
|
-
return f"{seconds:,.2f}s"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|