Skip to content

Commit 022c1e6

Browse files
authored
snapshots: stash BuildPlan on BuildContext (#799)
* snapshots: stash BuildPlan on BuildContext * snapshots: retrigger changelog skip check
1 parent b0953a0 commit 022c1e6

3 files changed

Lines changed: 96 additions & 0 deletions

File tree

bengal/orchestration/build_context.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
from bengal.rendering.pipeline.write_behind import WriteBehindCollector
5151
from bengal.services.data import DataService
5252
from bengal.services.query import QueryService
53+
from bengal.snapshots.build_plan import BuildPlan
5354
from bengal.snapshots.types import SiteSnapshot
5455
from bengal.utils.concurrency.executor import CancellationToken
5556
from bengal.utils.observability.cli_progress import LiveProgressManager
@@ -226,6 +227,10 @@ class BuildContext:
226227
# Snapshot for lock-free parallel rendering (RFC: rfc-bengal-snapshot-engine)
227228
snapshot: SiteSnapshot | None = None
228229

230+
# Frozen BuildPlan stashed by WaveScheduler template_first (RFC handoff).
231+
# Render still uses mutable site; pipeline does not read the plan yet.
232+
build_plan: BuildPlan | None = None
233+
229234
# Asset manifest context for postprocess (404, search templates use asset_url)
230235
# Set during phase_render bootstrap, reused in postprocess (Plan: asset-manifest-context-refactor)
231236
asset_manifest_ctx: AssetManifestContext | None = None

bengal/snapshots/scheduler.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -213,6 +213,10 @@ def _render_template_first(self, pages_to_build: Sequence[PageLike]) -> RenderSt
213213
content_snapshot_id=str(self.snapshot.snapshot_time),
214214
)
215215
self.build_plan = plan
216+
if self.build_context is not None:
217+
self.build_context.build_plan = plan
218+
# Named leftover: workers still get site=self.site.
219+
logger.info("render_uses_mutable_site")
216220
plan_by_source = {page_plan.source_path: page_plan for page_plan in plan.pages}
217221

218222
page_template_map: dict[PageLike, str] = {}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
"""WaveScheduler stashes frozen BuildPlan on live BuildContext (RFC handoff)."""
2+
3+
from __future__ import annotations
4+
5+
from dataclasses import FrozenInstanceError
6+
from unittest.mock import MagicMock
7+
8+
import pytest
9+
10+
from bengal.orchestration.build_context import BuildContext
11+
from bengal.snapshots import create_site_snapshot
12+
from bengal.snapshots.build_plan import BuildPlan, PagePlan
13+
from bengal.snapshots.scheduler import WaveScheduler
14+
from bengal.utils.observability.logger import get_logger
15+
16+
17+
def _scheduler(site) -> WaveScheduler:
18+
snapshot = create_site_snapshot(site)
19+
stats = MagicMock()
20+
build_context = BuildContext(
21+
site=site,
22+
pages=site.pages,
23+
stats=stats,
24+
)
25+
build_context.snapshot = snapshot
26+
return WaveScheduler(
27+
snapshot=snapshot,
28+
site=site,
29+
quiet=True,
30+
stats=stats,
31+
build_context=build_context,
32+
max_workers=2,
33+
)
34+
35+
36+
@pytest.mark.bengal(testroot="test-basic")
37+
def test_template_first_stashes_frozen_plan_on_build_context(site) -> None:
38+
scheduler = _scheduler(site)
39+
logger = get_logger("bengal.snapshots.scheduler")
40+
events_before = len(logger.get_events())
41+
42+
scheduler.render_all(list(site.pages))
43+
44+
plan = scheduler.build_plan
45+
assert isinstance(plan, BuildPlan)
46+
with pytest.raises(FrozenInstanceError):
47+
plan.config_hash = "mutated"
48+
assert scheduler.build_context.build_plan is scheduler.build_plan
49+
assert any(
50+
isinstance(page_plan, PagePlan) and page_plan.template_name == "page.html"
51+
for page_plan in plan.pages
52+
)
53+
54+
leftover_events = [
55+
event
56+
for event in logger.get_events()[events_before:]
57+
if event.event_type == "render_uses_mutable_site"
58+
]
59+
assert len(leftover_events) == 1
60+
61+
62+
@pytest.mark.bengal(testroot="test-basic")
63+
def test_template_first_renders_when_build_context_is_none(site) -> None:
64+
snapshot = create_site_snapshot(site)
65+
stats = MagicMock()
66+
scheduler = WaveScheduler(
67+
snapshot=snapshot,
68+
site=site,
69+
quiet=True,
70+
stats=stats,
71+
build_context=None,
72+
max_workers=2,
73+
)
74+
logger = get_logger("bengal.snapshots.scheduler")
75+
events_before = len(logger.get_events())
76+
77+
render_stats = scheduler.render_all(list(site.pages))
78+
79+
assert scheduler.build_context is None
80+
assert isinstance(scheduler.build_plan, BuildPlan)
81+
assert render_stats.pages_rendered == len(site.pages)
82+
leftover_events = [
83+
event
84+
for event in logger.get_events()[events_before:]
85+
if event.event_type == "render_uses_mutable_site"
86+
]
87+
assert len(leftover_events) == 1

0 commit comments

Comments
 (0)