Skip to content

Commit 92cb325

Browse files
Resolve required modules in subdirectories via caller-side basename
The top module's directory is already folded into template_dirs by get_template_directories, so passing the full filename to plain_file_parser double-counted the directory. Normalize the top module to its basename at the call sites instead, and keep plain_file_parser preserving relative subdirectory prefixes so required/imported modules (e.g. 'dir_name/b') resolve correctly. Replaces the earlier stem-only fix, which broke required modules in subdirectories. Add PlainModule-tree and parser-level regression tests.
1 parent 53aed1d commit 92cb325

5 files changed

Lines changed: 49 additions & 13 deletions

File tree

‎plain2code.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
165165
render_range = None
166166
if args.render_range or args.render_from:
167167
# Parse the plain file to get the plain_source for FRID extraction
168-
_, plain_source, _ = plain_file.plain_file_parser(args.filename, template_dirs)
168+
_, plain_source, _ = plain_file.plain_file_parser(os.path.basename(args.filename), template_dirs)
169169
render_range = plain_spec.compute_render_range(args, plain_source)
170170

171171
codeplainAPI = codeplain_api.CodeplainAPI(args.api_key, console)
@@ -179,7 +179,7 @@ def render(args, run_state: RunState, event_bus: EventBus, default_log_level: st
179179
signal.signal(signal.SIGTERM, lambda _signum, _frame: stop_event.set())
180180

181181
plain_module = plain_modules.PlainModule(
182-
args.filename,
182+
os.path.basename(args.filename),
183183
args.build_folder,
184184
args.conformance_tests_folder,
185185
template_dirs,
@@ -323,7 +323,7 @@ def main(): # noqa: C901
323323

324324
if args.dry_run:
325325
console.info("Printing dry run output...\n")
326-
_, plain_source_tree, _ = plain_file.plain_file_parser(args.filename, template_dirs)
326+
_, plain_source_tree, _ = plain_file.plain_file_parser(os.path.basename(args.filename), template_dirs)
327327
render_range = plain_spec.compute_render_range(args, plain_source_tree)
328328
print_dry_run_output(plain_source_tree, render_range)
329329
return

‎plain_file.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -651,7 +651,11 @@ def plain_file_parser( # noqa: C901
651651
f"Plain syntax error: Invalid plain file extension: {plain_source_file_path.suffix}. Expected: {PLAIN_SOURCE_FILE_EXTENSION}."
652652
)
653653

654-
module_name = plain_source_file_path.stem
654+
module_name = (
655+
plain_source_file_path.stem
656+
if plain_source_file_path.is_absolute()
657+
else plain_source_file_path.with_suffix("").as_posix()
658+
)
655659

656660
code_variables = {}
657661

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
---
2+
requires:
3+
- nested/leaf
4+
---
5+
6+
***implementation reqs***
7+
8+
- Main implementation requirement.
9+
10+
***functional specs***
11+
12+
- Display "hello, world"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
***implementation reqs***
2+
3+
- Base implementation requirement for the leaf.
4+
5+
***functional specs***
6+
7+
- A base functionality from the leaf.

‎tests/test_plainfileparser.py‎

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,30 @@ def test_regular_plain_source(get_test_data_path):
2727
}
2828

2929

30-
def test_relative_subdirectory_path_resolves(get_test_data_path):
31-
# Regression: the CLI passes the full path as the filename and its directory as template_dirs[0].
32-
# For a relative path with subdirectories, module_name must be the bare stem; otherwise the
33-
# subdirectory prefix double-joins with template_dirs[0] and the module is reported as missing.
34-
data_dir = get_test_data_path("data/plainfileparser")
35-
rel_file = os.path.relpath(os.path.join(data_dir, "regular_plain_source.plain"))
36-
assert os.sep in rel_file # ensure the relative path actually has a subdirectory prefix
30+
def test_parser_keeps_subdirectory_in_module_name(get_test_data_path):
31+
# A template-relative filename with a subdirectory (as passed for required/imported modules)
32+
# must keep its subdirectory prefix in the module name so it resolves under template_dirs.
33+
data_dir = get_test_data_path("data/requires_subdir")
3734

38-
module_name, _, _ = plain_file.plain_file_parser(rel_file, [os.path.dirname(rel_file)])
35+
module_name, _, required = plain_file.plain_file_parser("nested/leaf.plain", [data_dir])
3936

40-
assert module_name == "regular_plain_source"
37+
assert module_name == "nested/leaf"
38+
assert required == []
39+
40+
41+
def test_requires_module_in_subdirectory_resolves(get_test_data_path):
42+
# Regression: rendering a top module (referenced by basename, its dir supplied via
43+
# template_dirs) that `requires` a module in a subdirectory must build the full PlainModule
44+
# tree without a "Module does not exist" error, preserving the subdirectory in build paths.
45+
import plain_modules
46+
47+
data_dir = get_test_data_path("data/requires_subdir")
48+
49+
module = plain_modules.PlainModule("main_in_subdir.plain", "build", "conformance", [data_dir])
50+
51+
assert module.module_name == "main_in_subdir"
52+
assert [m.module_name for m in module.required_modules] == ["nested/leaf"]
53+
assert [m.module_build_folder for m in module.required_modules] == [os.path.join("build", "nested/leaf")]
4154

4255

4356
def test_unknown_section():

0 commit comments

Comments
 (0)