torchx-nightly 2025.9.3__py3-none-any.whl → 2025.9.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of torchx-nightly might be problematic. Click here for more details.
- torchx/cli/cmd_run.py +112 -20
- torchx/runner/api.py +7 -3
- torchx/specs/__init__.py +3 -0
- torchx/specs/builders.py +5 -1
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/METADATA +1 -1
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/RECORD +10 -10
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/LICENSE +0 -0
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/WHEEL +0 -0
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/entry_points.txt +0 -0
- {torchx_nightly-2025.9.3.dist-info → torchx_nightly-2025.9.4.dist-info}/top_level.txt +0 -0
torchx/cli/cmd_run.py
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
# pyre-strict
|
|
8
8
|
|
|
9
9
|
import argparse
|
|
10
|
+
import json
|
|
10
11
|
import logging
|
|
11
12
|
import os
|
|
12
13
|
import sys
|
|
@@ -41,6 +42,12 @@ MISSING_COMPONENT_ERROR_MSG = (
|
|
|
41
42
|
"missing component name, either provide it from the CLI or in .torchxconfig"
|
|
42
43
|
)
|
|
43
44
|
|
|
45
|
+
LOCAL_SCHEDULER_WARNING_MSG = (
|
|
46
|
+
"`local` scheduler is deprecated and will be"
|
|
47
|
+
" removed in the near future,"
|
|
48
|
+
" please use other variants of the local scheduler"
|
|
49
|
+
" (e.g. `local_cwd`)"
|
|
50
|
+
)
|
|
44
51
|
|
|
45
52
|
logger: logging.Logger = logging.getLogger(__name__)
|
|
46
53
|
|
|
@@ -54,7 +61,7 @@ class TorchXRunArgs:
|
|
|
54
61
|
dryrun: bool = False
|
|
55
62
|
wait: bool = False
|
|
56
63
|
log: bool = False
|
|
57
|
-
workspace: str =
|
|
64
|
+
workspace: str = ""
|
|
58
65
|
parent_run_id: Optional[str] = None
|
|
59
66
|
tee_logs: bool = False
|
|
60
67
|
component_args: Dict[str, Any] = field(default_factory=dict)
|
|
@@ -83,7 +90,10 @@ def torchx_run_args_from_json(json_data: Dict[str, Any]) -> TorchXRunArgs:
|
|
|
83
90
|
"Please check your JSON and try launching again.",
|
|
84
91
|
)
|
|
85
92
|
|
|
86
|
-
|
|
93
|
+
torchx_args = TorchXRunArgs(**filtered_json_data)
|
|
94
|
+
if torchx_args.workspace == "":
|
|
95
|
+
torchx_args.workspace = f"file://{Path.cwd()}"
|
|
96
|
+
return torchx_args
|
|
87
97
|
|
|
88
98
|
|
|
89
99
|
def torchx_run_args_from_argparse(
|
|
@@ -256,35 +266,35 @@ class CmdRun(SubCommand):
|
|
|
256
266
|
default=False,
|
|
257
267
|
help="Add additional prefix to log lines to indicate which replica is printing the log",
|
|
258
268
|
)
|
|
269
|
+
subparser.add_argument(
|
|
270
|
+
"--stdin",
|
|
271
|
+
action="store_true",
|
|
272
|
+
default=False,
|
|
273
|
+
help="Read JSON input from stdin to parse into torchx run args and run the component.",
|
|
274
|
+
)
|
|
259
275
|
subparser.add_argument(
|
|
260
276
|
"component_name_and_args",
|
|
261
277
|
nargs=argparse.REMAINDER,
|
|
262
278
|
)
|
|
263
279
|
|
|
264
|
-
def
|
|
280
|
+
def _run_inner(self, runner: Runner, args: TorchXRunArgs) -> None:
|
|
265
281
|
if args.scheduler == "local":
|
|
266
|
-
logger.warning(
|
|
267
|
-
"`local` scheduler is deprecated and will be"
|
|
268
|
-
" removed in the near future,"
|
|
269
|
-
" please use other variants of the local scheduler"
|
|
270
|
-
" (e.g. `local_cwd`)"
|
|
271
|
-
)
|
|
272
|
-
|
|
273
|
-
cfg = dict(runner.cfg_from_str(args.scheduler, args.scheduler_args))
|
|
274
|
-
config.apply(scheduler=args.scheduler, cfg=cfg)
|
|
282
|
+
logger.warning(LOCAL_SCHEDULER_WARNING_MSG)
|
|
275
283
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
284
|
+
config.apply(scheduler=args.scheduler, cfg=args.scheduler_cfg)
|
|
285
|
+
component_args = (
|
|
286
|
+
args.component_args_str
|
|
287
|
+
if args.component_args_str != []
|
|
288
|
+
else args.component_args
|
|
279
289
|
)
|
|
280
290
|
try:
|
|
281
291
|
if args.dryrun:
|
|
282
292
|
dryrun_info = runner.dryrun_component(
|
|
283
|
-
|
|
293
|
+
args.component_name,
|
|
284
294
|
component_args,
|
|
285
295
|
args.scheduler,
|
|
286
296
|
workspace=args.workspace,
|
|
287
|
-
cfg=
|
|
297
|
+
cfg=args.scheduler_cfg,
|
|
288
298
|
parent_run_id=args.parent_run_id,
|
|
289
299
|
)
|
|
290
300
|
print(
|
|
@@ -295,11 +305,11 @@ class CmdRun(SubCommand):
|
|
|
295
305
|
print("\n=== SCHEDULER REQUEST ===\n" f"{dryrun_info}")
|
|
296
306
|
else:
|
|
297
307
|
app_handle = runner.run_component(
|
|
298
|
-
|
|
308
|
+
args.component_name,
|
|
299
309
|
component_args,
|
|
300
310
|
args.scheduler,
|
|
301
311
|
workspace=args.workspace,
|
|
302
|
-
cfg=
|
|
312
|
+
cfg=args.scheduler_cfg,
|
|
303
313
|
parent_run_id=args.parent_run_id,
|
|
304
314
|
)
|
|
305
315
|
# DO NOT delete this line. It is used by slurm tests to retrieve the app id
|
|
@@ -320,7 +330,9 @@ class CmdRun(SubCommand):
|
|
|
320
330
|
)
|
|
321
331
|
|
|
322
332
|
except (ComponentValidationException, ComponentNotFoundException) as e:
|
|
323
|
-
error_msg =
|
|
333
|
+
error_msg = (
|
|
334
|
+
f"\nFailed to run component `{args.component_name}` got errors: \n {e}"
|
|
335
|
+
)
|
|
324
336
|
logger.error(error_msg)
|
|
325
337
|
sys.exit(1)
|
|
326
338
|
except specs.InvalidRunConfigException as e:
|
|
@@ -335,6 +347,86 @@ class CmdRun(SubCommand):
|
|
|
335
347
|
print(error_msg % (e, args.scheduler, args.scheduler), file=sys.stderr)
|
|
336
348
|
sys.exit(1)
|
|
337
349
|
|
|
350
|
+
def _run_from_cli_args(self, runner: Runner, args: argparse.Namespace) -> None:
|
|
351
|
+
scheduler_opts = runner.scheduler_run_opts(args.scheduler)
|
|
352
|
+
cfg = scheduler_opts.cfg_from_str(args.scheduler_args)
|
|
353
|
+
|
|
354
|
+
component, component_args = _parse_component_name_and_args(
|
|
355
|
+
args.component_name_and_args,
|
|
356
|
+
none_throws(self._subparser),
|
|
357
|
+
)
|
|
358
|
+
torchx_run_args = torchx_run_args_from_argparse(
|
|
359
|
+
args, component, component_args, cfg
|
|
360
|
+
)
|
|
361
|
+
self._run_inner(runner, torchx_run_args)
|
|
362
|
+
|
|
363
|
+
def _run_from_stdin_args(self, runner: Runner, stdin_data: Dict[str, Any]) -> None:
|
|
364
|
+
torchx_run_args = torchx_run_args_from_json(stdin_data)
|
|
365
|
+
scheduler_opts = runner.scheduler_run_opts(torchx_run_args.scheduler)
|
|
366
|
+
cfg = scheduler_opts.cfg_from_json_repr(
|
|
367
|
+
json.dumps(torchx_run_args.scheduler_args)
|
|
368
|
+
)
|
|
369
|
+
torchx_run_args.scheduler_cfg = cfg
|
|
370
|
+
self._run_inner(runner, torchx_run_args)
|
|
371
|
+
|
|
372
|
+
def torchx_json_from_stdin(self) -> Dict[str, Any]:
|
|
373
|
+
try:
|
|
374
|
+
stdin_data_json = json.load(sys.stdin)
|
|
375
|
+
if not isinstance(stdin_data_json, dict):
|
|
376
|
+
logger.error(
|
|
377
|
+
"Invalid JSON input for `torchx run` command. Expected a dictionary."
|
|
378
|
+
)
|
|
379
|
+
sys.exit(1)
|
|
380
|
+
return stdin_data_json
|
|
381
|
+
except (json.JSONDecodeError, EOFError):
|
|
382
|
+
logger.error(
|
|
383
|
+
"Unable to parse JSON input for `torchx run` command, please make sure it's a valid JSON input."
|
|
384
|
+
)
|
|
385
|
+
sys.exit(1)
|
|
386
|
+
|
|
387
|
+
def verify_no_extra_args(self, args: argparse.Namespace) -> None:
|
|
388
|
+
"""
|
|
389
|
+
Verifies that only --stdin was provided when using stdin mode.
|
|
390
|
+
"""
|
|
391
|
+
if not args.stdin:
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
subparser = none_throws(self._subparser)
|
|
395
|
+
conflicting_args = []
|
|
396
|
+
|
|
397
|
+
# Check each argument against its default value
|
|
398
|
+
for action in subparser._actions:
|
|
399
|
+
if action.dest == "stdin": # Skip stdin itself
|
|
400
|
+
continue
|
|
401
|
+
if action.dest == "help": # Skip help
|
|
402
|
+
continue
|
|
403
|
+
|
|
404
|
+
current_value = getattr(args, action.dest, None)
|
|
405
|
+
default_value = action.default
|
|
406
|
+
|
|
407
|
+
# For arguments that differ from default
|
|
408
|
+
if current_value != default_value:
|
|
409
|
+
# Handle special cases where non-default doesn't mean explicitly set
|
|
410
|
+
if action.dest == "component_name_and_args" and current_value == []:
|
|
411
|
+
continue # Empty list is still default
|
|
412
|
+
print(f"*********\n {default_value} = {current_value}")
|
|
413
|
+
conflicting_args.append(f"--{action.dest.replace('_', '-')}")
|
|
414
|
+
|
|
415
|
+
if conflicting_args:
|
|
416
|
+
subparser.error(
|
|
417
|
+
f"Cannot specify {', '.join(conflicting_args)} when using --stdin. "
|
|
418
|
+
"All configuration should be provided in JSON input."
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
def _run(self, runner: Runner, args: argparse.Namespace) -> None:
|
|
422
|
+
# Verify no conflicting arguments when using to loop over the stdin
|
|
423
|
+
self.verify_no_extra_args(args)
|
|
424
|
+
if args.stdin:
|
|
425
|
+
stdin_data_json = self.torchx_json_from_stdin()
|
|
426
|
+
self._run_from_stdin_args(runner, stdin_data_json)
|
|
427
|
+
else:
|
|
428
|
+
self._run_from_cli_args(runner, args)
|
|
429
|
+
|
|
338
430
|
def run(self, args: argparse.Namespace) -> None:
|
|
339
431
|
os.environ["TORCHX_CONTEXT_NAME"] = os.getenv("TORCHX_CONTEXT_NAME", "cli_run")
|
|
340
432
|
component_defaults = load_sections(prefix="component")
|
torchx/runner/api.py
CHANGED
|
@@ -25,6 +25,7 @@ from typing import (
|
|
|
25
25
|
Type,
|
|
26
26
|
TYPE_CHECKING,
|
|
27
27
|
TypeVar,
|
|
28
|
+
Union,
|
|
28
29
|
)
|
|
29
30
|
|
|
30
31
|
from torchx.runner.events import log_event
|
|
@@ -167,7 +168,7 @@ class Runner:
|
|
|
167
168
|
def run_component(
|
|
168
169
|
self,
|
|
169
170
|
component: str,
|
|
170
|
-
component_args:
|
|
171
|
+
component_args: Union[list[str], dict[str, Any]],
|
|
171
172
|
scheduler: str,
|
|
172
173
|
cfg: Optional[Mapping[str, CfgVal]] = None,
|
|
173
174
|
workspace: Optional[str] = None,
|
|
@@ -226,7 +227,7 @@ class Runner:
|
|
|
226
227
|
def dryrun_component(
|
|
227
228
|
self,
|
|
228
229
|
component: str,
|
|
229
|
-
component_args:
|
|
230
|
+
component_args: Union[list[str], dict[str, Any]],
|
|
230
231
|
scheduler: str,
|
|
231
232
|
cfg: Optional[Mapping[str, CfgVal]] = None,
|
|
232
233
|
workspace: Optional[str] = None,
|
|
@@ -237,10 +238,13 @@ class Runner:
|
|
|
237
238
|
component, but just returns what "would" have run.
|
|
238
239
|
"""
|
|
239
240
|
component_def = get_component(component)
|
|
241
|
+
args_from_cli = component_args if isinstance(component_args, list) else []
|
|
242
|
+
args_from_json = component_args if isinstance(component_args, dict) else {}
|
|
240
243
|
app = materialize_appdef(
|
|
241
244
|
component_def.fn,
|
|
242
|
-
|
|
245
|
+
args_from_cli,
|
|
243
246
|
self._component_defaults.get(component, None),
|
|
247
|
+
args_from_json,
|
|
244
248
|
)
|
|
245
249
|
return self.dryrun(
|
|
246
250
|
app,
|
torchx/specs/__init__.py
CHANGED
torchx/specs/builders.py
CHANGED
|
@@ -213,7 +213,11 @@ def component_args_from_str(
|
|
|
213
213
|
arg_value = getattr(parsed_args, param_name)
|
|
214
214
|
parameter_type = parameter.annotation
|
|
215
215
|
parameter_type = decode_optional(parameter_type)
|
|
216
|
-
|
|
216
|
+
if (
|
|
217
|
+
parameter_type != arg_value.__class__
|
|
218
|
+
and parameter.kind != inspect.Parameter.VAR_POSITIONAL
|
|
219
|
+
):
|
|
220
|
+
arg_value = decode(arg_value, parameter_type)
|
|
217
221
|
if parameter.kind == inspect.Parameter.VAR_POSITIONAL:
|
|
218
222
|
var_args = arg_value
|
|
219
223
|
elif parameter.kind == inspect.Parameter.KEYWORD_ONLY:
|
|
@@ -16,7 +16,7 @@ torchx/cli/cmd_configure.py,sha256=1kTv0qbsbV44So74plAySwWu56pQrqjhfW_kbfdC3Rw,1
|
|
|
16
16
|
torchx/cli/cmd_describe.py,sha256=E5disbHoKTsqYKp2s3DaFW9GDLCCOgdOc3pQoHKoyCs,1283
|
|
17
17
|
torchx/cli/cmd_list.py,sha256=4Y1ZOq-kqJbztoBt56hAW_InJEaJuDAjpKWgMhBw4II,1507
|
|
18
18
|
torchx/cli/cmd_log.py,sha256=v-EZYUDOcG95rEgTnrsmPJMUyxM9Mk8YFAJtUxtgViE,5475
|
|
19
|
-
torchx/cli/cmd_run.py,sha256=
|
|
19
|
+
torchx/cli/cmd_run.py,sha256=BtLl-FBVnf1B5VjyCMFgJrL20-PQN9k9akN6Oa3RGgg,18126
|
|
20
20
|
torchx/cli/cmd_runopts.py,sha256=NWZiP8XpQjfTDJgays2c6MgL_8wxFoeDge6NstaZdKk,1302
|
|
21
21
|
torchx/cli/cmd_status.py,sha256=22IAEmKs0qkG6kJi83u9dRX2Q-ntT7yehVx7FxtY-vQ,2114
|
|
22
22
|
torchx/cli/cmd_tracker.py,sha256=RfLxE4Cq1wfk7k051RtZ8RPJp0pEKSCa3KmTeRs3LF8,5218
|
|
@@ -56,7 +56,7 @@ torchx/pipelines/kfp/__init__.py,sha256=8iJ8lql_fxwuk9VCYSxXnX6tPL228fB5mDZpOs-k
|
|
|
56
56
|
torchx/pipelines/kfp/adapter.py,sha256=5GeHULjb1kxG6wJtYVLpNkgdzUi4iYEaR42VFOwT6fY,9045
|
|
57
57
|
torchx/pipelines/kfp/version.py,sha256=mYBxd6bm4MeR34D--xo-JLQ9wHeAl_ZQLwbItCf9tr0,539
|
|
58
58
|
torchx/runner/__init__.py,sha256=x8Sz7s_tLxPgJgvWIhK4ju9BNZU61uBFywGwDY6CqJs,315
|
|
59
|
-
torchx/runner/api.py,sha256=
|
|
59
|
+
torchx/runner/api.py,sha256=CJmTjoV2kB0FVqeE9B-bYaFyiMuQsZCY32kY13CIk6I,30559
|
|
60
60
|
torchx/runner/config.py,sha256=CBuYQCBj52fs4NclxJbdx5xtDdgNnfDd7XSdHPE1IGo,18267
|
|
61
61
|
torchx/runner/events/__init__.py,sha256=1_y0bojXl3FL0zlAj7BI4Dg5cXKXUmaa2jZbVH0EDUA,5268
|
|
62
62
|
torchx/runner/events/api.py,sha256=pPLfowWTXtN_XcrEDNI45pE6Ijvdc_Gdxq76RduqgGE,2664
|
|
@@ -82,9 +82,9 @@ torchx/schedulers/streams.py,sha256=8_SLezgnWgfv_zXUsJCUM34-h2dtv25NmZuxEwkzmxw,
|
|
|
82
82
|
torchx/schedulers/ray/__init__.py,sha256=fE0IHi1JJpxsNVBNzWNee2thrNXFFRhY94c80RxNSIE,231
|
|
83
83
|
torchx/schedulers/ray/ray_common.py,sha256=pyNYFvTKVwdjDAeCBNbPwAWwVNmlLOJWExfn90XY8u8,610
|
|
84
84
|
torchx/schedulers/ray/ray_driver.py,sha256=RdaCLfth16ky-5PDVOWRe_RuheWJu9xufWux2F9T7iw,12302
|
|
85
|
-
torchx/specs/__init__.py,sha256=
|
|
85
|
+
torchx/specs/__init__.py,sha256=Gw_2actqR_oWFtxEkGXCxGk_yrWK5JDZzwysyyqmXao,6438
|
|
86
86
|
torchx/specs/api.py,sha256=wkhHOxeWH_tFO3npKqPhNg4VX2NH5gPIFEylkPBo3AU,41315
|
|
87
|
-
torchx/specs/builders.py,sha256=
|
|
87
|
+
torchx/specs/builders.py,sha256=aozVl4q3h0mY5DDJCY1M1CyLC9SW66KJy8JIih8bZJo,13810
|
|
88
88
|
torchx/specs/file_linter.py,sha256=QCwob5STTBuy8RsxaevTI-Dk6R8siDJn81LyaOwazes,12333
|
|
89
89
|
torchx/specs/finder.py,sha256=lMCS1hUR-x7j4sAjfJZnKFeS1RlkFTeJtit4EL_ErIo,18182
|
|
90
90
|
torchx/specs/named_resources_aws.py,sha256=ISjHtifRJqB8u7PeAMiyLyO_S0WCaZiK-CFF3qe6JDU,11415
|
|
@@ -115,9 +115,9 @@ torchx/workspace/__init__.py,sha256=FqN8AN4VhR1C_SBY10MggQvNZmyanbbuPuE-JCjkyUY,
|
|
|
115
115
|
torchx/workspace/api.py,sha256=PtDkGTC5lX03pRoYpuMz2KCmM1ZOycRP1UknqvNb97Y,6341
|
|
116
116
|
torchx/workspace/dir_workspace.py,sha256=npNW_IjUZm_yS5r-8hrRkH46ndDd9a_eApT64m1S1T4,2268
|
|
117
117
|
torchx/workspace/docker_workspace.py,sha256=PFu2KQNVC-0p2aKJ-W_BKA9ZOmXdCY2ABEkCExp3udQ,10269
|
|
118
|
-
torchx_nightly-2025.9.
|
|
119
|
-
torchx_nightly-2025.9.
|
|
120
|
-
torchx_nightly-2025.9.
|
|
121
|
-
torchx_nightly-2025.9.
|
|
122
|
-
torchx_nightly-2025.9.
|
|
123
|
-
torchx_nightly-2025.9.
|
|
118
|
+
torchx_nightly-2025.9.4.dist-info/LICENSE,sha256=WVHfXhFC0Ia8LTKt_nJVYobdqTJVg_4J3Crrfm2A8KQ,1721
|
|
119
|
+
torchx_nightly-2025.9.4.dist-info/METADATA,sha256=JbjR1Xl4KB9UbvnpQyD7EXmVS9xI376IdmQh8L7Hdqc,6103
|
|
120
|
+
torchx_nightly-2025.9.4.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
|
|
121
|
+
torchx_nightly-2025.9.4.dist-info/entry_points.txt,sha256=T328AMXeKI3JZnnxfkEew2ZcMN1oQDtkXjMz7lkV-P4,169
|
|
122
|
+
torchx_nightly-2025.9.4.dist-info/top_level.txt,sha256=pxew3bc2gsiViS0zADs0jb6kC5v8o_Yy_85fhHj_J1A,7
|
|
123
|
+
torchx_nightly-2025.9.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|