Skip to content

Commit 163ce0f

Browse files
Resolve output paths relative to plain file
1 parent 35cd8ad commit 163ce0f

2 files changed

Lines changed: 103 additions & 0 deletions

File tree

plain2code_arguments.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,23 @@ def resolve_config_file(config_name: str, plain_file_path: str):
119119
return None
120120

121121

122+
def resolve_output_paths(args):
123+
"""Resolve relative output folder paths to be relative to the plain file directory.
124+
125+
All relative paths (whether from command line, config file, or defaults) are anchored
126+
to the directory of the plain file being rendered, not the current working directory.
127+
"""
128+
plain_file_dir = os.path.dirname(os.path.abspath(args.filename))
129+
130+
folder_args = ["build_folder", "conformance_tests_folder", "build_dest", "conformance_tests_dest", "base_folder"]
131+
for arg_name in folder_args:
132+
value = getattr(args, arg_name, None)
133+
if value and not os.path.isabs(value):
134+
setattr(args, arg_name, os.path.normpath(os.path.join(plain_file_dir, value)))
135+
136+
return args
137+
138+
122139
def update_args_with_config(args, parser):
123140
try:
124141
resolved_config = resolve_config_file(args.config_name, args.filename)
@@ -350,6 +367,7 @@ def parse_arguments():
350367

351368
args = parser.parse_args()
352369
args = update_args_with_config(args, parser)
370+
args = resolve_output_paths(args)
353371

354372
if args.build_folder == args.build_dest:
355373
parser.error("--build-folder and --build-dest cannot be the same")

tests/test_resolve_output_paths.py

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import os
2+
import tempfile
3+
from argparse import Namespace
4+
5+
import pytest
6+
7+
from plain2code_arguments import (
8+
DEFAULT_BUILD_DEST,
9+
DEFAULT_BUILD_FOLDER,
10+
DEFAULT_CONFORMANCE_TESTS_DEST,
11+
DEFAULT_CONFORMANCE_TESTS_FOLDER,
12+
resolve_output_paths,
13+
)
14+
15+
16+
def _make_args(plain_file, **kwargs):
17+
"""Build a minimal Namespace mimicking parsed args."""
18+
defaults = dict(
19+
filename=plain_file,
20+
build_folder=DEFAULT_BUILD_FOLDER,
21+
conformance_tests_folder=DEFAULT_CONFORMANCE_TESTS_FOLDER,
22+
build_dest=DEFAULT_BUILD_DEST,
23+
conformance_tests_dest=DEFAULT_CONFORMANCE_TESTS_DEST,
24+
base_folder=None,
25+
)
26+
defaults.update(kwargs)
27+
return Namespace(**defaults)
28+
29+
30+
def test_default_folders_resolve_to_plain_file_dir():
31+
with tempfile.TemporaryDirectory() as plain_dir:
32+
plain_file = os.path.join(plain_dir, "module.plain")
33+
args = resolve_output_paths(_make_args(plain_file))
34+
35+
assert args.build_folder == os.path.join(plain_dir, DEFAULT_BUILD_FOLDER)
36+
assert args.conformance_tests_folder == os.path.join(plain_dir, DEFAULT_CONFORMANCE_TESTS_FOLDER)
37+
assert args.build_dest == os.path.join(plain_dir, DEFAULT_BUILD_DEST)
38+
assert args.conformance_tests_dest == os.path.join(plain_dir, DEFAULT_CONFORMANCE_TESTS_DEST)
39+
40+
41+
def test_relative_path_from_config_resolves_to_plain_file_dir():
42+
"""A relative build_folder set in config (e.g. '../build') resolves relative to the plain file."""
43+
with tempfile.TemporaryDirectory() as plain_dir:
44+
plain_file = os.path.join(plain_dir, "module.plain")
45+
args = resolve_output_paths(_make_args(plain_file, build_folder="../build"))
46+
47+
assert args.build_folder == os.path.normpath(os.path.join(plain_dir, "../build"))
48+
assert os.path.isabs(args.build_folder)
49+
50+
51+
def test_absolute_path_left_unchanged():
52+
with tempfile.TemporaryDirectory() as plain_dir:
53+
plain_file = os.path.join(plain_dir, "module.plain")
54+
abs_path = "/some/absolute/path"
55+
args = resolve_output_paths(_make_args(plain_file, build_folder=abs_path))
56+
57+
assert args.build_folder == abs_path
58+
59+
60+
def test_base_folder_none_stays_none():
61+
with tempfile.TemporaryDirectory() as plain_dir:
62+
plain_file = os.path.join(plain_dir, "module.plain")
63+
args = resolve_output_paths(_make_args(plain_file, base_folder=None))
64+
65+
assert args.base_folder is None
66+
67+
68+
def test_base_folder_relative_resolves_to_plain_file_dir():
69+
with tempfile.TemporaryDirectory() as plain_dir:
70+
plain_file = os.path.join(plain_dir, "module.plain")
71+
args = resolve_output_paths(_make_args(plain_file, base_folder="base"))
72+
73+
assert args.build_folder == os.path.join(plain_dir, DEFAULT_BUILD_FOLDER)
74+
assert args.base_folder == os.path.join(plain_dir, "base")
75+
76+
77+
def test_plain_file_in_subdirectory():
78+
"""Plain file in a subdirectory — all defaults land next to it, not at CWD."""
79+
with tempfile.TemporaryDirectory() as root:
80+
sub = os.path.join(root, "b", "c")
81+
os.makedirs(sub)
82+
plain_file = os.path.join(sub, "example.plain")
83+
args = resolve_output_paths(_make_args(plain_file))
84+
85+
assert args.build_folder == os.path.join(sub, DEFAULT_BUILD_FOLDER)

0 commit comments

Comments
 (0)