Skip to content

Commit cd634e6

Browse files
Look up config file in plain file dir and CWD
1 parent 4f26a1c commit cd634e6

4 files changed

Lines changed: 143 additions & 8 deletions

File tree

plain2code_arguments.py

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import os
33
import re
44

5+
from plain2code_console import console
6+
from plain2code_exceptions import AmbiguousConfigFileError
57
from plain2code_read_config import get_args_from_config
68

79
CODEPLAIN_API_KEY = os.getenv("CODEPLAIN_API_KEY")
@@ -83,9 +85,51 @@ def frid_range_string(s):
8385
return s
8486

8587

88+
def resolve_config_file(config_name: str, plain_file_path: str):
89+
"""
90+
Resolve the config file path by searching in two locations:
91+
1. Directory of the plain file
92+
2. Current working directory (where render is called from)
93+
94+
Returns the resolved absolute path, or None if the file is not found in either location.
95+
Raises AmbiguousConfigFileError if the file exists in both locations (and they differ).
96+
"""
97+
plain_file_dir = os.path.dirname(os.path.abspath(plain_file_path))
98+
cwd = os.getcwd()
99+
100+
plain_dir_config = os.path.normpath(os.path.join(plain_file_dir, config_name))
101+
cwd_config = os.path.normpath(os.path.join(cwd, config_name))
102+
103+
in_plain_dir = os.path.exists(plain_dir_config)
104+
in_cwd = os.path.exists(cwd_config)
105+
same_location = plain_dir_config == cwd_config
106+
107+
if in_plain_dir and in_cwd and not same_location:
108+
raise AmbiguousConfigFileError(
109+
f"Config file '{config_name}' was found in two locations:\n"
110+
f" - Plain file directory: {plain_file_dir}\n"
111+
f" - Current working directory: {cwd}\n"
112+
f"Remove the config file from one of these locations to resolve the ambiguity."
113+
)
114+
115+
if in_plain_dir:
116+
return plain_dir_config
117+
if in_cwd:
118+
return cwd_config
119+
return None
120+
121+
86122
def update_args_with_config(args, parser):
87123
try:
88-
config_args = get_args_from_config(args.config_name, parser)
124+
resolved_config = resolve_config_file(args.config_name, args.filename)
125+
126+
if resolved_config is None:
127+
console.info(f"No config file '{args.config_name}' found. Proceeding without one.")
128+
return args
129+
130+
args.config_name = resolved_config
131+
config_args = get_args_from_config(resolved_config, parser)
132+
89133
# Get all action types from the parser
90134
action_types = {action.dest: action for action in parser._actions}
91135

@@ -109,6 +153,8 @@ def update_args_with_config(args, parser):
109153
else:
110154
parser.error(f"Invalid argument: {key}")
111155

156+
except AmbiguousConfigFileError as e:
157+
parser.error(str(e))
112158
except Exception as e:
113159
parser.error(f"Error reading config file: {str(e)}")
114160

@@ -152,7 +198,7 @@ def create_parser():
152198
"--config-name",
153199
type=non_empty_string,
154200
default="config.yaml",
155-
help="Path to the config file, defaults to config.yaml",
201+
help="Name of the config file to look for. Looked up in the plain file directory and the current working directory. Defaults to config.yaml.",
156202
)
157203

158204
render_range_group = parser.add_mutually_exclusive_group()

plain2code_exceptions.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,3 +73,9 @@ class NetworkConnectionError(Exception):
7373
"""Raised when there is a network connectivity issue with the API server."""
7474

7575
pass
76+
77+
78+
class AmbiguousConfigFileError(Exception):
79+
"""Raised when a config file is found in both the plain file directory and the current working directory."""
80+
81+
pass

plain2code_read_config.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import os
21
from argparse import ArgumentParser, Namespace
32
from typing import Any, Dict
43

@@ -46,11 +45,6 @@ def get_args_from_config(config_file: str, parser: ArgumentParser) -> Namespace:
4645

