Skip to content
Merged
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
19 changes: 12 additions & 7 deletions devcycle_python_sdk/managers/config_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ def _get_config(self, last_modified: Optional[float] = None):
json_config = json.dumps(self._config)
self._local_bucketing.store_config(json_config)
if not self._options.disable_realtime_updates:
if self._sse_manager is None:
if (
self._sse_manager is None
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential AttributeError when accessing self._sse_manager.client.is_connected(). The SSEManager's client attribute is initialized to None in the constructor and only gets assigned when the update() method is called. This check could raise an AttributeError if the SSEManager exists but update() hasn't been called yet. Consider adding a None check for the client before calling is_connected().

Suggested change
self._sse_manager is None
self._sse_manager is None
or self._sse_manager.client is None

Copilot uses AI. Check for mistakes.
or not self._sse_manager.client.is_connected()
):
self._sse_manager = SSEManager(
self.sse_state,
self.sse_error,
Expand Down Expand Up @@ -128,9 +131,9 @@ def run(self):
time.sleep(self._options.config_polling_interval_ms / 1000.0)

def sse_message(self, message: ld_eventsource.actions.Event):
if self._sse_connected is False:
self._sse_connected = True
logger.info("DevCycle: Connected to SSE stream")
# Received a message from the SSE stream but our sse_connected is False, so we need to set it to True
if not self._sse_connected:
self.sse_state(None)
logger.info(f"DevCycle: Received message: {message.data}")
sse_message = json.loads(message.data)
dvc_data = json.loads(sse_message.get("data"))
Expand All @@ -143,11 +146,13 @@ def sse_message(self, message: ld_eventsource.actions.Event):
self._get_config(dvc_data["lastModified"] / 1000.0)

def sse_error(self, error: ld_eventsource.actions.Fault):
self._sse_connected = False
logger.debug(f"DevCycle: Received SSE error: {error}")

def sse_state(self, state: ld_eventsource.actions.Start):
self._sse_connected = True
logger.info("DevCycle: Connected to SSE stream")
def sse_state(self, state: Optional[ld_eventsource.actions.Start]):
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type signature inconsistency detected. The SSEManager constructor expects handle_state to be of type Callable[[ld_eventsource.actions.Start], None] (line 15 in sse_manager.py), but the sse_state method signature has been changed to accept Optional[ld_eventsource.actions.Start]. The SSEManager type hints should be updated to reflect this change, using Optional[ld_eventsource.actions.Start] instead of just ld_eventsource.actions.Start.

Copilot uses AI. Check for mistakes.
if not self._sse_connected:
self._sse_connected = True
logger.info("DevCycle: Connected to SSE stream")
Comment on lines +149 to +155
Copy link

Copilot AI Dec 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential race condition with _sse_connected flag. This boolean flag is accessed and modified from multiple threads without synchronization: the main polling thread (line 128 in run()) and the SSE event handling thread (via sse_message, sse_error, and sse_state callbacks). Consider using a threading.Lock to protect access to _sse_connected, or use a thread-safe alternative like threading.Event.

Copilot uses AI. Check for mistakes.

def close(self):
self._polling_enabled = False
Loading