bear-utils 0.7.21__py3-none-any.whl → 0.7.22__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.
- bear_utils/__init__.py +24 -1
- bear_utils/ai/__init__.py +5 -5
- bear_utils/ai/ai_helpers/__init__.py +24 -18
- bear_utils/ai/ai_helpers/_parsers.py +27 -21
- bear_utils/ai/ai_helpers/_types.py +2 -7
- bear_utils/cache/__init__.py +35 -23
- bear_utils/cli/__init__.py +13 -0
- bear_utils/cli/commands.py +14 -8
- bear_utils/cli/prompt_helpers.py +40 -34
- bear_utils/cli/shell/__init__.py +1 -0
- bear_utils/cli/shell/_base_command.py +17 -18
- bear_utils/cli/shell/_base_shell.py +37 -34
- bear_utils/config/__init__.py +4 -2
- bear_utils/config/config_manager.py +193 -56
- bear_utils/config/dir_manager.py +8 -3
- bear_utils/config/settings_manager.py +94 -171
- bear_utils/constants/__init__.py +2 -1
- bear_utils/constants/_exceptions.py +6 -1
- bear_utils/constants/date_related.py +2 -0
- bear_utils/constants/logger_protocol.py +28 -0
- bear_utils/constants/time_related.py +2 -0
- bear_utils/database/__init__.py +2 -0
- bear_utils/database/_db_manager.py +10 -11
- bear_utils/events/__init__.py +3 -1
- bear_utils/events/events_class.py +11 -11
- bear_utils/events/events_module.py +17 -8
- bear_utils/extras/__init__.py +8 -6
- bear_utils/extras/_async_helpers.py +2 -3
- bear_utils/extras/_tools.py +54 -52
- bear_utils/extras/platform_utils.py +5 -1
- bear_utils/extras/responses/__init__.py +1 -0
- bear_utils/extras/responses/function_response.py +301 -0
- bear_utils/extras/wrappers/__init__.py +1 -0
- bear_utils/extras/wrappers/add_methods.py +17 -15
- bear_utils/files/__init__.py +3 -1
- bear_utils/files/file_handlers/__init__.py +2 -0
- bear_utils/files/file_handlers/_base_file_handler.py +23 -3
- bear_utils/files/file_handlers/file_handler_factory.py +38 -38
- bear_utils/files/file_handlers/json_file_handler.py +49 -22
- bear_utils/files/file_handlers/log_file_handler.py +19 -12
- bear_utils/files/file_handlers/toml_file_handler.py +13 -5
- bear_utils/files/file_handlers/txt_file_handler.py +56 -14
- bear_utils/files/file_handlers/yaml_file_handler.py +19 -13
- bear_utils/files/ignore_parser.py +52 -57
- bear_utils/graphics/__init__.py +3 -1
- bear_utils/graphics/bear_gradient.py +17 -12
- bear_utils/graphics/image_helpers.py +11 -5
- bear_utils/gui/__init__.py +3 -1
- bear_utils/gui/gui_tools/__init__.py +3 -1
- bear_utils/gui/gui_tools/_settings.py +0 -1
- bear_utils/gui/gui_tools/qt_app.py +16 -11
- bear_utils/gui/gui_tools/qt_color_picker.py +24 -13
- bear_utils/gui/gui_tools/qt_file_handler.py +30 -38
- bear_utils/gui/gui_tools/qt_input_dialog.py +11 -14
- bear_utils/logging/__init__.py +6 -4
- bear_utils/logging/logger_manager/__init__.py +1 -0
- bear_utils/logging/logger_manager/_common.py +0 -1
- bear_utils/logging/logger_manager/_console_junk.py +15 -11
- bear_utils/logging/logger_manager/_styles.py +1 -2
- bear_utils/logging/logger_manager/loggers/__init__.py +1 -0
- bear_utils/logging/logger_manager/loggers/_base_logger.py +33 -33
- bear_utils/logging/logger_manager/loggers/_base_logger.pyi +6 -5
- bear_utils/logging/logger_manager/loggers/_buffer_logger.py +2 -3
- bear_utils/logging/logger_manager/loggers/_console_logger.py +54 -26
- bear_utils/logging/logger_manager/loggers/_console_logger.pyi +7 -21
- bear_utils/logging/logger_manager/loggers/_file_logger.py +20 -13
- bear_utils/logging/logger_manager/loggers/_level_sin.py +15 -15
- bear_utils/logging/logger_manager/loggers/_logger.py +4 -6
- bear_utils/logging/logger_manager/loggers/_sub_logger.py +16 -23
- bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +4 -19
- bear_utils/logging/loggers.py +9 -13
- bear_utils/monitoring/__init__.py +7 -4
- bear_utils/monitoring/_common.py +28 -0
- bear_utils/monitoring/host_monitor.py +44 -48
- bear_utils/time/__init__.py +13 -6
- {bear_utils-0.7.21.dist-info → bear_utils-0.7.22.dist-info}/METADATA +50 -6
- bear_utils-0.7.22.dist-info/RECORD +83 -0
- bear_utils-0.7.21.dist-info/RECORD +0 -79
- {bear_utils-0.7.21.dist-info → bear_utils-0.7.22.dist-info}/WHEEL +0 -0
@@ -1,206 +1,140 @@
|
|
1
|
+
"""Settings Manager Module for Bear Utils."""
|
2
|
+
|
1
3
|
import atexit
|
2
|
-
from
|
4
|
+
from collections.abc import Generator
|
3
5
|
from contextlib import contextmanager
|
4
6
|
from pathlib import Path
|
5
|
-
from
|
6
|
-
from typing import Any
|
7
|
+
from typing import Any, Self
|
7
8
|
|
8
|
-
from
|
9
|
+
from tinydb import Query, TinyDB
|
9
10
|
|
10
11
|
|
11
12
|
def get_bear_config_path() -> Path:
|
12
13
|
"""Get the path to the bear configuration path"""
|
13
|
-
path = Path.home() / ".config" / "bear_utils"
|
14
|
-
|
15
|
-
path.mkdir(parents=True, exist_ok=True)
|
14
|
+
path: Path = Path.home() / ".config" / "bear_utils"
|
15
|
+
path.mkdir(parents=True, exist_ok=True)
|
16
16
|
return path
|
17
17
|
|
18
18
|
|
19
|
-
def get_config_file_path(filename: str) -> Path:
|
20
|
-
"""Get the path to a specific configuration file in the bear configuration path"""
|
21
|
-
config_path = get_bear_config_path()
|
22
|
-
return config_path / filename
|
23
|
-
|
24
|
-
|
25
|
-
class SettingsCache(Namespace):
|
26
|
-
"""A Namespace to hold settings in memory."""
|
27
|
-
|
28
|
-
def __init__(self):
|
29
|
-
super().__init__()
|
30
|
-
|
31
|
-
def clear(self):
|
32
|
-
"""Clear the settings cache."""
|
33
|
-
self.__dict__.clear()
|
34
|
-
|
35
|
-
def set(self, key: str, value):
|
36
|
-
"""Set a value in the settings cache."""
|
37
|
-
setattr(self, key, value)
|
38
|
-
|
39
|
-
def get(self, key: str, default=None):
|
40
|
-
"""Get a value from the settings cache."""
|
41
|
-
return getattr(self, key, default)
|
42
|
-
|
43
|
-
def has(self, key: str) -> bool:
|
44
|
-
"""Check if a key exists in the settings cache."""
|
45
|
-
return hasattr(self, key)
|
46
|
-
|
47
|
-
|
48
19
|
class SettingsManager:
|
49
|
-
|
50
|
-
shelf: Shelf
|
51
|
-
settings_cache: SettingsCache
|
20
|
+
"""A class to manage settings using TinyDB and an in-memory cache."""
|
52
21
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
22
|
+
__slots__ = ("cache", "db", "file_path", "settings_name")
|
23
|
+
|
24
|
+
def __init__(self, settings_name: str) -> None:
|
25
|
+
"""Initialize the SettingsManager with a specific settings name."""
|
26
|
+
self.settings_name = settings_name
|
27
|
+
self.cache: dict[str, Any] = {}
|
28
|
+
self.file_path: Path = get_bear_config_path() / f"{settings_name}.json"
|
29
|
+
self.db: TinyDB = TinyDB(self.file_path, indent=4, ensure_ascii=False)
|
58
30
|
|
59
31
|
atexit.register(self.close)
|
60
|
-
self.
|
32
|
+
self._load_cache()
|
61
33
|
|
62
|
-
def __getattr__(self, key: str):
|
34
|
+
def __getattr__(self, key: str) -> Any:
|
63
35
|
"""Handle dot notation access for settings."""
|
36
|
+
if key in self.__slots__:
|
37
|
+
raise AttributeError(f"'{key}' not initialized")
|
64
38
|
if key.startswith("_"):
|
65
39
|
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{key}'")
|
66
40
|
return self.get(key)
|
67
41
|
|
68
|
-
def __setattr__(self, key: str, value):
|
42
|
+
def __setattr__(self, key: str, value: Any) -> None:
|
69
43
|
"""Handle dot notation assignment for settings."""
|
70
|
-
if
|
44
|
+
if key in self.__slots__:
|
71
45
|
object.__setattr__(self, key, value)
|
72
46
|
return
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
self.
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
return default
|
90
|
-
|
91
|
-
def set(self, key: str, value):
|
92
|
-
"""Set a setting value by key."""
|
93
|
-
self.shelf[key] = value
|
94
|
-
self.settings_cache.set(key, value)
|
47
|
+
self.set(key=key, value=value)
|
48
|
+
|
49
|
+
def get(self, key: str, default: Any = None) -> Any:
|
50
|
+
"""Get a setting value."""
|
51
|
+
if key in self.cache:
|
52
|
+
return self.cache[key]
|
53
|
+
if result := self.db.search(Query().key == key):
|
54
|
+
value = result[0]["value"]
|
55
|
+
self.cache[key] = value
|
56
|
+
return value
|
57
|
+
return default
|
58
|
+
|
59
|
+
def set(self, key: str, value: Any) -> None:
|
60
|
+
"""Set a setting value."""
|
61
|
+
self.db.upsert({"key": key, "value": value}, Query().key == key)
|
62
|
+
self.cache[key] = value
|
95
63
|
|
96
64
|
def has(self, key: str) -> bool:
|
97
|
-
"""Check if a setting exists
|
98
|
-
return self.
|
99
|
-
|
100
|
-
def
|
101
|
-
"""
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
def _load_existing_settings(self):
|
119
|
-
"""Load existing settings from shelf into namespace."""
|
120
|
-
for key in self.shelf:
|
121
|
-
if not self.settings_cache.has(key):
|
122
|
-
self.settings_cache.set(key, self.shelf[key])
|
123
|
-
|
124
|
-
def close(self):
|
125
|
-
"""Close the settings file."""
|
126
|
-
if self.shelf is not None:
|
127
|
-
self.shelf.close()
|
128
|
-
if self.settings_cache is not None:
|
129
|
-
self.settings_cache.clear()
|
65
|
+
"""Check if a setting exists."""
|
66
|
+
return key in self.cache or self.db.contains(Query().key == key)
|
67
|
+
|
68
|
+
def _load_cache(self) -> None:
|
69
|
+
"""Load all settings into cache."""
|
70
|
+
for record in self.db.all():
|
71
|
+
self.cache[record["key"]] = record["value"]
|
72
|
+
|
73
|
+
def open(self) -> None:
|
74
|
+
"""Reopen the settings file after it's been closed/destroyed."""
|
75
|
+
self.db = TinyDB(self.file_path, indent=4, ensure_ascii=False)
|
76
|
+
self.cache = {}
|
77
|
+
self._load_cache()
|
78
|
+
|
79
|
+
def close(self) -> None:
|
80
|
+
"""Close the database."""
|
81
|
+
if hasattr(self, "db"):
|
82
|
+
self.db.close()
|
83
|
+
if hasattr(self, "cache"):
|
84
|
+
self.cache.clear()
|
130
85
|
|
131
86
|
def destroy_settings(self) -> bool:
|
132
|
-
"""Delete the settings file
|
133
|
-
|
134
|
-
if file_path.exists():
|
87
|
+
"""Delete the settings file."""
|
88
|
+
if self.file_path.exists():
|
135
89
|
self.close()
|
136
|
-
file_path.unlink()
|
137
|
-
self.
|
138
|
-
object.__setattr__(self, "shelf", None)
|
90
|
+
self.file_path.unlink()
|
91
|
+
self.cache.clear()
|
139
92
|
return True
|
140
93
|
return False
|
141
94
|
|
142
|
-
def __del__(self):
|
143
|
-
"""Destructor to ensure the shelf is closed."""
|
144
|
-
self.close()
|
145
|
-
|
146
|
-
def __enter__(self):
|
147
|
-
"""Context manager entry."""
|
148
|
-
return self
|
149
|
-
|
150
|
-
def __exit__(self, exc_type, exc_val, exc_tb):
|
151
|
-
"""Context manager exit."""
|
152
|
-
self.close()
|
153
|
-
|
154
95
|
def __contains__(self, key: str) -> bool:
|
155
|
-
"""Check if a setting exists."""
|
156
96
|
return self.has(key)
|
157
97
|
|
158
98
|
def keys(self) -> list[str]:
|
159
99
|
"""Get all setting keys."""
|
160
|
-
return list(
|
100
|
+
return list(self.cache.keys())
|
161
101
|
|
162
|
-
def items(self):
|
102
|
+
def items(self) -> list[tuple[str, Any]]:
|
163
103
|
"""Get all setting key-value pairs."""
|
164
|
-
|
165
|
-
yield key, self.settings_cache.get(key)
|
104
|
+
return list(self.cache.items())
|
166
105
|
|
167
|
-
def values(self):
|
106
|
+
def values(self) -> list[Any]:
|
168
107
|
"""Get all setting values."""
|
169
|
-
|
170
|
-
yield self.settings_cache.get(key)
|
108
|
+
return list(self.cache.values())
|
171
109
|
|
172
|
-
def
|
173
|
-
|
174
|
-
return f"<SettingsManager settings_name='{self.settings_name}'>"
|
110
|
+
def __len__(self):
|
111
|
+
return len(self.cache)
|
175
112
|
|
176
|
-
def
|
177
|
-
|
178
|
-
return f"SettingsManager for '{self.settings_name}' with {len(self.keys())} settings."
|
113
|
+
def __enter__(self) -> Self:
|
114
|
+
return self
|
179
115
|
|
180
|
-
def
|
181
|
-
|
182
|
-
|
116
|
+
def __exit__(self, exc_type: object, exc_val: object, exc_tb: object) -> None:
|
117
|
+
self.close()
|
118
|
+
|
119
|
+
def __repr__(self) -> str:
|
120
|
+
return f"<SettingsManager settings_name='{self.settings_name}'>"
|
183
121
|
|
122
|
+
def __str__(self) -> str:
|
123
|
+
return f"SettingsManager for '{self.settings_name}' with {len(self.keys())} settings."
|
184
124
|
|
185
|
-
class SettingsSupervisor(SingletonBase):
|
186
|
-
def __init__(self):
|
187
|
-
self._settings_managers = {}
|
188
125
|
|
189
|
-
|
190
|
-
"""Get or create a SettingsManager instance."""
|
191
|
-
if settings_name in self._settings_managers:
|
192
|
-
return self._settings_managers[settings_name]
|
193
|
-
return SettingsManager(settings_name)
|
126
|
+
_settings_managers: dict[str, SettingsManager] = {}
|
194
127
|
|
195
128
|
|
196
129
|
def get_settings_manager(settings_name: str) -> SettingsManager:
|
197
|
-
"""Get
|
198
|
-
|
199
|
-
|
130
|
+
"""Get or create a SettingsManager instance."""
|
131
|
+
if settings_name not in _settings_managers:
|
132
|
+
_settings_managers[settings_name] = SettingsManager(settings_name=settings_name)
|
133
|
+
return _settings_managers[settings_name]
|
200
134
|
|
201
135
|
|
202
136
|
@contextmanager
|
203
|
-
def settings(settings_name: str):
|
137
|
+
def settings(settings_name: str) -> Generator[SettingsManager]:
|
204
138
|
"""Context manager for SettingsManager."""
|
205
139
|
sm: SettingsManager = get_settings_manager(settings_name)
|
206
140
|
try:
|
@@ -209,24 +143,13 @@ def settings(settings_name: str):
|
|
209
143
|
sm.close()
|
210
144
|
|
211
145
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
# for value in sm.values():
|
223
|
-
# print("This is values()")
|
224
|
-
# print(value)
|
225
|
-
# print(len(sm))
|
226
|
-
# print(sm)
|
227
|
-
# if sm.destroy_settings():
|
228
|
-
# print("Settings destroyed successfully.")
|
229
|
-
# sm.open()
|
230
|
-
# print(sm.keys())
|
231
|
-
# sm.test = "Test setting"
|
232
|
-
# print(len(sm))
|
146
|
+
if __name__ == "__main__":
|
147
|
+
# Example usage of the SettingsManager
|
148
|
+
with settings(settings_name="example_settings") as sm:
|
149
|
+
sm.sample_setting = "This is a sample setting"
|
150
|
+
print(sm.sample_setting)
|
151
|
+
print(sm.keys())
|
152
|
+
for key, value in sm.items():
|
153
|
+
print("This is items()")
|
154
|
+
print(f"Key: {key}, Value: {value}")
|
155
|
+
print(sm.file_path)
|
bear_utils/constants/__init__.py
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
-
|
1
|
+
"""Custom exceptions for the application."""
|
2
|
+
|
3
|
+
|
4
|
+
class UserCancelledError(Exception):
|
5
|
+
"""Exception raised when a user cancels an operation."""
|
6
|
+
|
2
7
|
def __init__(self, message: str = "User cancelled the operation"):
|
3
8
|
super().__init__(message)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
"""A protocol for logging classes for general use."""
|
2
|
+
|
3
|
+
from typing import Protocol, runtime_checkable
|
4
|
+
|
5
|
+
|
6
|
+
@runtime_checkable
|
7
|
+
class LoggerProtocol(Protocol):
|
8
|
+
"""A protocol for logging classes."""
|
9
|
+
|
10
|
+
def debug(self, message: object, *args, **kwargs) -> None:
|
11
|
+
"""Log a debug message."""
|
12
|
+
...
|
13
|
+
|
14
|
+
def info(self, message: object, *args, **kwargs) -> None:
|
15
|
+
"""Log an info message."""
|
16
|
+
...
|
17
|
+
|
18
|
+
def warning(self, message: object, *args, **kwargs) -> None:
|
19
|
+
"""Log a warning message."""
|
20
|
+
...
|
21
|
+
|
22
|
+
def error(self, message: object, *args, **kwargs) -> None:
|
23
|
+
"""Log an error message."""
|
24
|
+
...
|
25
|
+
|
26
|
+
def verbose(self, message: object, *args, **kwargs) -> None:
|
27
|
+
"""Log a verbose message."""
|
28
|
+
...
|
bear_utils/database/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""Database Manager Module for managing database connections and operations."""
|
2
|
+
|
1
3
|
import atexit
|
2
4
|
from collections.abc import Generator
|
3
5
|
from contextlib import contextmanager
|
@@ -10,7 +12,7 @@ from sqlalchemy.ext.declarative import DeclarativeMeta
|
|
10
12
|
from sqlalchemy.orm import declarative_base, scoped_session, sessionmaker
|
11
13
|
from sqlalchemy.orm.session import Session
|
12
14
|
|
13
|
-
from
|
15
|
+
from bear_utils.constants._lazy_typing import TableType
|
14
16
|
|
15
17
|
|
16
18
|
class DatabaseManager:
|
@@ -45,8 +47,7 @@ class DatabaseManager:
|
|
45
47
|
def get_all_records(self, table_obj: type[TableType]) -> list[TableType]:
|
46
48
|
"""Get all records from a table."""
|
47
49
|
with self.open_session() as session:
|
48
|
-
|
49
|
-
return records
|
50
|
+
return session.query(table_obj).all()
|
50
51
|
|
51
52
|
def count_records(self, table_obj: type[TableType]) -> int:
|
52
53
|
"""Count the number of records in a table."""
|
@@ -54,20 +55,20 @@ class DatabaseManager:
|
|
54
55
|
count: int = session.query(table_obj).count()
|
55
56
|
return count
|
56
57
|
|
57
|
-
def get_records_by_var(self, table_obj: type[TableType], variable: str, value) -> list[TableType]:
|
58
|
+
def get_records_by_var(self, table_obj: type[TableType], variable: str, value: str) -> list[TableType]:
|
58
59
|
"""Get records from a table by a specific variable."""
|
59
60
|
with self.open_session() as session:
|
60
61
|
records: list[TableType] = session.query(table_obj).filter(getattr(table_obj, variable) == value).all()
|
61
62
|
return records
|
62
63
|
|
63
|
-
def count_records_by_var(self, table_obj: type[TableType], variable: str, value) -> int:
|
64
|
+
def count_records_by_var(self, table_obj: type[TableType], variable: str, value: str) -> int:
|
64
65
|
"""Count the number of records in a table by a specific variable."""
|
65
66
|
with self.open_session() as session:
|
66
67
|
count: int = session.query(table_obj).filter(getattr(table_obj, variable) == value).count()
|
67
68
|
return count
|
68
69
|
|
69
70
|
@contextmanager
|
70
|
-
def open_session(self) -> Generator[Session, Any
|
71
|
+
def open_session(self) -> Generator[Session, Any]:
|
71
72
|
"""Provide a transactional scope around a series of operations."""
|
72
73
|
session: Session = self.session()
|
73
74
|
try:
|
@@ -81,15 +82,15 @@ class DatabaseManager:
|
|
81
82
|
"""Get a new session."""
|
82
83
|
return self.session()
|
83
84
|
|
84
|
-
def close_session(self):
|
85
|
+
def close_session(self) -> None:
|
85
86
|
"""Close the session."""
|
86
87
|
self.session.remove()
|
87
88
|
|
88
|
-
def create_tables(self):
|
89
|
+
def create_tables(self) -> None:
|
89
90
|
"""Create all tables defined by Base"""
|
90
91
|
self.metadata.create_all(self.engine)
|
91
92
|
|
92
|
-
def close_all(self):
|
93
|
+
def close_all(self) -> None:
|
93
94
|
"""Close all sessions and connections."""
|
94
95
|
self.session.close()
|
95
96
|
self.engine.dispose()
|
@@ -98,7 +99,5 @@ class DatabaseManager:
|
|
98
99
|
class SingletonDB(DatabaseManager, SingletonBase):
|
99
100
|
"""Singleton class for DatabaseManager, uses SingletonBase to inject singleton pattern."""
|
100
101
|
|
101
|
-
...
|
102
|
-
|
103
102
|
|
104
103
|
__all__ = ["DatabaseManager", "SingletonDB"]
|
bear_utils/events/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""A module for event handling in Bear Utils."""
|
2
|
+
|
1
3
|
from .events_class import Events
|
2
4
|
from .events_module import clear_all, clear_handlers_for_event, dispatch_event, event_handler, set_handler
|
3
5
|
|
@@ -10,7 +12,7 @@ __all__ = [
|
|
10
12
|
"clear_handlers_for_event",
|
11
13
|
"dispatch_event",
|
12
14
|
"event_handler",
|
15
|
+
"publish",
|
13
16
|
"set_handler",
|
14
17
|
"subscribe",
|
15
|
-
"publish",
|
16
18
|
]
|
@@ -1,11 +1,15 @@
|
|
1
|
+
"""A module for event handling in Bear Utils."""
|
2
|
+
|
1
3
|
from collections.abc import Callable
|
2
4
|
from typing import Any
|
3
5
|
|
4
|
-
from .events_module import
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
from .events_module import (
|
7
|
+
clear_all as _clear_all,
|
8
|
+
clear_handlers_for_event as _clear_handlers_for_event,
|
9
|
+
dispatch_event as _dispatch_event,
|
10
|
+
event_handler as _event_handler,
|
11
|
+
set_handler as _set_handler,
|
12
|
+
)
|
9
13
|
|
10
14
|
Callback = Callable[..., Any]
|
11
15
|
|
@@ -18,8 +22,8 @@ class Events:
|
|
18
22
|
def event_handler(self, event_name: str, func: Callback | None = None):
|
19
23
|
"""Register ``func`` as a handler for ``event_name``.
|
20
24
|
|
21
|
-
Can be used as a decorator when ``func`` is omitted.
|
22
|
-
|
25
|
+
Can be used as a decorator when ``func`` is omitted.
|
26
|
+
"""
|
23
27
|
if func is None:
|
24
28
|
return _event_handler(event_name)
|
25
29
|
_set_handler(event_name, func)
|
@@ -27,22 +31,18 @@ class Events:
|
|
27
31
|
|
28
32
|
def dispatch_event(self, event_name: str, *args, **kwargs) -> Any | None:
|
29
33
|
"""Dispatch ``event_name`` to all subscribed handlers."""
|
30
|
-
|
31
34
|
return _dispatch_event(event_name, *args, **kwargs)
|
32
35
|
|
33
36
|
def set_handler(self, event_name: str, func: Callback) -> None:
|
34
37
|
"""Register ``func`` as a handler for ``event_name``."""
|
35
|
-
|
36
38
|
_set_handler(event_name, func)
|
37
39
|
|
38
40
|
def clear_handlers_for_event(self, event_name: str) -> None:
|
39
41
|
"""Remove all handlers associated with ``event_name``."""
|
40
|
-
|
41
42
|
_clear_handlers_for_event(event_name)
|
42
43
|
|
43
44
|
def clear_all(self) -> None:
|
44
45
|
"""Remove all registered event handlers."""
|
45
|
-
|
46
46
|
_clear_all()
|
47
47
|
|
48
48
|
subscribe = event_handler
|
@@ -1,24 +1,28 @@
|
|
1
|
+
"""Event handling module for Bear Utils."""
|
2
|
+
|
1
3
|
import asyncio
|
2
|
-
import weakref
|
3
4
|
from collections import defaultdict
|
4
5
|
from collections.abc import Callable
|
5
6
|
from functools import wraps
|
6
7
|
from types import MethodType
|
7
|
-
from typing import Any
|
8
|
+
from typing import Any
|
9
|
+
import weakref
|
8
10
|
from weakref import WeakMethod, ref
|
9
11
|
|
10
|
-
from
|
12
|
+
from bear_utils.extras._async_helpers import is_async_function
|
11
13
|
|
12
|
-
Callback
|
14
|
+
Callback = Callable[..., Any]
|
13
15
|
|
14
16
|
_event_registry: dict[str, weakref.WeakSet[Callback]] = defaultdict(weakref.WeakSet)
|
15
17
|
|
16
18
|
|
17
19
|
def clear_handlers_for_event(event_name: str) -> None:
|
20
|
+
"""Remove all handlers associated with a specific event."""
|
18
21
|
_event_registry.pop(event_name, None)
|
19
22
|
|
20
23
|
|
21
24
|
def clear_all() -> None:
|
25
|
+
"""Remove all registered event handlers."""
|
22
26
|
_event_registry.clear()
|
23
27
|
|
24
28
|
|
@@ -34,6 +38,7 @@ def _make_callback(name: str) -> Callable[[Any], None]:
|
|
34
38
|
|
35
39
|
|
36
40
|
def set_handler(name: str, func: Callback) -> None:
|
41
|
+
"""Register a function as a handler for a specific event."""
|
37
42
|
if isinstance(func, MethodType):
|
38
43
|
_event_registry[name].add(WeakMethod(func, _make_callback(name)))
|
39
44
|
else:
|
@@ -41,12 +46,13 @@ def set_handler(name: str, func: Callback) -> None:
|
|
41
46
|
|
42
47
|
|
43
48
|
def dispatch_event(name: str, *args, **kwargs) -> Any | None:
|
44
|
-
|
49
|
+
"""Dispatch an event to all registered handlers."""
|
50
|
+
results: list[Any] = []
|
45
51
|
for func in _event_registry.get(name, []):
|
46
52
|
if is_async_function(func):
|
47
|
-
result = asyncio.run(func(*args, **kwargs))
|
53
|
+
result: Any = asyncio.run(func(*args, **kwargs))
|
48
54
|
else:
|
49
|
-
result = func(*args, **kwargs)
|
55
|
+
result: Any = func(*args, **kwargs)
|
50
56
|
results.append(result)
|
51
57
|
if not results:
|
52
58
|
return None
|
@@ -54,9 +60,12 @@ def dispatch_event(name: str, *args, **kwargs) -> Any | None:
|
|
54
60
|
|
55
61
|
|
56
62
|
def event_handler(event_name: str) -> Callable[[Callback], Callback]:
|
63
|
+
"""Decorator to register a callback as an event handler for a specific event."""
|
64
|
+
|
57
65
|
def decorator(callback: Callback) -> Callback:
|
58
66
|
@wraps(callback)
|
59
|
-
def wrapper(*args, **kwargs):
|
67
|
+
def wrapper(*args, **kwargs) -> Any:
|
68
|
+
"""Wrapper to register the callback and call it."""
|
60
69
|
return callback(*args, **kwargs)
|
61
70
|
|
62
71
|
set_handler(event_name, wrapper)
|
bear_utils/extras/__init__.py
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
"""A module for various utilities in Bear Utils extras."""
|
2
|
+
|
1
3
|
from singleton_base import SingletonBase
|
2
4
|
|
3
5
|
from ._tools import ClipboardManager, clear_clipboard, copy_to_clipboard, fmt_header, paste_from_clipboard
|
@@ -6,15 +8,15 @@ from .wrappers.add_methods import add_comparison_methods
|
|
6
8
|
|
7
9
|
__all__ = [
|
8
10
|
"OS",
|
11
|
+
"ClipboardManager",
|
12
|
+
"SingletonBase",
|
13
|
+
"add_comparison_methods",
|
14
|
+
"clear_clipboard",
|
15
|
+
"copy_to_clipboard",
|
16
|
+
"fmt_header",
|
9
17
|
"get_platform",
|
10
18
|
"is_linux",
|
11
19
|
"is_macos",
|
12
20
|
"is_windows",
|
13
|
-
"ClipboardManager",
|
14
|
-
"copy_to_clipboard",
|
15
21
|
"paste_from_clipboard",
|
16
|
-
"clear_clipboard",
|
17
|
-
"fmt_header",
|
18
|
-
"add_comparison_methods",
|
19
|
-
"SingletonBase",
|
20
22
|
]
|
@@ -1,10 +1,9 @@
|
|
1
|
-
import inspect
|
2
1
|
from collections.abc import Callable
|
2
|
+
import inspect
|
3
3
|
|
4
4
|
|
5
5
|
def is_async_function(func: Callable) -> bool:
|
6
|
-
"""
|
7
|
-
Check if a function is asynchronous.
|
6
|
+
"""Check if a function is asynchronous.
|
8
7
|
|
9
8
|
Args:
|
10
9
|
func (Callable): The function/method to check.
|