4746
args = Namespace()
4847

49-
if config_file == "config.yaml":
50-
if not os.path.exists(config_file):
51-
console.info(f"Default config file {config_file} not found. No config file is read.")
52-
return args
53-
5448
# Load config
5549
config = load_config(config_file)
5650
config = validate_config(config, parser)

tests/test_resolve_config_file.py

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import os
2+
import tempfile
3+
from pathlib import Path
4+
from unittest.mock import patch
5+
6+
import pytest
7+
8+
from plain2code_arguments import resolve_config_file
9+
from plain2code_exceptions import AmbiguousConfigFileError
10+
11+
12+
@pytest.fixture
13+
def two_dirs():
14+
"""Provide two separate temporary directories: one for the plain file, one as CWD."""
15+
with tempfile.TemporaryDirectory() as plain_dir:
16+
with tempfile.TemporaryDirectory() as cwd:
17+
yield plain_dir, cwd
18+
19+
20+
def _plain_file(plain_dir):
21+
return os.path.join(plain_dir, "module.plain")
22+
23+
24+
def test_config_in_plain_file_dir_only(two_dirs):
25+
plain_dir, cwd = two_dirs
26+
config = Path(plain_dir) / "config.yaml"
27+
config.write_text("verbose: true\n")
28+
29+
with patch("os.getcwd", return_value=cwd):
30+
result = resolve_config_file("config.yaml", _plain_file(plain_dir))
31+
32+
assert result == os.path.normpath(str(config))
33+
34+
35+
def test_config_in_cwd_only(two_dirs):
36+
plain_dir, cwd = two_dirs
37+
config = Path(cwd) / "config.yaml"
38+
config.write_text("verbose: true\n")
39+
40+
with patch("os.getcwd", return_value=cwd):
41+
result = resolve_config_file("config.yaml", _plain_file(plain_dir))
42+
43+
assert result == os.path.normpath(str(config))
44+
45+
46+
def test_config_in_both_locations_raises(two_dirs):
47+
plain_dir, cwd = two_dirs
48+
(Path(plain_dir) / "config.yaml").write_text("verbose: true\n")
49+
(Path(cwd) / "config.yaml").write_text("verbose: false\n")
50+
51+
with patch("os.getcwd", return_value=cwd):
52+
with pytest.raises(AmbiguousConfigFileError) as exc_info:
53+
resolve_config_file("config.yaml", _plain_file(plain_dir))
54+
55+
assert plain_dir in str(exc_info.value)
56+
assert cwd in str(exc_info.value)
57+
58+
59+
def test_config_not_found_returns_none(two_dirs):
60+
plain_dir, cwd = two_dirs
61+
62+
with patch("os.getcwd", return_value=cwd):
63+
result = resolve_config_file("config.yaml", _plain_file(plain_dir))
64+
65+
assert result is None
66+
67+
68+
def test_config_same_dir_no_error():
69+
"""When the plain file and CWD are in the same directory, a single config file is fine."""
70+
with tempfile.TemporaryDirectory() as d:
71+
config = Path(d) / "config.yaml"
72+
config.write_text("verbose: true\n")
73+
74+
with patch("os.getcwd", return_value=d):
75+
result = resolve_config_file("config.yaml", os.path.join(d, "module.plain"))
76+
77+
assert result == os.path.normpath(str(config))
78+
79+
80+
def test_custom_config_name_found_in_plain_file_dir(two_dirs):
81+
"""A custom --config-name is also looked up in the two locations."""
82+
plain_dir, cwd = two_dirs
83+
config = Path(plain_dir) / "myconfig.yaml"
84+
config.write_text("verbose: true\n")
85+
86+
with patch("os.getcwd", return_value=cwd):
87+
result = resolve_config_file("myconfig.yaml", _plain_file(plain_dir))
88+
89+
assert result == os.path.normpath(str(config))

0 commit comments

Comments
 (0)