torchx-nightly 2025.9.3__py3-none-any.whl → 2025.9.5__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 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 = f"file://{Path.cwd()}"
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
- return TorchXRunArgs(**filtered_json_data)
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(
@@ -196,6 +206,7 @@ class CmdBuiltins(SubCommand):
196
206
  class CmdRun(SubCommand):
197
207
  def __init__(self) -> None:
198
208
  self._subparser: Optional[argparse.ArgumentParser] = None
209
+ self._stdin_data_json: Optional[Dict[str, Any]] = None
199
210
 
200
211
  def add_arguments(self, subparser: argparse.ArgumentParser) -> None:
201
212
  scheduler_names = get_scheduler_factories().keys()
@@ -256,35 +267,35 @@ class CmdRun(SubCommand):
256
267
  default=False,
257
268
  help="Add additional prefix to log lines to indicate which replica is printing the log",
258
269
  )
270
+ subparser.add_argument(
271
+ "--stdin",
272
+ action="store_true",
273
+ default=False,
274
+ help="Read JSON input from stdin to parse into torchx run args and run the component.",
275
+ )
259
276
  subparser.add_argument(
260
277
  "component_name_and_args",
261
278
  nargs=argparse.REMAINDER,
262
279
  )
263
280
 
264
- def _run(self, runner: Runner, args: argparse.Namespace) -> None:
281
+ def _run_inner(self, runner: Runner, args: TorchXRunArgs) -> None:
265
282
  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)
283
+ logger.warning(LOCAL_SCHEDULER_WARNING_MSG)
275
284
 
