bear-utils 0.7.21__py3-none-any.whl → 0.7.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.
Files changed (79) hide show
  1. bear_utils/__init__.py +24 -1
  2. bear_utils/ai/__init__.py +5 -5
  3. bear_utils/ai/ai_helpers/__init__.py +24 -18
  4. bear_utils/ai/ai_helpers/_parsers.py +27 -21
  5. bear_utils/ai/ai_helpers/_types.py +2 -7
  6. bear_utils/cache/__init__.py +35 -23
  7. bear_utils/cli/__init__.py +13 -0
  8. bear_utils/cli/commands.py +14 -8
  9. bear_utils/cli/prompt_helpers.py +40 -34
  10. bear_utils/cli/shell/__init__.py +1 -0
  11. bear_utils/cli/shell/_base_command.py +18 -18
  12. bear_utils/cli/shell/_base_shell.py +37 -34
  13. bear_utils/config/__init__.py +4 -2
  14. bear_utils/config/config_manager.py +193 -56
  15. bear_utils/config/dir_manager.py +8 -3
  16. bear_utils/config/settings_manager.py +94 -171
  17. bear_utils/constants/__init__.py +2 -1
  18. bear_utils/constants/_exceptions.py +6 -1
  19. bear_utils/constants/date_related.py +2 -0
  20. bear_utils/constants/logger_protocol.py +28 -0
  21. bear_utils/constants/time_related.py +2 -0
  22. bear_utils/database/__init__.py +2 -0
  23. bear_utils/database/_db_manager.py +10 -11
  24. bear_utils/events/__init__.py +3 -1
  25. bear_utils/events/events_class.py +11 -11
  26. bear_utils/events/events_module.py +17 -8
  27. bear_utils/extras/__init__.py +8 -6
  28. bear_utils/extras/_async_helpers.py +2 -3
  29. bear_utils/extras/_tools.py +62 -52
  30. bear_utils/extras/platform_utils.py +5 -1
  31. bear_utils/extras/responses/__init__.py +1 -0
  32. bear_utils/extras/responses/function_response.py +301 -0
  33. bear_utils/extras/wrappers/__init__.py +1 -0
  34. bear_utils/extras/wrappers/add_methods.py +17 -15
  35. bear_utils/files/__init__.py +3 -1
  36. bear_utils/files/file_handlers/__init__.py +2 -0
  37. bear_utils/files/file_handlers/_base_file_handler.py +23 -3
  38. bear_utils/files/file_handlers/file_handler_factory.py +38 -38
  39. bear_utils/files/file_handlers/json_file_handler.py +49 -22
  40. bear_utils/files/file_handlers/log_file_handler.py +19 -12
  41. bear_utils/files/file_handlers/toml_file_handler.py +13 -5
  42. bear_utils/files/file_handlers/txt_file_handler.py +56 -14
  43. bear_utils/files/file_handlers/yaml_file_handler.py +19 -13
  44. bear_utils/files/ignore_parser.py +52 -57
  45. bear_utils/graphics/__init__.py +3 -1
  46. bear_utils/graphics/bear_gradient.py +17 -12
  47. bear_utils/graphics/image_helpers.py +11 -5
  48. bear_utils/gui/__init__.py +3 -1
  49. bear_utils/gui/gui_tools/__init__.py +3 -1
  50. bear_utils/gui/gui_tools/_settings.py +0 -1
  51. bear_utils/gui/gui_tools/qt_app.py +16 -11
  52. bear_utils/gui/gui_tools/qt_color_picker.py +24 -13
  53. bear_utils/gui/gui_tools/qt_file_handler.py +30 -38
  54. bear_utils/gui/gui_tools/qt_input_dialog.py +11 -14
  55. bear_utils/logging/__init__.py +6 -4
  56. bear_utils/logging/logger_manager/__init__.py +1 -0
  57. bear_utils/logging/logger_manager/_common.py +0 -1
  58. bear_utils/logging/logger_manager/_console_junk.py +14 -10
  59. bear_utils/logging/logger_manager/_styles.py +1 -2
  60. bear_utils/logging/logger_manager/loggers/__init__.py +1 -0
  61. bear_utils/logging/logger_manager/loggers/_base_logger.py +33 -36
  62. bear_utils/logging/logger_manager/loggers/_base_logger.pyi +6 -5
  63. bear_utils/logging/logger_manager/loggers/_buffer_logger.py +2 -3
  64. bear_utils/logging/logger_manager/loggers/_console_logger.py +52 -26
  65. bear_utils/logging/logger_manager/loggers/_console_logger.pyi +7 -21
  66. bear_utils/logging/logger_manager/loggers/_file_logger.py +20 -13
  67. bear_utils/logging/logger_manager/loggers/_level_sin.py +15 -15
  68. bear_utils/logging/logger_manager/loggers/_logger.py +4 -6
  69. bear_utils/logging/logger_manager/loggers/_sub_logger.py +16 -23
  70. bear_utils/logging/logger_manager/loggers/_sub_logger.pyi +4 -19
  71. bear_utils/logging/loggers.py +9 -13
  72. bear_utils/monitoring/__init__.py +7 -4
  73. bear_utils/monitoring/_common.py +28 -0
  74. bear_utils/monitoring/host_monitor.py +44 -48
  75. bear_utils/time/__init__.py +13 -6
  76. {bear_utils-0.7.21.dist-info → bear_utils-0.7.23.dist-info}/METADATA +50 -6
  77. bear_utils-0.7.23.dist-info/RECORD +83 -0
  78. bear_utils-0.7.21.dist-info/RECORD +0 -79
  79. {bear_utils-0.7.21.dist-info → bear_utils-0.7.23.dist-info}/WHEEL +0 -0
