Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import pathlib
import re
from typing import Any, Optional, Type
from typing import Any, Dict, Optional, Type

import appdirs
from commonwealth.settings.bases.pydantic_base import PydanticSettings
from commonwealth.utils.events import events
from loguru import logger


Expand All @@ -29,6 +30,7 @@ def __init__(
self.config_folder.mkdir(parents=True, exist_ok=True)
self.settings_type = settings_type
self._settings: Optional[PydanticSettings] = None
self._initial_event_emitted = False
logger.debug(
f"Starting {project_name} settings with {settings_type.__name__}, configuration path: {config_folder}"
)
Expand Down Expand Up @@ -97,6 +99,7 @@ def load_from_file(settings_type: Type[PydanticSettings], file_path: pathlib.Pat
def save(self) -> None:
"""Save settings"""
self.settings.save(self.settings_file_path())
self._publish_settings_snapshot("save")

def load(self) -> None:
"""Load settings"""
Expand Down Expand Up @@ -125,13 +128,15 @@ def get_settings_version_from_filename(filename: pathlib.Path) -> int:
try:
self._settings = PydanticManager.load_from_file(self.settings_type, valid_file)
logger.debug(f"Using {valid_file} as settings source")
self._emit_initial_settings_event()
return
except Exception as exception:
logger.debug("Invalid settings, going to try another file:", exception)

logger.debug("No valid settings found, using default settings")
self._settings = self.settings_type()
self.save()
self._emit_initial_settings_event()

def _clear_temp_files(self) -> None:
"""Clear temporary files"""
Expand All @@ -140,3 +145,30 @@ def _clear_temp_files(self) -> None:
temp_file.unlink()
except Exception as exception:
logger.debug(f"Failed to clear temporary file {temp_file}: {exception}")

def _serialize_settings(self) -> Optional[Dict[str, Any]]:
if not self._settings:
return None
try:
if hasattr(self._settings, "model_dump"):
return self._settings.model_dump()
return self._settings.dict()
except Exception as exc: # pragma: no cover - best effort
logger.debug(f"Failed to serialize settings for event publishing: {exc}")
return None

def _publish_settings_snapshot(self, reason: str) -> None:
serialized = self._serialize_settings()
if not serialized:
return
metadata = {"project": self.project_name, "reason": reason}
try:
events.publish_settings(serialized, metadata)
except Exception as exc: # pragma: no cover - best effort
logger.debug(f"Unable to publish settings event: {exc}")

def _emit_initial_settings_event(self) -> None:
if self._initial_event_emitted:
return
self._initial_event_emitted = True
self._publish_settings_snapshot("initial-load")
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import json
import pathlib
import re
from typing import Any, Optional, Type
from typing import Any, Dict, Optional, Type

import appdirs
from commonwealth.settings.bases.pykson_base import PyksonSettings
from commonwealth.utils.events import events
from loguru import logger


Expand All @@ -29,6 +31,7 @@ def __init__(
self.config_folder.mkdir(parents=True, exist_ok=True)
self.settings_type = settings_type
self._settings: Optional[PyksonSettings] = None
self._initial_event_emitted = False
logger.debug(
f"Starting {project_name} settings with {settings_type.__name__}, configuration path: {config_folder}"
)
Expand Down Expand Up @@ -93,6 +96,7 @@ def load_from_file(settings_type: Type[PyksonSettings], file_path: pathlib.Path)
def save(self) -> None:
"""Save settings"""
self.settings.save(self.settings_file_path())
self._publish_settings_snapshot("save")

def load(self) -> None:
"""Load settings"""
Expand Down Expand Up @@ -121,13 +125,15 @@ def get_settings_version_from_filename(filename: pathlib.Path) -> int:
try:
self._settings = PyksonManager.load_from_file(self.settings_type, valid_file)
logger.debug(f"Using {valid_file} as settings source")
self._emit_initial_settings_event()
return
except Exception as exception:
logger.debug("Invalid settings, going to try another file:", exception)

logger.debug("No valid settings found, using default settings")
self._settings = self.settings_type()
self.save()
self._emit_initial_settings_event()

def _clear_temp_files(self) -> None:
"""Clear temporary files"""
Expand All @@ -136,3 +142,29 @@ def _clear_temp_files(self) -> None:
temp_file.unlink()
except Exception as exception:
logger.debug(f"Failed to clear temporary file {temp_file}: {exception}")

def _serialize_settings(self) -> Optional[Dict[str, Any]]:
if not self._settings:
return None
try:
raw = Pykson().to_json(self._settings)
return json.loads(raw)
except Exception as exc: # pragma: no cover - best effort
logger.debug(f"Failed to serialize pykson settings: {exc}")
return None

def _publish_settings_snapshot(self, reason: str) -> None:
serialized = self._serialize_settings()
if not serialized:
return
metadata = {"project": self.project_name, "reason": reason}
try:
events.publish_settings(serialized, metadata)
except Exception as exc: # pragma: no cover - best effort
logger.debug(f"Unable to publish settings event: {exc}")

def _emit_initial_settings_event(self) -> None:
if self._initial_event_emitted:
return
self._initial_event_emitted = True
self._publish_settings_snapshot("initial-load")
21 changes: 21 additions & 0 deletions core/libs/commonwealth/src/commonwealth/utils/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
from commonwealth.utils.events import (
events,
init_event_publisher,
publish_error_event,
publish_health_event,
publish_running_event,
publish_settings_event,
publish_start_event,
publish_stop_event,
)

__all__ = [
"events",
"init_event_publisher",
"publish_start_event",
"publish_settings_event",
"publish_running_event",
"publish_health_event",
"publish_error_event",
"publish_stop_event",
]
Loading
Loading