Skip to content

Commit e864db0

Browse files
committed
WIP:core:libs: Add standard events
1 parent 4320b3b commit e864db0

7 files changed

Lines changed: 437 additions & 4 deletions

File tree

core/libs/commonwealth/src/commonwealth/settings/managers/pydantic_manager.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import pathlib
22
import re
3-
from typing import Any, Optional, Type
3+
from typing import Any, Dict, Optional, Type
44

55
import appdirs
66
from commonwealth.settings.bases.pydantic_base import PydanticSettings
7+
from commonwealth.utils.events import events
78
from loguru import logger
89

910

@@ -29,6 +30,7 @@ def __init__(
2930
self.config_folder.mkdir(parents=True, exist_ok=True)
3031
self.settings_type = settings_type
3132
self._settings: Optional[PydanticSettings] = None
33+
self._initial_event_emitted = False
3234
logger.debug(
3335
f"Starting {project_name} settings with {settings_type.__name__}, configuration path: {config_folder}"
3436
)
@@ -97,6 +99,7 @@ def load_from_file(settings_type: Type[PydanticSettings], file_path: pathlib.Pat
9799
def save(self) -> None:
98100
"""Save settings"""
99101
self.settings.save(self.settings_file_path())
102+
self._publish_settings_snapshot("save")
100103

101104
def load(self) -> None:
102105
"""Load settings"""
@@ -125,13 +128,15 @@ def get_settings_version_from_filename(filename: pathlib.Path) -> int:
125128
try:
126129
self._settings = PydanticManager.load_from_file(self.settings_type, valid_file)
127130
logger.debug(f"Using {valid_file} as settings source")
131+
self._emit_initial_settings_event()
128132
return
129133
except Exception as exception:
130134
logger.debug("Invalid settings, going to try another file:", exception)
131135

132136
logger.debug("No valid settings found, using default settings")
133137
self._settings = self.settings_type()
134138
self.save()
139+
self._emit_initial_settings_event()
135140

136141
def _clear_temp_files(self) -> None:
137142
"""Clear temporary files"""
@@ -140,3 +145,30 @@ def _clear_temp_files(self) -> None:
140145
temp_file.unlink()
141146
except Exception as exception:
142147
logger.debug(f"Failed to clear temporary file {temp_file}: {exception}")
148+
149+
def _serialize_settings(self) -> Optional[Dict[str, Any]]:
150+
if not self._settings:
151+
return None
152+
try:
153+
if hasattr(self._settings, "model_dump"):
154+
return self._settings.model_dump()
155+
return self._settings.dict()
156+
except Exception as exc: # pragma: no cover - best effort
157+
logger.debug(f"Failed to serialize settings for event publishing: {exc}")
158+
return None
159+
160+
def _publish_settings_snapshot(self, reason: str) -> None:
161+
serialized = self._serialize_settings()
162+
if not serialized:
163+
return
164+
metadata = {"project": self.project_name, "reason": reason}
165+
try:
166+
events.publish_settings(serialized, metadata)
167+
except Exception as exc: # pragma: no cover - best effort
168+
logger.debug(f"Unable to publish settings event: {exc}")
169+
170+
def _emit_initial_settings_event(self) -> None:
171+
if self._initial_event_emitted:
172+
return
173+
self._initial_event_emitted = True
174+
self._publish_settings_snapshot("initial-load")

core/libs/commonwealth/src/commonwealth/settings/managers/pykson_manager.py

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import json
12
import pathlib
23
import re
3-
from typing import Any, Optional, Type
4+
from typing import Any, Dict, Optional, Type
45

56
import appdirs
67
from commonwealth.settings.bases.pykson_base import PyksonSettings
8+
from commonwealth.utils.events import events
79
from loguru import logger
810

911

@@ -29,6 +31,7 @@ def __init__(
2931
self.config_folder.mkdir(parents=True, exist_ok=True)
3032
self.settings_type = settings_type
3133
self._settings: Optional[PyksonSettings] = None
34+
self._initial_event_emitted = False
3235
logger.debug(
3336
f"Starting {project_name} settings with {settings_type.__name__}, configuration path: {config_folder}"
3437
)
@@ -93,6 +96,7 @@ def load_from_file(settings_type: Type[PyksonSettings], file_path: pathlib.Path)
9396
def save(self) -> None:
9497
"""Save settings"""
9598
self.settings.save(self.settings_file_path())
99+
self._publish_settings_snapshot("save")
96100

97101
def load(self) -> None:
98102
"""Load settings"""
@@ -121,13 +125,15 @@ def get_settings_version_from_filename(filename: pathlib.Path) -> int:
121125
try:
122126
self._settings = PyksonManager.load_from_file(self.settings_type, valid_file)
123127
logger.debug(f"Using {valid_file} as settings source")
128+
self._emit_initial_settings_event()
124129
return
125130
except Exception as exception:
126131
logger.debug("Invalid settings, going to try another file:", exception)
127132

128133
logger.debug("No valid settings found, using default settings")
129134
self._settings = self.settings_type()
130135
self.save()
136+
self._emit_initial_settings_event()
131137

132138
def _clear_temp_files(self) -> None:
133139
"""Clear temporary files"""
@@ -136,3 +142,29 @@ def _clear_temp_files(self) -> None:
136142
temp_file.unlink()
137143
except Exception as exception:
138144
logger.debug(f"Failed to clear temporary file {temp_file}: {exception}")
145+
146+
def _serialize_settings(self) -> Optional[Dict[str, Any]]:
147+
if not self._settings:
148+
return None
149+
try:
150+
raw = Pykson().to_json(self._settings)
151+
return json.loads(raw)
152+
except Exception as exc: # pragma: no cover - best effort
153+
logger.debug(f"Failed to serialize pykson settings: {exc}")
154+
return None
155+
156+
def _publish_settings_snapshot(self, reason: str) -> None:
157+
serialized = self._serialize_settings()
158+
if not serialized:
159+
return
160+
metadata = {"project": self.project_name, "reason": reason}
161+
try:
162+
events.publish_settings(serialized, metadata)
163+
except Exception as exc: # pragma: no cover - best effort
164+
logger.debug(f"Unable to publish settings event: {exc}")
165+
166+
def _emit_initial_settings_event(self) -> None:
167+
if self._initial_event_emitted:
168+
return
169+
self._initial_event_emitted = True
170+
self._publish_settings_snapshot("initial-load")
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from commonwealth.utils.events import (
2+
events,
3+
init_event_publisher,
4+
publish_error_event,
5+
publish_health_event,
6+
publish_running_event,
7+
publish_settings_event,
8+
publish_start_event,
9+
publish_stop_event,
10+
)
11+
12+
__all__ = [
13+
"events",
14+
"init_event_publisher",
15+
"publish_start_event",
16+
"publish_settings_event",
17+
"publish_running_event",
18+
"publish_health_event",
19+
"publish_error_event",
20+
"publish_stop_event",
21+
]

0 commit comments

Comments
 (0)