Skip to content

Commit a0bc25a

Browse files
committed
Bugfixing module_renderer.py
1 parent e48a903 commit a0bc25a

7 files changed

Lines changed: 132 additions & 92 deletions

module_renderer.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,12 @@ def _render_module(
8989
if not self.args.render_machine_graph and plain_module.required_modules and not is_partial_render_choice_module:
9090
console.debug(f"Analyzing required modules of module {plain_module.module_name}...")
9191
for required_module in plain_module.required_modules:
92-
has_any_required_module_changed, rendering_failed = self._render_module(
92+
has_module_changed, rendering_failed = self._render_module(
9393
required_module,
9494
None,
9595
self.args.force_render,
9696
)
97-
97+
has_any_required_module_changed |= has_module_changed
9898
if rendering_failed:
9999
return False, True
100100

@@ -145,6 +145,18 @@ def _render_module(
145145
return True, False
146146

147147
def render_module(self) -> None:
148+
if self.partial_render_choice is not None and self.partial_render_choice.wipe_later_modules:
149+
later_module = False
150+
all_modules = self.plain_module.all_required_modules + [self.plain_module]
151+
for module in all_modules:
152+
if module.module_name == self.partial_render_choice.module.module_name:
153+
later_module = True
154+
continue
155+
156+
if later_module:
157+
console.info(f"Wiping module {module.module_name}...")
158+
module.wipe_module()
159+
148160
self.loaded_modules = list[PlainModule]()
149161
_, rendering_failed = self._render_module(self.plain_module, self.render_range, True)
150162
if not rendering_failed:

partial_rendering.py

Lines changed: 48 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ class PartialRenderChoice:
1919
module: PlainModule | None = None
2020
render_range: list[str] | None = None
2121
msg: str | None = None
22+
wipe_later_modules: bool = False
2223

2324

2425
def spec_change(plain_module: PlainModule) -> PlainModule | None:
@@ -72,7 +73,7 @@ def detect_partial_rendering(plain_module: PlainModule) -> PartialRender | None:
7273
sc = spec_change(plain_module)
7374
cc = code_change(plain_module)
7475
all_required_modules = plain_module.all_required_modules
75-
last_rendered_module_name, last_rendered_frid = plain_module.get_module_last_rendered_functionality()
76+
last_rendered_module_name, last_rendered_frid = plain_module.get_module_render_status()
7677
if last_rendered_module_name is None and last_rendered_frid is None:
7778
return None
7879

@@ -84,16 +85,17 @@ def detect_partial_rendering(plain_module: PlainModule) -> PartialRender | None:
8485
module = plain_module
8586
else:
8687
return None
87-
88-
module = None
89-
for required_module in all_required_modules:
90-
if required_module.module_name == last_rendered_module_name:
91-
module = required_module
92-
93-
if module is None:
94-
raise ModuleDoesNotExistError(
95-
f"Last rendered module {last_rendered_module_name} not found in {[rmodule.module_name for rmodule in all_required_modules]}"
96-
)
88+
else:
89+
found_module: PlainModule | None = None
90+
for required_module in all_required_modules:
91+
if required_module.module_name == last_rendered_module_name:
92+
found_module = required_module
93+
94+
if found_module is None:
95+
raise ModuleDoesNotExistError(
96+
f"Last rendered module {last_rendered_module_name} not found in {[rmodule.module_name for rmodule in all_required_modules]}"
97+
)
98+
module = found_module
9799

98100
pr = PartialRender(
99101
last_render_module=module,
@@ -122,33 +124,47 @@ def detect_partial_rendering(plain_module: PlainModule) -> PartialRender | None:
122124
def get_choices(plain_module: PlainModule, partial_render: PartialRender) -> dict[str, PartialRenderChoice]:
123125
choices = dict[str, PartialRenderChoice]()
124126
choice_idx = 1
127+
125128
if not partial_render.last_render_module.is_module_fully_rendered():
129+
if not partial_render.last_render_frid:
130+
raise ValueError("Last render FRID is not set for a non-initial module")
131+
132+
next_frid, next_module = plain_module.get_next_frid(
133+
partial_render.last_render_frid, partial_render.last_render_module.module_name
134+
)
135+
render_range = plain_spec.get_render_range_from(next_frid, next_module.plain_source)
136+
msg = "Continue from"
137+
if next_frid != plain_spec.get_first_frid(next_module.plain_source):
138+
msg += f" functionality [#5593FF]{next_frid}[/]"
139+
else:
140+
msg += f" module [#5593FF]{next_module.module_name}[/]"
141+
142+
choices[str(choice_idx)] = PartialRenderChoice(
143+
module=next_module,
144+
render_range=render_range,
145+
msg=msg,
146+
)
147+
choice_idx += 1
148+
149+
elif partial_render.last_render_module.is_initial_module():
126150
choices[str(choice_idx)] = PartialRenderChoice(
127151
module=partial_render.last_render_module,
128152
render_range=None,
129153
msg=f"Start from module [#5593FF]{partial_render.last_render_module.module_name}[/]",
154+
wipe_later_modules=True,
130155
)
131156
choice_idx += 1
132-
if not partial_render.last_render_module.is_initial_module():
133-
if not partial_render.last_render_frid:
134-
raise ValueError("Last render FRID is not set for a non-initial module")
135157

136-
next_frid, next_module = plain_module.get_next_frid(
137-
partial_render.last_render_frid, partial_render.last_render_module.module_name
138-
)
139-
render_range = plain_spec.get_render_range_from(next_frid, next_module.plain_source)
140-
msg = "Continue from"
141-
if next_frid != plain_spec.get_first_frid(next_module.plain_source):
142-
msg += f" functionality [#5593FF]{next_frid}[/]"
143-
else:
144-
msg += f" module [#5593FF]{next_module.module_name}[/]"
145-
146-
choices[str(choice_idx)] = PartialRenderChoice(
147-
module=next_module,
148-
render_range=render_range,
149-
msg=msg,
150-
)
151-
choice_idx += 1
158+
else:
159+
next_module = plain_module.get_next_module(partial_render.last_render_module.module_name)
160+
if next_module is None:
161+
next_module = partial_render.last_render_module
162+
163+
choices[str(choice_idx)] = PartialRenderChoice(
164+
module=next_module,
165+
msg=f"Start from module [#5593FF]{next_module.module_name}[/]",
166+
)
167+
choice_idx += 1
152168

153169
if partial_render.change:
154170
all_affected_modules = list[str]()
@@ -163,6 +179,7 @@ def get_choices(plain_module: PlainModule, partial_render: PartialRender) -> dic
163179
module=partial_render.change,
164180
render_range=None,
165181
msg=f"Re-render all affected modules ([#5593FF]{', '.join(all_affected_modules)}[/])",
182+
wipe_later_modules=True,
166183
)
167184
choice_idx += 1
168185

@@ -174,6 +191,7 @@ def get_choices(plain_module: PlainModule, partial_render: PartialRender) -> dic
174191
module=first_module,
175192
render_range=None,
176193
msg=f"Re-render from first module ([#5593FF]{first_module.module_name}[/])",
194+
wipe_later_modules=True,
177195
)
178196
choice_idx += 1
179197

plain2code.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -202,8 +202,8 @@ def render(args, run_state: RunState, event_bus: EventBus): # noqa: C901
202202
# Last choice is Quit, first choice is the only other actionable choice
203203
partial_render_choice = choices[list(choices.keys())[0]]
204204

205-
# We can't have both partial render choice and render range
206-
assert not (partial_render_choice is not None and render_range is not None)
205+
if partial_render_choice is not None and render_range is not None:
206+
raise Exception("Partial rendering and render range cannot be used together")
207207

208208
module_renderer = ModuleRenderer(
209209
codeplainAPI,

plain_modules.py

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,15 @@
22

33
import json
44
import os
5+
import shutil
56
from functools import cached_property
67

78
from git.exc import NoSuchPathError
89

910
import git_utils
1011
import plain_file
1112
import plain_spec
13+
from plain2code_console import console
1214
from plain2code_exceptions import MissingPreviousFunctionalitiesError, ModuleDoesNotExistError
1315
from render_machine.implementation_code_helpers import ImplementationCodeHelpers
1416

@@ -68,7 +70,7 @@ def module_build_folder(self):
6870
def get_codeplain_folder(self):
6971
return os.path.join(self.module_build_folder, CODEPLAIN_METADATA_FOLDER)
7072

71-
def get_module_last_rendered_functionality(self) -> tuple[str | None, str | None]:
73+
def get_module_render_status(self) -> tuple[str | None, str | None]:
7274
if len(self.required_modules) == 0:
7375
return git_utils.get_last_rendered_functionality(self.module_build_folder)
7476

@@ -77,7 +79,7 @@ def get_module_last_rendered_functionality(self) -> tuple[str | None, str | None
7779
return module_name, frid
7880

7981
for module in reversed(self.required_modules):
80-
last_rendered_module_name, last_rendered_frid = module.get_module_last_rendered_functionality()
82+
last_rendered_module_name, last_rendered_frid = module.get_module_render_status()
8183
if last_rendered_module_name is not None:
8284
return last_rendered_module_name, last_rendered_frid
8385

@@ -162,19 +164,12 @@ def get_hashes(self) -> dict[str, str]:
162164
hashes["required_modules_code_hash"] = self.required_modules[-1].get_module_code_hash()
163165
return hashes
164166

165-
def save_module_metadata(self, only_hashes: bool = False):
167+
def save_module_metadata(self):
166168
codeplain_folder = self.get_codeplain_folder()
167169
os.makedirs(codeplain_folder, exist_ok=True)
168170

169171
module_metadata = self.get_hashes()
170-
171172
metadata_path = self.module_metadata_path()
172-
173-
if only_hashes:
174-
with open(metadata_path, "w", encoding="utf-8") as f:
175-
json.dump(module_metadata, f, indent=4)
176-
return
177-
178173
module_metadata[MODULE_FUNCTIONALITIES] = self._get_module_functional_requirements()
179174

180175
required_modules_functionalities = {}
@@ -277,10 +272,7 @@ def ensure_previous_frid_commits_exist(self, render_range: list[str], render_con
277272
for prev_frid in previous_frids:
278273
self._ensure_frid_commit_exists(prev_frid, first_render_frid, render_conformance_tests)
279274

280-
def get_module_by_name(self, module_name: str) -> PlainModule:
281-
if self.module_name == module_name:
282-
return self
283-
275+
def get_required_module_by_name(self, module_name: str) -> PlainModule:
284276
for module in self.all_required_modules:
285277
if module.module_name == module_name:
286278
return module
@@ -294,16 +286,22 @@ def get_next_module(self, module_name: str) -> PlainModule:
294286
return all_modules[idx + 1]
295287

296288
if module_name == self.module_name:
297-
return self
289+
return None
298290

299291
raise ModuleDoesNotExistError(f"Module {module_name} does not exist")
300292

301293
def get_next_frid(self, frid: str, module_name: str) -> tuple[str, PlainModule]:
302-
module = self.get_module_by_name(module_name)
294+
if module_name != self.module_name:
295+
module = self.get_required_module_by_name(module_name)
296+
else:
297+
module = self
298+
303299
next_frid = plain_spec.get_next_frid(module.plain_source, frid)
304300

305301
if next_frid is None:
306302
next_module = self.get_next_module(module_name)
303+
if next_module is None:
304+
next_module = self
307305
return plain_spec.get_first_frid(next_module.plain_source), next_module
308306

309307
return next_frid, module
@@ -335,3 +333,12 @@ def is_initial_module(self) -> bool:
335333
return True
336334

337335
return False
336+
337+
def wipe_module(self) -> None:
338+
if os.path.exists(self.module_build_folder):
339+
console.warning(f"Wiping module {self.module_build_folder}...")
340+
shutil.rmtree(self.module_build_folder)
341+
342+
if os.path.exists(self.module_conformance_tests_folder):
343+
console.warning(f"Wiping conformance tests for module {self.module_conformance_tests_folder}...")
344+
shutil.rmtree(self.module_conformance_tests_folder)

tests/test_partial_rendering.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,11 @@
66
under test relies on: ``module_name``, ``required_modules``,
77
``all_required_modules``, ``load_module_metadata``, ``get_module_source_hash``,
88
``get_module_code_hash`` and (for ``detect_partial_rendering``)
9-
``get_module_last_rendered_functionality``.
9+
``get_module_render_status``.
1010
"""
1111

1212
import pytest
1313

14-
# Import ``plain_file`` first — it pulls in ``file_utils`` which transitively
15-
# imports ``plain_modules``; starting the import graph at ``plain_file``
16-
# sidesteps a circular-import failure that occurs when tests import
17-
# ``partial_rendering`` (and therefore ``plain_modules``) first.
18-
import plain_file # noqa: F401
1914
from partial_rendering import (
2015
PartialRender,
2116
code_change,
@@ -61,7 +56,7 @@ def get_module_source_hash(self):
6156
def get_module_code_hash(self):
6257
return self._code_hash
6358

64-
def get_module_last_rendered_functionality(self):
59+
def get_module_render_status(self):
6560
return self._last_rendered
6661

6762

@@ -260,7 +255,7 @@ def test_detect_partial_rendering_raises_when_last_rendered_module_unknown():
260255
def test_detect_partial_rendering_spec_change_on_earlier_module_wins():
261256
"""A spec change on an earlier (required) module is reported via
262257
``change``/``change_type``; ``last_render_module`` and ``last_render_frid``
263-
remain the ones reported by ``get_module_last_rendered_functionality``."""
258+
remain the ones reported by ``get_module_render_status``."""
264259
root, middle, leaf = _build_fresh_tree(last_rendered=("middle", "1"))
265260
leaf._metadata = {"source_hash": "stale-leaf"} # leaf's spec changed
266261

0 commit comments

Comments
 (0)