Skip to content

Commit 8672dca

Browse files
Kiuk Chungfacebook-github-bot
authored andcommitted
(torchx/config) rename TORCHX_CONFIG to TORCHXCONFIG, improve unittest and documentation for config.find_configs (#507)
Summary: Pull Request resolved: #507 Renames env var `TORCHX_CONFIG` to `TORCHXCONFIG` to keep it consistent with the naming pattern of `.torchxconfig`. Improves documentation for `config.find_config` and unittests. Fixes the above that was introduced in D36692686 (5a88b4d) that landed before I got a chance to review it. Reviewed By: d4l3k Differential Revision: D36849299 fbshipit-source-id: edd6db88ff20535b50741d509a1d2dfe57b974dd
1 parent b3bc685 commit 8672dca

File tree

2 files changed

+74
-38
lines changed

2 files changed

+74
-38
lines changed

torchx/runner/config.py

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ def my_component(a: int) -> specs.AppDef:
152152

153153

154154
CONFIG_FILE = ".torchxconfig"
155-
ENV_TORCHX_CONFIG = "TORCHX_CONFIG"
156155
CONFIG_PREFIX_DELIM = ":"
156+
ENV_TORCHXCONFIG = "TORCHXCONFIG"
157157

158158
_NONE = "None"
159159

@@ -431,20 +431,29 @@ def get_config(
431431

432432
def find_configs(dirs: Optional[Iterable[str]] = None) -> List[str]:
433433
"""
434-
find_configs returns all the .torchxconfig files it finds:
435-
- if environment variable TORCHX_CONFIG is specified, then
436-
it is expected to exists and will be loaded as the only config
437-
- otherwise, directories are checked to look for .torchxconfig and loaded. If
438-
directories is empty it checks the local directory.
434+
Finds and returns the filepath to ``.torchxconfig`` files based
435+
on the following logic:
436+
437+
1. If the environment variable ``TORCHXCONFIG`` exists, then its value
438+
is returned in a single-element list and the directories specified through
439+
the ``dirs`` parameter is NOT searched.
440+
2. Otherwise, a ``.torchxconfig`` file is looked for in ``dirs`` and
441+
the filepaths to existing config files are returned. If ``dirs`` is
442+
not specified or is empty then, this method looks for a ``.torchxconfig`` file
443+
in CWD (current working dir) and returns the filepath to it if one exists.
444+
439445
"""
440446

441-
config_files = []
442-
config = os.getenv(ENV_TORCHX_CONFIG, "")
443-
if len(config) > 0:
447+
config = os.getenv(ENV_TORCHXCONFIG)
448+
if config is not None:
444449
configfile = Path(config)
445-
assert configfile.exists(), f"{str(configfile)} expected but not found"
446-
config_files.append(str(configfile))
450+
if not configfile.is_file():
451+
raise FileNotFoundError(
452+
f"`{ENV_TORCHXCONFIG}={config}` does not exist or is not a file."
453+
)
454+
return [str(configfile)]
447455
else:
456+
config_files = []
448457
if not dirs:
449458
dirs = [str(Path.cwd())]
450459
for d in dirs:

torchx/runner/test/config_test.py

Lines changed: 54 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
from torchx.runner.config import (
1919
apply,
2020
dump,
21+
ENV_TORCHXCONFIG,
22+
find_configs,
2123
get_config,
2224
get_configs,
2325
load,
@@ -259,6 +261,44 @@ def test_get_configs(self) -> None:
259261
),
260262
)
261263

264+
def test_find_configs(self) -> None:
265+
config_dir = Path(self.test_dir)
266+
cwd_dir = config_dir / "cwd"
267+
custom_dir = config_dir / "custom"
268+
269+
cwd_dir.mkdir()
270+
custom_dir.mkdir()
271+
272+
base_config = config_dir / ".torchxconfig"
273+
cwd_config = cwd_dir / ".torchxconfig"
274+
override_config = custom_dir / ".torchxconfig"
275+
276+
base_config.touch()
277+
cwd_config.touch()
278+
override_config.touch()
279+
280+
# should find configs in the specified dirs
281+
configs = find_configs(dirs=[str(config_dir)])
282+
self.assertEqual([str(base_config)], configs)
283+
284+
# should find config in cwd if no dirs is specified
285+
with patch(PATH_CWD, return_value=str(cwd_dir)):
286+
configs = find_configs()
287+
self.assertEqual([str(cwd_config)], configs)
288+
289+
# if TORCHXCONFIG env var exists then should just return the config specified
290+
with patch.dict(os.environ, {ENV_TORCHXCONFIG: str(override_config)}):
291+
configs = find_configs(dirs=[str(config_dir)])
292+
self.assertEqual([str(override_config)], configs)
293+
294+
# if TORCHXCONFIG points to a non-existing file, then assert exception
295+
with patch.dict(
296+
os.environ,
297+
{ENV_TORCHXCONFIG: str(config_dir / ".torchxconfig_nonexistent")},
298+
):
299+
with self.assertRaises(FileNotFoundError):
300+
find_configs(dirs=[str(config_dir)])
301+
262302
def test_get_config(self) -> None:
263303
configdir0 = Path(self.test_dir) / "test_load_component_defaults" / "0"
264304
configdir1 = Path(self.test_dir) / "test_load_component_defaults" / "1"
@@ -286,6 +326,20 @@ def test_get_config(self) -> None:
286326
get_config(prefix="badprefix", name="dist.ddp", key="j", dirs=dirs),
287327
)
288328