@@ -1,206 +1,140 @@
1
+ """Settings Manager Module for Bear Utils."""
2
+
1
3
  import atexit
2
- from argparse import Namespace
4
+ from collections.abc import Generator
3
5
  from contextlib import contextmanager
4
6
  from pathlib import Path
5
- from shelve import Shelf
6
- from typing import Any
7
+ from typing import Any, Self
7
8
 
8
- from singleton_base.singleton_base_new import SingletonBase
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
- if not path.exists():
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
- settings_name: str
50
- shelf: Shelf
51
- settings_cache: SettingsCache
20
+ """A class to manage settings using TinyDB and an in-memory cache."""
52
21
 
53
- def __init__(self, settings_name: str):
54
- object.__setattr__(self, "settings_name", settings_name)
55
- object.__setattr__(self, "settings_cache", SettingsCache())
56
- object.__setattr__(self, "shelf", self._open())
57
- object.__setattr__(self, "_initialized", True)
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._load_existing_settings()
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 not hasattr(self, "_initialized"):
44
+ if key in self.__slots__:
71
45
  object.__setattr__(self, key, value)
72
46
  return
73
-
74
- if key in ["settings_name", "settings_cache", "shelf"] or key.startswith("_"):
75
- raise AttributeError(f"Cannot modify '{key}' after initialization")
76
-
77
- self.set(key, value)
78
-
79
- def get(self, key: str, default=None):
80
- """Get a setting value by key with optional default."""
81
- try:
82
- if self.settings_cache.has(key):
83
- return self.settings_cache.get(key, default)
84
- elif self._shelf_has(key):
85
- return self.shelf[key]
86
- else:
87
- return default
88
- except Exception as e:
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 by key."""
98
- return self.settings_cache.has(key) or self._shelf_has(key)
99
-
100
- def _shelf_has(self, key: str) -> bool:
101
- """Check if a setting exists by key."""
102
- return key in self.shelf
103
-
104
- def open(self):
105
- object.__setattr__(self, "shelf", self._open())
106
- self._load_existing_settings()
107
-
108
- def _open(self) -> Shelf:
109
- """Open the settings file."""
110
- import shelve
111
-
112
- try:
113
- shelf: Shelf[Any] = shelve.open(get_config_file_path(self.settings_name))
114
- return shelf
115
- except Exception as e:
116
- raise RuntimeError(f"Warning: Could not open settings file '{self.settings_name}': {e}")
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, a bit nuclear and will require calling open() again."""
133
- file_path = get_config_file_path(self.settings_name)
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.settings_cache.clear()
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(vars(self.settings_cache).keys())
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
- for key in self.keys():
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
- for key in self.keys():
170
- yield self.settings_cache.get(key)
108
+ return list(self.cache.values())
171
109
 
172
- def __repr__(self):
173
- """String representation of the SettingsManager."""
174
- return f"<SettingsManager settings_name='{self.settings_name}'>"
110
+ def __len__(self):
111
+ return len(self.cache)
175
112
 
176
- def __str__(self):
177
- """String representation of the SettingsManager."""
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 __len__(self):
181
- """Get the number of settings."""
182
- return len(self.keys())
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
- def _get_instance(self, settings_name: str) -> SettingsManager:
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 the SettingsManager instance for a given settings name."""
198
- supervisor: SettingsSupervisor = SettingsSupervisor.get_instance(init=True)
199
- return supervisor._get_instance(settings_name)
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
- __all__: list[str] = ["SettingsManager", "settings", "get_settings_manager"]
213
-
214
- # if __name__ == "__main__":
215
- # with settings("example_settings") as sm:
216
- # sm.sample_setting = "This is a sample setting"
217
- # print(sm.sample_setting)
218
- # print(sm.keys())
219
- # for key, value in sm.items():
220
- # print("This is items()")
221
- # print(f"Key: {key}, Value: {value}")
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)
@@ -1,4 +1,5 @@
1
- from os import getenv
1
+ """Constants Module for Bear Utils."""
2
+
2
3
  from pathlib import Path
3
4
 
4
5
  VIDEO_EXTS = [".mp4", ".mov", ".avi", ".mkv"]
@@ -1,3 +1,8 @@
1
- class UserCancelled(Exception):
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)
@@ -1,3 +1,5 @@
1
+ """A module containing constants related to date and time formatting."""
2
+
1
3
  from bear_epoch_time.constants.date_related import (
2
4
  DATE_FORMAT,
3
5
  DATE_TIME_FORMAT,
@@ -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
+ ...
@@ -1,3 +1,5 @@
1
+ """A module containing constants related to time calculations."""
2
+
1
3
  from typing import Literal
2
4
 
3
5
  MINUTES_IN_HOUR: Literal[60] = 60
@@ -1,3 +1,5 @@
1
+ """Database Manager Module for managing database connections and operations."""
2
+
1
3
  from ._db_manager import DatabaseManager, SingletonDB
2
4
 
3
5
  __all__ = [
@@ -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 ..constants._lazy_typing import TableType
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
- records = session.query(table_obj).all()
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, None]:
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"]
@@ -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 clear_all as _clear_all
5
- from .events_module import clear_handlers_for_event as _clear_handlers_for_event
6
- from .events_module import dispatch_event as _dispatch_event
7
- from .events_module import event_handler as _event_handler
8
- from .events_module import set_handler as _set_handler
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, TypeAlias
8
+ from typing import Any
9
+ import weakref
8
10
  from weakref import WeakMethod, ref
9
11
 
10
- from ..extras._async_helpers import is_async_function
12
+ from bear_utils.extras._async_helpers import is_async_function
11
13
 
12
- Callback: TypeAlias = Callable[..., Any]
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
- results = list()
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)
@@ -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.