Skip to content

Commit 76db5f2

Browse files
committed
Swift: make codegen resilient to formatting errors
More in general, the managed renderer flow does things more sensibly in case an exception is thrown: * it will not remove any file * it will drop already written files from the registry, so that codegen won't be skipped for those files during the next run
1 parent d53d275 commit 76db5f2

File tree

2 files changed

+51
-6
lines changed

2 files changed

+51
-6
lines changed

swift/codegen/lib/render.py

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,12 +103,18 @@ def __enter__(self):
103103
return self
104104

105105
def __exit__(self, exc_type, exc_val, exc_tb):
106-
for f in self._existing - self._skipped - self.written:
107-
self._hashes.pop(self._get_path(f), None)
108-
f.unlink(missing_ok=True)
109-
log.info(f"removed {f.name}")
110-
for f in self.written:
111-
self._hashes[self._get_path(f)].post = self._hash_file(f)
106+
if exc_val is None:
107+
for f in self._existing - self._skipped - self.written:
108+
self._hashes.pop(self._get_path(f), None)
109+
f.unlink(missing_ok=True)
110+
log.info(f"removed {f.name}")
111+
for f in self.written:
112+
self._hashes[self._get_path(f)].post = self._hash_file(f)
113+
else:
114+
# if an error was encountered, drop already written files from the registry
115+
# so that they get the chance to be regenerated again during the next run
116+
for f in self.written:
117+
self._hashes.pop(self._get_path(f), None)
112118
self._dump_registry()
113119

114120
def _do_write(self, mnemonic: str, contents: str, output: pathlib.Path):

swift/codegen/test/test_render.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,45 @@ def test_managed_render_with_modified_stub_file_not_marked_as_generated(pystache
192192
assert_file(registry, "")
193193

194194

195+
class MyError(Exception):
196+
pass
197+
198+
199+
def test_managed_render_exception_drops_written_from_registry(pystache_renderer, sut):
200+
data = mock.Mock(spec=("template",))
201+
text = "some text"
202+
pystache_renderer.render_name.side_effect = (text,)
203+
output = paths.swift_dir / "some/output.txt"
204+
registry = paths.swift_dir / "a/registry.list"
205+
write(output, text)
206+
write(registry, "a a a\n"
207+
f"some/output.txt whatever {hash(text)}\n"
208+
"b b b")
209+
210+
with pytest.raises(MyError):
211+
with sut.manage(generated=(), stubs=(), registry=registry) as renderer:
212+
renderer.render(data, output)
213+
raise MyError
214+
215+
assert_file(registry, "a a a\nb b b\n")
216+
217+
218+
def test_managed_render_exception_does_not_erase(pystache_renderer, sut):
219+
output = paths.swift_dir / "some/output.txt"
220+
stub = paths.swift_dir / "some/stub.txt"
221+
registry = paths.swift_dir / "a/registry.list"
222+
write(output)
223+
write(stub, "// generated bla bla")
224+
write(registry)
225+
226+
with pytest.raises(MyError):
227+
with sut.manage(generated=(output,), stubs=(stub,), registry=registry) as renderer:
228+
raise MyError
229+
230+
assert output.is_file()
231+
assert stub.is_file()
232+
233+
195234
def test_render_with_extensions(pystache_renderer, sut):
196235
data = mock.Mock(spec=("template", "extensions"))
197236
data.template = "test_template"

0 commit comments

Comments
 (0)