Skip to content

Commit 5228adf

Browse files
raise clear error when plain file references a binary resource
Referencing a binary file from a .plain file crashed with "cannot access local variable 'content_text' where it is not associated with a value" because open_from() swallowed UnicodeDecodeError but still returned the never-assigned variable. Now the decode error propagates and load_linked_resources() converts it into a PlainSyntaxError naming the offending file.
1 parent 70150ef commit 5228adf

2 files changed

Lines changed: 49 additions & 8 deletions

File tree

file_utils.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import plain_spec
1010
from plain2code_console import console
11+
from plain2code_exceptions import PlainSyntaxError
1112
from plain2code_nodes import Plain2CodeIncludeTag, Plain2CodeLoaderMixin
1213
from plain_modules import CODEPLAIN_MEMORY_SUBFOLDER, CODEPLAIN_METADATA_FOLDER
1314

@@ -217,13 +218,7 @@ def open_from(dirs, file_name):
217218

218219
with open(full_file_name, "rb") as f:
219220
content = f.read()
220-
try:
221-
content_text = content.decode("utf-8")
222-
except UnicodeDecodeError:
223-
console.debug(
224-
f"WARNING! Error loading {file_name} ({file_name}). File is not a text file. Skipping it."
225-
)
226-
return content_text
221+
return content.decode("utf-8")
227222

228223
return None
229224

@@ -236,7 +231,13 @@ def load_linked_resources(template_dirs: list[str], resources_list):
236231
if file_name in linked_resources:
237232
continue
238233

239-
content = open_from(template_dirs, file_name)
234+
try:
235+
content = open_from(template_dirs, file_name)
236+
except UnicodeDecodeError:
237+
raise PlainSyntaxError(
238+
f"Plain syntax error: Referenced resource '{file_name}' is a binary file. "
239+
f"Only text files (e.g. .md, .txt, .json, .yaml) can be referenced from a .plain file."
240+
)
240241

241242
if content is None:
242243
raise FileNotFoundError(f"""File not found:

tests/test_file_utils.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import os
2+
import tempfile
3+
4+
import pytest
5+
6+
from file_utils import load_linked_resources
7+
from plain2code_exceptions import PlainSyntaxError
8+
9+
10+
@pytest.fixture
11+
def template_dir():
12+
with tempfile.TemporaryDirectory() as d:
13+
yield d
14+
15+
16+
def test_load_linked_resources_text_file(template_dir):
17+
file_path = os.path.join(template_dir, "notes.md")
18+
with open(file_path, "w") as f:
19+
f.write("# hello")
20+
21+
result = load_linked_resources([template_dir], [{"text": "Notes", "target": "notes.md"}])
22+
23+
assert result == {"notes.md": "# hello"}
24+
25+
26+
def test_load_linked_resources_binary_file_raises_plain_syntax_error(template_dir):
27+
file_path = os.path.join(template_dir, "icon.png")
28+
with open(file_path, "wb") as f:
29+
f.write(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\xff\xfe\xfd")
30+
31+
with pytest.raises(PlainSyntaxError) as exc_info:
32+
load_linked_resources([template_dir], [{"text": "Icon", "target": "icon.png"}])
33+
34+
assert "icon.png" in str(exc_info.value)
35+
assert "binary file" in str(exc_info.value)
36+
37+
38+
def test_load_linked_resources_missing_file_raises_file_not_found(template_dir):
39+
with pytest.raises(FileNotFoundError):
40+
load_linked_resources([template_dir], [{"text": "Missing", "target": "missing.md"}])

0 commit comments

Comments
 (0)