dycw-utilities 0.125.21__py3-none-any.whl → 0.125.23__py3-none-any.whl
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.
- {dycw_utilities-0.125.21.dist-info → dycw_utilities-0.125.23.dist-info}/METADATA +1 -1
- {dycw_utilities-0.125.21.dist-info → dycw_utilities-0.125.23.dist-info}/RECORD +7 -6
- utilities/__init__.py +1 -1
- utilities/asyncio.py +4 -0
- utilities/psutil.py +115 -0
- {dycw_utilities-0.125.21.dist-info → dycw_utilities-0.125.23.dist-info}/WHEEL +0 -0
- {dycw_utilities-0.125.21.dist-info → dycw_utilities-0.125.23.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
|
|
1
|
-
utilities/__init__.py,sha256=
|
1
|
+
utilities/__init__.py,sha256=wPKNBX6ygqFYs-IuHSilaZXm0vMIdcuqBGP9j1b_5O8,61
|
2
2
|
utilities/altair.py,sha256=Gpja-flOo-Db0PIPJLJsgzAlXWoKUjPU1qY-DQ829ek,9156
|
3
|
-
utilities/asyncio.py,sha256=
|
3
|
+
utilities/asyncio.py,sha256=wtMOjHG85exqdwVBNH-DazTprkUCvMFv-JgY-9REhYs,51259
|
4
4
|
utilities/atomicwrites.py,sha256=geFjn9Pwn-tTrtoGjDDxWli9NqbYfy3gGL6ZBctiqSo,5393
|
5
5
|
utilities/atools.py,sha256=IYMuFSFGSKyuQmqD6v5IUtDlz8PPw0Sr87Cub_gRU3M,1168
|
6
6
|
utilities/cachetools.py,sha256=C1zqOg7BYz0IfQFK8e3qaDDgEZxDpo47F15RTfJM37Q,2910
|
@@ -50,6 +50,7 @@ utilities/platform.py,sha256=48IOKx1IC6ZJXWG-b56ZQptITcNFhWRjELW72o2dGTA,2398
|
|
50
50
|
utilities/polars.py,sha256=QlmUpYTqHNkcLnWOQh1TW22W2QyLzvifCvBcbsqhpdE,63272
|
51
51
|
utilities/polars_ols.py,sha256=Uc9V5kvlWZ5cU93lKZ-cfAKdVFFw81tqwLW9PxtUvMs,5618
|
52
52
|
utilities/pqdm.py,sha256=foRytQybmOQ05pjt5LF7ANyzrIa--4ScDE3T2wd31a4,3118
|
53
|
+
utilities/psutil.py,sha256=0dXHKW7t9J3uzY3YtHGiRiZVRWr0uozccioZRCVFQfk,3761
|
53
54
|
utilities/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
54
55
|
utilities/pydantic.py,sha256=f6qtR5mO2YMuyvNmbaEj5YeD9eGA4YYfb7Bjzh9jUs0,1845
|
55
56
|
utilities/pyinstrument.py,sha256=OJFDh4o1CWIa4aYPYURdQjgap_nvP45KUsCEe94rQHY,829
|
@@ -89,7 +90,7 @@ utilities/warnings.py,sha256=un1LvHv70PU-LLv8RxPVmugTzDJkkGXRMZTE2-fTQHw,1771
|
|
89
90
|
utilities/whenever.py,sha256=jS31ZAY5OMxFxLja_Yo5Fidi87Pd-GoVZ7Vi_teqVDA,16743
|
90
91
|
utilities/zipfile.py,sha256=24lQc9ATcJxHXBPc_tBDiJk48pWyRrlxO2fIsFxU0A8,699
|
91
92
|
utilities/zoneinfo.py,sha256=-5j7IQ9nb7gR43rdgA7ms05im-XuqhAk9EJnQBXxCoQ,1874
|
92
|
-
dycw_utilities-0.125.
|
93
|
-
dycw_utilities-0.125.
|
94
|
-
dycw_utilities-0.125.
|
95
|
-
dycw_utilities-0.125.
|
93
|
+
dycw_utilities-0.125.23.dist-info/METADATA,sha256=sTzpB8MuDKbN3vHMg9otGuFDqzQ6Ko-_iYSb_9W94QY,12852
|
94
|
+
dycw_utilities-0.125.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
95
|
+
dycw_utilities-0.125.23.dist-info/licenses/LICENSE,sha256=gppZp16M6nSVpBbUBrNL6JuYfvKwZiKgV7XoKKsHzqo,1066
|
96
|
+
dycw_utilities-0.125.23.dist-info/RECORD,,
|
utilities/__init__.py
CHANGED
utilities/asyncio.py
CHANGED
@@ -895,6 +895,8 @@ class Looper(Generic[_T]):
|
|
895
895
|
backoff: Duration | Sentinel = sentinel,
|
896
896
|
logger: str | None | Sentinel = sentinel,
|
897
897
|
timeout: Duration | None | Sentinel = sentinel,
|
898
|
+
timeout_error: type[Exception] | Sentinel = sentinel,
|
899
|
+
_debug: bool | Sentinel = sentinel,
|
898
900
|
) -> Self:
|
899
901
|
"""Replace elements of the looper."""
|
900
902
|
return replace_non_sentinel(
|
@@ -904,6 +906,8 @@ class Looper(Generic[_T]):
|
|
904
906
|
backoff=backoff,
|
905
907
|
logger=logger,
|
906
908
|
timeout=timeout,
|
909
|
+
timeout_error=timeout_error,
|
910
|
+
_debug=_debug,
|
907
911
|
)
|
908
912
|
|
909
913
|
def request_restart(self) -> None:
|
utilities/psutil.py
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from dataclasses import dataclass, field
|
4
|
+
from json import dumps
|
5
|
+
from logging import getLogger
|
6
|
+
from math import isclose, nan
|
7
|
+
from pathlib import Path
|
8
|
+
from typing import TYPE_CHECKING, Self, override
|
9
|
+
|
10
|
+
from psutil import swap_memory, virtual_memory
|
11
|
+
|
12
|
+
from utilities.asyncio import Looper
|
13
|
+
from utilities.contextlib import suppress_super_object_attribute_error
|
14
|
+
from utilities.datetime import SECOND, get_now
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
import datetime as dt
|
18
|
+
from logging import Logger
|
19
|
+
|
20
|
+
from utilities.types import Duration, PathLike
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass(kw_only=True)
|
24
|
+
class MemoryMonitorService(Looper[None]):
|
25
|
+
"""Service to monitor memory usage."""
|
26
|
+
|
27
|
+
# base
|
28
|
+
freq: Duration = field(default=10 * SECOND, repr=False)
|
29
|
+
backoff: Duration = field(default=10 * SECOND, repr=False)
|
30
|
+
# self
|
31
|
+
console: str | None = field(default=None, repr=False)
|
32
|
+
path: PathLike = "memory.txt"
|
33
|
+
_console: Logger | None = field(init=False, repr=False)
|
34
|
+
_max_age: int | None = field(default=None, init=False, repr=False)
|
35
|
+
_path: Path = field(init=False, repr=False)
|
36
|
+
|
37
|
+
@override
|
38
|
+
def __post_init__(self) -> None:
|
39
|
+
super().__post_init__()
|
40
|
+
if self.console is not None:
|
41
|
+
self._console = getLogger(self.console)
|
42
|
+
self._path = Path(self.path)
|
43
|
+
|
44
|
+
@override
|
45
|
+
async def core(self) -> None:
|
46
|
+
await super().core()
|
47
|
+
memory = MemoryUsage.new()
|
48
|
+
mapping = {
|
49
|
+
"datetime": memory.datetime.strftime("%Y-%m-%d %H:%M:%S"),
|
50
|
+
"virtual used (mb)": memory.virtual_used_mb,
|
51
|
+
"virtual total (mb)": memory.virtual_total_mb,
|
52
|
+
"virtual (%)": memory.virtual_pct,
|
53
|
+
"swap used (mb)": memory.swap_used_mb,
|
54
|
+
"swap total (mb)": memory.swap_total_mb,
|
55
|
+
"swap (%)": memory.swap_pct,
|
56
|
+
}
|
57
|
+
ser = dumps(mapping)
|
58
|
+
with self._path.open(mode="a") as fh:
|
59
|
+
_ = fh.write(f"{ser}\n")
|
60
|
+
if self._console is not None:
|
61
|
+
self._console.info("%s", mapping)
|
62
|
+
|
63
|
+
|
64
|
+
##
|
65
|
+
|
66
|
+
|
67
|
+
@dataclass(kw_only=True)
|
68
|
+
class MemoryUsage:
|
69
|
+
"""A memory usage."""
|
70
|
+
|
71
|
+
datetime: dt.datetime = field(default_factory=get_now)
|
72
|
+
virtual_used: int = field(repr=False)
|
73
|
+
virtual_used_mb: int = field(init=False)
|
74
|
+
virtual_total: int = field(repr=False)
|
75
|
+
virtual_total_mb: int = field(init=False)
|
76
|
+
virtual_pct: float = field(init=False)
|
77
|
+
swap_used: int = field(repr=False)
|
78
|
+
swap_used_mb: int = field(init=False)
|
79
|
+
swap_total: int = field(repr=False)
|
80
|
+
swap_total_mb: int = field(init=False)
|
81
|
+
swap_pct: float = field(init=False)
|
82
|
+
|
83
|
+
def __post_init__(self) -> None:
|
84
|
+
with suppress_super_object_attribute_error():
|
85
|
+
super().__post_init__() # pyright: ignore[reportAttributeAccessIssue]
|
86
|
+
self.virtual_used_mb = self._to_mb(self.virtual_used)
|
87
|
+
self.virtual_total_mb = self._to_mb(self.virtual_total)
|
88
|
+
self.virtual_pct = (
|
89
|
+
nan
|
90
|
+
if isclose(self.virtual_total, 0.0)
|
91
|
+
else self.virtual_used / self.virtual_total
|
92
|
+
)
|
93
|
+
self.swap_used_mb = self._to_mb(self.swap_used)
|
94
|
+
self.swap_total_mb = self._to_mb(self.swap_total)
|
95
|
+
self.swap_pct = (
|
96
|
+
nan if isclose(self.swap_total, 0.0) else self.swap_used / self.swap_total
|
97
|
+
)
|
98
|
+
|
99
|
+
@classmethod
|
100
|
+
def new(cls) -> Self:
|
101
|
+
virtual = virtual_memory()
|
102
|
+
virtual_total = virtual.total
|
103
|
+
swap = swap_memory()
|
104
|
+
return cls(
|
105
|
+
virtual_used=virtual_total - virtual.available,
|
106
|
+
virtual_total=virtual_total,
|
107
|
+
swap_used=swap.used,
|
108
|
+
swap_total=swap.total,
|
109
|
+
)
|
110
|
+
|
111
|
+
def _to_mb(self, bytes_: int) -> int:
|
112
|
+
return round(bytes_ / (1024**2))
|
113
|
+
|
114
|
+
|
115
|
+
__all__ = ["MemoryMonitorService", "MemoryUsage"]
|
File without changes
|
File without changes
|