276
- component, component_args = _parse_component_name_and_args(
277
- args.component_name_and_args,
278
- none_throws(self._subparser),
285
+ config.apply(scheduler=args.scheduler, cfg=args.scheduler_cfg)
286
+ component_args = (
287
+ args.component_args_str
288
+ if args.component_args_str != []
289
+ else args.component_args
279
290
  )
280
291
  try:
281
292
  if args.dryrun:
282
293
  dryrun_info = runner.dryrun_component(
283
- component,
294
+ args.component_name,
284
295
  component_args,
285
296
  args.scheduler,
286
297
  workspace=args.workspace,
287
- cfg=cfg,
298
+ cfg=args.scheduler_cfg,
288
299
  parent_run_id=args.parent_run_id,
289
300
  )
290
301
  print(
@@ -295,11 +306,11 @@ class CmdRun(SubCommand):
295
306
  print("\n=== SCHEDULER REQUEST ===\n" f"{dryrun_info}")
296
307
  else:
297
308
  app_handle = runner.run_component(
298
- component,
309
+ args.component_name,
299
310
  component_args,
300
311
  args.scheduler,
301
312
  workspace=args.workspace,
302
- cfg=cfg,
313
+ cfg=args.scheduler_cfg,
303
314
  parent_run_id=args.parent_run_id,
304
315
  )
305
316
  # DO NOT delete this line. It is used by slurm tests to retrieve the app id
@@ -320,7 +331,9 @@ class CmdRun(SubCommand):
320
331
  )
321
332
 
322
333
  except (ComponentValidationException, ComponentNotFoundException) as e:
323
- error_msg = f"\nFailed to run component `{component}` got errors: \n {e}"
334
+ error_msg = (
335
+ f"\nFailed to run component `{args.component_name}` got errors: \n {e}"
336
+ )
324
337
  logger.error(error_msg)
325
338
  sys.exit(1)
326
339
  except specs.InvalidRunConfigException as e:
@@ -335,6 +348,95 @@ class CmdRun(SubCommand):
335
348
  print(error_msg % (e, args.scheduler, args.scheduler), file=sys.stderr)
336
349
  sys.exit(1)
337
350
 
351
+ def _run_from_cli_args(self, runner: Runner, args: argparse.Namespace) -> None:
352
+ scheduler_opts = runner.scheduler_run_opts(args.scheduler)
353
+ cfg = scheduler_opts.cfg_from_str(args.scheduler_args)
354
+
355
+ component, component_args = _parse_component_name_and_args(
356
+ args.component_name_and_args,
357
+ none_throws(self._subparser),
358
+ )
359
+ torchx_run_args = torchx_run_args_from_argparse(
360
+ args, component, component_args, cfg
361
+ )
362
+ self._run_inner(runner, torchx_run_args)
363
+
364
+ def _run_from_stdin_args(self, runner: Runner, stdin_data: Dict[str, Any]) -> None:
365
+ torchx_run_args = torchx_run_args_from_json(stdin_data)
366
+ scheduler_opts = runner.scheduler_run_opts(torchx_run_args.scheduler)
367
+ cfg = scheduler_opts.cfg_from_json_repr(
368
+ json.dumps(torchx_run_args.scheduler_args)
369
+ )
370
+ torchx_run_args.scheduler_cfg = cfg
371
+ self._run_inner(runner, torchx_run_args)
372
+
373
+ def _get_torchx_stdin_args(
374
+ self, args: argparse.Namespace
375
+ ) -> Optional[Dict[str, Any]]:
376
+ if not args.stdin:
377
+ return None
378
+ if self._stdin_data_json is None:
379
+ self._stdin_data_json = self.torchx_json_from_stdin()
380
+ return self._stdin_data_json
381
+
382
+ def torchx_json_from_stdin(self) -> Dict[str, Any]:
383
+ try:
384
+ stdin_data_json = json.load(sys.stdin)
385
+ if not isinstance(stdin_data_json, dict):
386
+ logger.error(
387
+ "Invalid JSON input for `torchx run` command. Expected a dictionary."
388
+ )
389
+ sys.exit(1)
390
+ return stdin_data_json
391
+ except (json.JSONDecodeError, EOFError):
392
+ logger.error(
393
+ "Unable to parse JSON input for `torchx run` command, please make sure it's a valid JSON input."
394
+ )
395
+ sys.exit(1)
396
+
397
+ def verify_no_extra_args(self, args: argparse.Namespace) -> None:
398
+ """
399
+ Verifies that only --stdin was provided when using stdin mode.
400
+ """
401
+ if not args.stdin:
402
+ return
403
+
404
+ subparser = none_throws(self._subparser)
405
+ conflicting_args = []
406
+
407
+ # Check each argument against its default value
408
+ for action in subparser._actions:
409
+ if action.dest == "stdin": # Skip stdin itself
410
+ continue
411
+ if action.dest == "help": # Skip help
412
+ continue
413
+
414
+ current_value = getattr(args, action.dest, None)
415
+ default_value = action.default
416
+
417
+ # For arguments that differ from default
418
+ if current_value != default_value:
419
+ # Handle special cases where non-default doesn't mean explicitly set
420
+ if action.dest == "component_name_and_args" and current_value == []:
421
+ continue # Empty list is still default
422
+ print(f"*********\n {default_value} = {current_value}")
423
+ conflicting_args.append(f"--{action.dest.replace('_', '-')}")
424
+
425
+ if conflicting_args:
426
+ subparser.error(
427
+ f"Cannot specify {', '.join(conflicting_args)} when using --stdin. "
428
+ "All configuration should be provided in JSON input."
429
+ )
430
+
431
+ def _run(self, runner: Runner, args: argparse.Namespace) -> None:
432
+ self.verify_no_extra_args(args)
433
+ if args.stdin:
434
+ stdin_data_json = self._get_torchx_stdin_args(args)
435
+ if stdin_data_json is not None:
436
+ self._run_from_stdin_args(runner, stdin_data_json)
437
+ else:
438
+ self._run_from_cli_args(runner, args)
439
+
338
440
  def run(self, args: argparse.Namespace) -> None:
339
441
  os.environ["TORCHX_CONTEXT_NAME"] = os.getenv("TORCHX_CONTEXT_NAME", "cli_run")
340
442
  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: List[str],
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: List[str],
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
- component_args,
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/runner/config.py CHANGED
@@ -73,7 +73,7 @@ CLI Usage
73
73
 
74
74
  #. In addition, it is possible to specify a different config other than .torchxconfig to
75
75
  load at runtime. Requirements are that the config path is specified by enviornment
76
- variable TORCHX_CONFIG. It also disables hierarchy loading configs from multiple
76
+ variable TORCHXCONFIG. It also disables hierarchy loading configs from multiple
77
77
  directories as the cases otherwise.
78
78
 
79
79
  #. User level .torchxconfig
torchx/specs/__init__.py CHANGED
@@ -225,5 +225,8 @@ __all__ = [
225
225
  "make_app_handle",
226
226
  "materialize_appdef",
227
227
  "parse_mounts",
228
+ "torchx_run_args_from_argparse",
229
+ "torchx_run_args_from_json",
230
+ "TorchXRunArgs",
228
231
  "ALL",
229
232
  ]
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
- arg_value = decode(arg_value, parameter_type)
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:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: torchx-nightly
3
- Version: 2025.9.3
3
+ Version: 2025.9.5
4
4
  Summary: TorchX SDK and Components
5
5
  Home-page: https://github.com/pytorch/torchx
6
6
  Author: TorchX Devs
@@ -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=xSWo1IZB5Nife3AGExalW8DthPlT91xQCf3bdOYOeQQ,14419
19
+ torchx/cli/cmd_run.py,sha256=Ugbn6PUmfVbSlzcrwXSQmdLOEzVnjS5bBUJ5v2Djw0s,18468
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,8 +56,8 @@ 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=I72ecOkUVqq7iMI_6H2_e0TJjU_URh-wLdLVLd5AYX8,30308
60
- torchx/runner/config.py,sha256=CBuYQCBj52fs4NclxJbdx5xtDdgNnfDd7XSdHPE1IGo,18267
59
+ torchx/runner/api.py,sha256=CJmTjoV2kB0FVqeE9B-bYaFyiMuQsZCY32kY13CIk6I,30559
60
+ torchx/runner/config.py,sha256=20X-vveAJVjb1AjjDSC6x_BVcdrTj9_ZLt_CHTykiFo,18266
61
61
  torchx/runner/events/__init__.py,sha256=1_y0bojXl3FL0zlAj7BI4Dg5cXKXUmaa2jZbVH0EDUA,5268
62
62
  torchx/runner/events/api.py,sha256=pPLfowWTXtN_XcrEDNI45pE6Ijvdc_Gdxq76RduqgGE,2664
63
63
  torchx/runner/events/handlers.py,sha256=ThHCIJW21BfBgB7b6ftyjASJmD1KdizpjuTtsyqnvJs,522
@@ -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=c2ALDbqHIhNBhrYxwXXURRwu1Rg5jcwukWF8emEO1Bk,6347
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=R60CdQvHhpGJ83_cwnJDVa_WAf1E_0Sqm92htJKFZsQ,13665
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.3.dist-info/LICENSE,sha256=WVHfXhFC0Ia8LTKt_nJVYobdqTJVg_4J3Crrfm2A8KQ,1721
119
- torchx_nightly-2025.9.3.dist-info/METADATA,sha256=xcGwmuaw3N3-9s2p4kLPrRRugzoq7xOcyrzIZugKAws,6103
120
- torchx_nightly-2025.9.3.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
121
- torchx_nightly-2025.9.3.dist-info/entry_points.txt,sha256=T328AMXeKI3JZnnxfkEew2ZcMN1oQDtkXjMz7lkV-P4,169
122
- torchx_nightly-2025.9.3.dist-info/top_level.txt,sha256=pxew3bc2gsiViS0zADs0jb6kC5v8o_Yy_85fhHj_J1A,7
123
- torchx_nightly-2025.9.3.dist-info/RECORD,,
118
+ torchx_nightly-2025.9.5.dist-info/LICENSE,sha256=WVHfXhFC0Ia8LTKt_nJVYobdqTJVg_4J3Crrfm2A8KQ,1721
119
+ torchx_nightly-2025.9.5.dist-info/METADATA,sha256=Tldr_dcawwnBFNfDrRT1l1l9Ss_3TLBe2WM06JKMiik,6103
120
+ torchx_nightly-2025.9.5.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
121
+ torchx_nightly-2025.9.5.dist-info/entry_points.txt,sha256=T328AMXeKI3JZnnxfkEew2ZcMN1oQDtkXjMz7lkV-P4,169
122
+ torchx_nightly-2025.9.5.dist-info/top_level.txt,sha256=pxew3bc2gsiViS0zADs0jb6kC5v8o_Yy_85fhHj_J1A,7
123
+ torchx_nightly-2025.9.5.dist-info/RECORD,,