Skip to content

Commit 48db5c4

Browse files
aivanoufacebook-github-bot
authored andcommitted
Move test_components to a separate target (#61)
Summary: Pull Request resolved: #61 Move test_components to a separate target Reviewed By: kiukchung Differential Revision: D29154499 fbshipit-source-id: 0479c36252bea7c3fa1cacaaa88a6e10faf2bf14
1 parent ddd42a6 commit 48db5c4

File tree

9 files changed

+17
-205
lines changed

9 files changed

+17
-205
lines changed

torchx/cli/cmd_run.py

Lines changed: 9 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
# LICENSE file in the root directory of this source tree.
66

77
import argparse
8-
import ast
98
import glob
109
import importlib
1110
import os
1211
from dataclasses import dataclass
1312
from inspect import getmembers, isfunction
14-
from typing import Dict, Iterable, List, Optional, Type, Union
13+
from typing import Dict, List, Optional, Union
1514

1615
import torchx.specs as specs
1716
from pyre_extensions import none_throws
@@ -32,63 +31,6 @@ def parse_args_children(arg: str) -> Dict[str, Union[str, List[str]]]:
3231
return conf
3332

3433

35-
class UnsupportFeatureError(Exception):
36-
def __init__(self, name: str) -> None:
37-
super().__init__(f"Using unsupported feature {name} in config.")
38-
39-
40-
class ConfValidator(ast.NodeVisitor):
41-
IMPORT_ALLOWLIST: Iterable[str] = (
42-
"torchx",
43-
"torchelastic.tsm",
44-
"os.path",
45-
"pytorch.elastic.torchelastic.tsm",
46-
)
47-
48-
FEATURE_BLOCKLIST: Iterable[Type[object]] = (
49-
# statements
50-
ast.FunctionDef,
51-
ast.ClassDef,
52-
ast.Return,
53-
ast.Delete,
54-
ast.For,
55-
ast.AsyncFor,
56-
ast.While,
57-
ast.If,
58-
ast.With,
59-
ast.AsyncWith,
60-
ast.Raise,
61-
ast.Try,
62-
ast.Global,
63-
ast.Nonlocal,
64-
# expressions
65-
ast.ListComp,
66-
ast.SetComp,
67-
ast.DictComp,
68-
# ast.GeneratorExp,
69-
)
70-
71-
def visit(self, node: ast.AST) -> None:
72-
if node.__class__ in self.FEATURE_BLOCKLIST:
73-
raise UnsupportFeatureError(node.__class__.__name__)
74-
75-
super().visit(node)
76-
77-
def _validate_import_path(self, names: List[str]) -> None:
78-
for name in names:
79-
if not any(name.startswith(prefix) for prefix in self.IMPORT_ALLOWLIST):
80-
raise ImportError(
81-
f"import {name} not in allowed import prefixes {self.IMPORT_ALLOWLIST}"
82-
)
83-
84-
def visit_Import(self, node: ast.Import) -> None:
85-
self._validate_import_path([alias.name for alias in node.names])
86-
87-
def visit_ImportFrom(self, node: ast.ImportFrom) -> None:
88-
if module := node.module:
89-
self._validate_import_path([module])
90-
91-
9234
def _parse_run_config(arg: str) -> specs.RunConfig:
9335
conf = specs.RunConfig()
9436
for key, value in parse_args_children(arg).items():
@@ -122,18 +64,17 @@ def _get_component_definition(module: str, function_name: str) -> str:
12264
return f"{module}.{function_name}"
12365

12466

125-
def _get_components_from_file(filepath: str) -> List[BuiltinComponent]:
126-
if filepath.endswith("__init__.py"):
127-
return []
67+
def _to_relative(filepath: str) -> str:
12868
if os.path.isabs(filepath):
129-
if str(COMPONENTS_DIR) not in filepath:
130-
return []
69+
# make path torchx/components/$suffix out of the abs
13170
rel_path = filepath.split(str(COMPONENTS_DIR))[1]
132-
if rel_path[1:].startswith("test"):
133-
return []
134-
components_path = f"{str(COMPONENTS_DIR)}{rel_path}"
71+
return f"{str(COMPONENTS_DIR)}{rel_path}"
13572
else:
136-
components_path = os.path.join(str(COMPONENTS_DIR), filepath)
73+
return os.path.join(str(COMPONENTS_DIR), filepath)
74+
75+
76+
def _get_components_from_file(filepath: str) -> List[BuiltinComponent]:
77+
components_path = _to_relative(filepath)
13778
components_module_path = _to_module(components_path)
13879
module = importlib.import_module(components_module_path)
13980
functions = getmembers(module, isfunction)

torchx/cli/test/cmd_run_test.py

Lines changed: 4 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,12 @@ def setUp(self) -> None:
3838
def tearDown(self) -> None:
3939
shutil.rmtree(self.tmpdir)
4040

41-
def test_run_with_builtin(self) -> None:
42-
foobar_txt = str(self.tmpdir / "foobar.txt")
43-
args = self.parser.parse_args(
44-
["--scheduler", "local", "tests.touch", "--file", foobar_txt]
45-
)
46-
47-
self.cmd_run.run(args)
48-
self.assertTrue(os.path.isfile(foobar_txt))
49-
5041
def test_run_with_user_conf_abs_path(self) -> None:
5142
args = self.parser.parse_args(
5243
[
5344
"--scheduler",
5445
"local",
55-
str(Path(__file__).parent / "examples/test.py:touch"),
46+
str(Path(__file__).parent / "components.py:touch"),
5647
"--file",
5748
str(self.tmpdir / "foobar.txt"),
5849
]
@@ -62,12 +53,12 @@ def test_run_with_user_conf_abs_path(self) -> None:
6253

6354
def test_run_with_relpath(self) -> None:
6455
# should pick up test/examples/touch.torchx (not the builtin)
65-
with cwd(str(Path(__file__).parent / "examples")):
56+
with cwd(str(Path(__file__).parent)):
6657
args = self.parser.parse_args(
6758
[
6859
"--scheduler",
6960
"local",
70-
"tests.touch_v2",
61+
str(Path(__file__).parent / "components.py:touch_v2"),
7162
"--file",
7263
str(self.tmpdir / "foobar.txt"),
7364
]
@@ -95,7 +86,7 @@ def test_run_dryrun(self, mock_runner_run: MagicMock) -> None:
9586
"--verbose",
9687
"--scheduler",
9788
"local",
98-
"tests.echo",
89+
"utils.echo",
9990
]
10091
)
10192
self.cmd_run.run(args)

torchx/cli/test/examples/test.py renamed to torchx/cli/test/components.py

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,6 @@
88
import torchx.specs as specs
99

1010

11-
def echo(msg: str = "hello world", image: str = "/tmp") -> specs.AppDef:
12-
"""Echos a message
13-
14-
Args:
15-
msg: Message to echo
16-
image: Image to run
17-
"""
18-
19-
echo = specs.Role(
20-
name="echo",
21-
image=image,
22-
entrypoint="/bin/echo",
23-
args=[msg],
24-
num_replicas=1,
25-
)
26-
27-
return specs.AppDef(name="echo", roles=[echo])
28-
29-
3011
def touch(file: str) -> specs.AppDef:
3112
"""Echos a message
3213

torchx/cli/test/main_test.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515

1616
_root: Path = Path(__file__).parent
1717

18-
_SIMPLE_CONF: str = "tests.simple"
18+
_SIMPLE_CONF: str = str(Path(__file__).parent / "components.py:simple")
1919

2020

2121
class CLITest(unittest.TestCase):
@@ -25,11 +25,11 @@ def test_run_abs_config_path(self) -> None:
2525
"run",
2626
"--scheduler",
2727
"local",
28-
str(_root / "examples" / "test.py:simple"),
28+
str(_root / "components.py:simple"),
2929
"--num_trainers",
3030
"2",
3131
"--trainer_image",
32-
str(_root / "examples" / "container"),
32+
str(_root / "container"),
3333
]
3434
)
3535

@@ -43,7 +43,7 @@ def test_run_builtin_config(self) -> None:
4343
"--num_trainers",
4444
"2",
4545
"--trainer_image",
46-
str(_root / "examples" / "container"),
46+
str(_root / "container"),
4747
]
4848
)
4949

torchx/components/tests.py

Lines changed: 0 additions & 100 deletions
This file was deleted.

torchx/runner/api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,6 @@ def run_from_path(
107107
AppNotReRunnableException: if the session/scheduler does not support re-running attached apps
108108
ValueError: if the ``component_path`` is failed to resolve.
109109
"""
110-
111110
app_fn = entrypoints.load("torchx.components", component_path, default=NONE)
112111
if app_fn != NONE:
113112
app = from_function(app_fn, app_args)

0 commit comments

Comments
 (0)