329+
# check that if TORCHXCONFIG is set then only that config is loaded
330+
override_config = Path(self.test_dir) / ".torchxconfig_custom"
331+
override_config_contents = """
332+
[component:dist.ddp]
333+
image = foobar_custom
334+
"""
335+
self._write(str(override_config), override_config_contents)
336+
337+
with patch.dict(os.environ, {ENV_TORCHXCONFIG: str(override_config)}):
338+
self.assertDictEqual(
339+
{"image": "foobar_custom"},
340+
get_configs(prefix="component", name="dist.ddp", dirs=dirs),
341+
)
342+
289343
def test_load(self) -> None:
290344
cfg = {}
291345
load(scheduler="local_cwd", f=StringIO(_CONFIG), cfg=cfg)
@@ -328,33 +382,6 @@ def test_apply_dirs(self, _) -> None:
328382
self.assertEqual(100, cfg.get("i"))
329383
self.assertEqual(1.2, cfg.get("f"))
330384

331-
@patch(
332-
TORCHX_GET_SCHEDULERS,
333-
return_value={"test": TestScheduler()},
334-
)
335-
def test_apply_from_annotated_config(self, _) -> None:
336-
def mock_getenv(x: str, y: Optional[str] = None) -> Optional[str]:
337-
if x != "TORCHX_CONFIG":
338-
return os.environ.get(x, y)
339-
else:
340-
return f"{self.test_dir}/another_torchx_config"
341-
342-
with patch(PATH_CWD, return_value=Path(self.test_dir)):
343-
with patch(
344-
"torchx.runner.config.os.getenv",
345-
mock_getenv,
346-
):
347-
cfg: Dict[str, CfgVal] = {"s": "runtime_value"}
348-
apply(
349-
scheduler="test",
350-
cfg=cfg,
351-
# these dirs will be ignored
352-
dirs=[str(Path(self.test_dir) / "home" / "bob"), self.test_dir],
353-
)
354-
self.assertEqual("runtime_value", cfg.get("s"))
355-
self.assertEqual(200, cfg.get("i"))
356-
self.assertEqual(None, cfg.get("f"))
357-
358385
def test_dump_invalid_scheduler(self) -> None:
359386
with self.assertRaises(ValueError):
360387
dump(f=StringIO(), schedulers=["does-not-exist"])

0 commit comments

Comments
 (0)