refactor(schedule): extract scheduling/ layer (scheduler + orchestrator)
Stage 3 of the layered refactor. Relocate the two scheduling components
into their own package so that domain / application / scheduling /
execution / infrastructure boundaries actually exist on disk.
- Add schedule/src/schedule/scheduling/__init__.py
- Move scheduler.py (232 lines) -> scheduling/scheduler.py
(byte-identical via diff; CronScheduler class name unchanged)
- Move orchestrator.py (946 lines) -> scheduling/orchestrator.py
(byte-identical via diff; DispatchOrchestrator + event constants
unchanged; NOT further split this round, per plan)
- service.py lines 39-40: import paths rewritten to the new module
- tests/test_janitor.py: rewrite the import + 5 patch() string targets
The 5 patch() targets ("schedule.orchestrator.session_scope" x3,
"schedule.orchestrator.asyncio.sleep" x2) were NOT caught by the
import-line grep — they patch module attributes at runtime and would
have become dead no-ops after the move (and would hard-raise once
the old module is deleted in stage 6). Rewriting them to
"schedule.scheduling.orchestrator.*" keeps the janitor tests meaningfully
exercising the new module.
- old flat scheduler.py / orchestrator.py left on disk; stage 6 deletes
them once all layers are extracted.
Validation:
- uv run --package schedule pytest schedule/tests -q: 18 passed
- uv run python -m compileall schedule/src: zero errors
- grep 'from schedule.(scheduler|orchestrator)\\b' (old paths): 0 matches
- grep '"schedule.orchestrator.' (old patch targets): 0 matches
- main.py / worker.py / domain/ / infrastructure/ / pyproject.toml
byte-identical to HEAD
Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,7 @@ import pytest
|
||||
|
||||
from common.db.models import ScheduleNodeRuns
|
||||
from common.eventing import utcnow
|
||||
from schedule.orchestrator import NODE_FINISHED_EVENT, DispatchOrchestrator
|
||||
from schedule.scheduling.orchestrator import NODE_FINISHED_EVENT, DispatchOrchestrator
|
||||
|
||||
|
||||
def _make_orchestrator() -> DispatchOrchestrator:
|
||||
@@ -195,7 +195,7 @@ async def test_reap_kills_running_row_past_deadline() -> None:
|
||||
fake_session.add = MagicMock()
|
||||
|
||||
with patch(
|
||||
"schedule.orchestrator.session_scope",
|
||||
"schedule.scheduling.orchestrator.session_scope",
|
||||
return_value=_open_session_scope(fake_session),
|
||||
):
|
||||
killed = await orch._reap_stuck_node_runs()
|
||||
@@ -223,7 +223,7 @@ async def test_reap_skips_healthy_row() -> None:
|
||||
fake_session.add = MagicMock()
|
||||
|
||||
with patch(
|
||||
"schedule.orchestrator.session_scope",
|
||||
"schedule.scheduling.orchestrator.session_scope",
|
||||
return_value=_open_session_scope(fake_session),
|
||||
):
|
||||
killed = await orch._reap_stuck_node_runs()
|
||||
@@ -266,7 +266,7 @@ async def test_reap_processes_multiple_rows_in_one_pass() -> None:
|
||||
fake_session.add = MagicMock()
|
||||
|
||||
with patch(
|
||||
"schedule.orchestrator.session_scope",
|
||||
"schedule.scheduling.orchestrator.session_scope",
|
||||
return_value=_open_session_scope(fake_session),
|
||||
):
|
||||
killed = await orch._reap_stuck_node_runs()
|
||||
@@ -289,7 +289,7 @@ async def test_janitor_loop_propagates_cancellation() -> None:
|
||||
raise asyncio.CancelledError
|
||||
|
||||
with patch(
|
||||
"schedule.orchestrator.asyncio.sleep",
|
||||
"schedule.scheduling.orchestrator.asyncio.sleep",
|
||||
side_effect=cancel_on_sleep,
|
||||
):
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
@@ -321,7 +321,7 @@ async def test_janitor_loop_continues_after_reap_exception() -> None:
|
||||
raise asyncio.CancelledError
|
||||
|
||||
with patch(
|
||||
"schedule.orchestrator.asyncio.sleep",
|
||||
"schedule.scheduling.orchestrator.asyncio.sleep",
|
||||
side_effect=_count_sleeps,
|
||||
):
|
||||
with pytest.raises(asyncio.CancelledError):
|
||||
|
||||
Reference in New Issue
Block a user