Skip to content

Commit 9858ce6

Browse files
committed
Allow to enable WAL events in workspace config, not only via env variable. Add 'allow_no_handlers' to project executor. Add WAL events for ER lifecycle. If WM server fails to start, save output to file. WM: handle multiple requests asynchronously. WM client: avoid warnings for cancelled requests. Create prepare_envs_service which contains auto prepare-envs.
1 parent 150c9a0 commit 9858ce6

11 files changed

Lines changed: 937 additions & 42 deletions

File tree

src/finecode/wm_client.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,20 @@ async def _read_loop(self) -> None:
511511
if "id" in msg:
512512
# Response to a pending request.
513513
future = self._pending.pop(msg["id"], None)
514-
if future is not None and not future.done():
515-
future.set_result(msg)
516-
else:
514+
if future is None:
517515
logger.warning(
518516
f"WmClient: received response for unknown id {msg['id']}"
519517
)
518+
elif future.cancelled():
519+
logger.debug(
520+
f"WmClient: received late response for cancelled request {msg['id']}, discarding"
521+
)
522+
elif future.done():
523+
logger.warning(
524+
f"WmClient: received response for already-resolved id {msg['id']}"
525+
)
526+
else:
527+
future.set_result(msg)
520528
else:
521529
# Server→client notification.
522530
method = msg.get("method")

src/finecode/wm_server/cli.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,8 @@ def start_wm_server(
6161
wm_server._log_file_path = log_file_path
6262
port_file_path = pathlib.Path(port_file) if port_file else None
6363

64-
env_wal_enabled = _parse_env_bool("FINECODE_WAL_ENABLED", False)
64+
wm_wal = read_configs.read_wm_wal_config(workspace_root)
65+
env_wal_enabled = _parse_env_bool("FINECODE_WAL_ENABLED", wm_wal.enabled)
6566
final_wal_enabled = wal_enabled if wal_enabled is not None else env_wal_enabled
6667

6768
wal_config = wal.WalConfig(

src/finecode/wm_server/config/config_models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ class WmTelemetryConfig:
8181
otlp_endpoint: str | None = None
8282

8383

84+
@dataclass
85+
class WmWalConfig:
86+
enabled: bool = False
87+
88+
8489
@dataclass
8590
class ErEnvConfig:
8691
debug: bool = False

src/finecode/wm_server/config/read_configs.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,8 +80,13 @@ async def read_projects_in_dir(
8080

8181
status = domain.ProjectStatus.CONFIG_VALID
8282

83-
with open(def_file, "rb") as pyproject_file:
84-
project_def = toml_loads(pyproject_file.read()).unwrap()
83+
try:
84+
with open(def_file, "rb") as pyproject_file:
85+
project_def = toml_loads(pyproject_file.read()).unwrap()
86+
except Exception as e:
87+
raise config_models.ConfigurationError(
88+
f"Failed to parse '{def_file}': {e}"
89+
) from e
8590

8691
finecode_toml_exists = (def_file.parent / "finecode.toml").exists()
8792
has_pyproject_finecode = project_def.get("tool", {}).get("finecode") is not None
@@ -240,6 +245,24 @@ def read_wm_telemetry_config(workspace_root: Path) -> config_models.WmTelemetryC
240245
return config_models.WmTelemetryConfig(otlp_endpoint=otlp_endpoint)
241246

242247

248+
def read_wm_wal_config(workspace_root: Path) -> config_models.WmWalConfig:
249+
"""Read WM WAL config from [workspace.wm.wal] in finecode-workspace.toml.
250+
"""
251+
enabled = False
252+
253+
ws_config_path = workspace_root / "finecode-workspace.toml"
254+
if ws_config_path.exists():
255+
try:
256+
with open(ws_config_path, "rb") as f:
257+
ws_config = toml_loads(f.read()).unwrap()
258+
wal_raw = ws_config.get("workspace", {}).get("wm", {}).get("wal", {})
259+
enabled = bool(wal_raw.get("enabled", False))
260+
except Exception:
261+
pass
262+
263+
return config_models.WmWalConfig(enabled=enabled)
264+
265+
243266
def read_env_configs(project_config: dict[str, Any]) -> dict[str, domain.EnvConfig]:
244267
env_configs: dict[str, domain.EnvConfig] = {}
245268

@@ -597,6 +620,14 @@ def _merge_projects_configs(
597620
tool_finecode_config1[key][action_name] = action_info
598621
else:
599622
# action with the same name, merge
623+
#
624+
# Propagate source if the existing entry doesn't have one yet.
625+
# Presets are processed in non-deterministic order, so a preset
626+
# that adds handlers to an action declared by another preset may
627+
# be merged before the declaring preset supplies the source.
628+
if "source" in action_info and "source" not in tool_finecode_config1[key][action_name]:
629+
tool_finecode_config1[key][action_name]["source"] = action_info["source"]
630+
600631
if "config" in action_info:
601632
if "config" not in tool_finecode_config1[key][action_name]:
602633
tool_finecode_config1[key][action_name]["config"] = {}

0 commit comments

Comments
 (0)