accli 2.1.0__tar.gz → 2.1.1__tar.gz
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.
- {accli-2.1.0 → accli-2.1.1}/PKG-INFO +9 -9
- {accli-2.1.0 → accli-2.1.1}/README.md +7 -7
- {accli-2.1.0 → accli-2.1.1}/accli/_version.py +1 -1
- {accli-2.1.0 → accli-2.1.1}/accli/cli.py +20 -46
- {accli-2.1.0 → accli-2.1.1}/accli/gui.py +2 -1
- {accli-2.1.0 → accli-2.1.1}/accli/job_views.py +3 -3
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/PKG-INFO +9 -9
- {accli-2.1.0 → accli-2.1.1}/pyproject.toml +1 -1
- {accli-2.1.0 → accli-2.1.1}/tests/test_jobs_commands.py +33 -13
- {accli-2.1.0 → accli-2.1.1}/tests/test_logs_command.py +3 -1
- {accli-2.1.0 → accli-2.1.1}/LICENSE +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/AcceleratorJobProjectService.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/AcceleratorTaskDispatcher.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/AcceleratorTerminalCliProjectService.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/CsvRegionalTimeseriesValidator.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/__init__.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/__main__.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/cleanup-nfs.ps1 +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/common.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/log_timeline.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/mount_downloader.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/nfs_watcher.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/refresh_explorer.ps1 +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/setup-nfs.ps1 +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/token.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli/version_checker.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/SOURCES.txt +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/dependency_links.txt +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/entry_points.txt +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/requires.txt +0 -0
- {accli-2.1.0 → accli-2.1.1}/accli.egg-info/top_level.txt +0 -0
- {accli-2.1.0 → accli-2.1.1}/setup.cfg +0 -0
- {accli-2.1.0 → accli-2.1.1}/tests/test_log_timeline.py +0 -0
- {accli-2.1.0 → accli-2.1.1}/tests/test_mount_lifecycle.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: accli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: IIASA Accelerator Client
|
|
5
5
|
Author-email: Wrufesh S <wrufesh@gmail.com>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -29,7 +29,7 @@ Project-URL: Bug Tracker, https://github.com/iiasa/accli/issues
|
|
|
29
29
|
Classifier: Programming Language :: Python :: 3
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Operating System :: OS Independent
|
|
32
|
-
Requires-Python: >=3.
|
|
32
|
+
Requires-Python: >=3.8
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
35
|
Requires-Dist: urllib3>=1.26.5
|
|
@@ -137,19 +137,19 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
|
|
|
137
137
|
List root jobs, newest first, with each job's type (`standalone`, `children`, `callback` or `children+callback`), its own status and the status of its whole graph:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
accli jobs
|
|
141
|
-
accli jobs
|
|
142
|
-
accli jobs -s failed
|
|
143
|
-
accli jobs -s processing,error
|
|
140
|
+
accli jobs my-project # latest 10
|
|
141
|
+
accli jobs my-project -n 50
|
|
142
|
+
accli jobs my-project -s failed # graphs with a failed job
|
|
143
|
+
accli jobs my-project -s processing,error
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
`--status` filters on the graph status: `processing` (or `active`), `error` (or `failed`) and `done`. A standalone job counts by its own status.
|
|
147
147
|
|
|
148
|
-
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <job>`:
|
|
148
|
+
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <project> <job>`:
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
|
-
accli job 1234
|
|
152
|
-
accli job 1234 --json
|
|
151
|
+
accli job my-project 1234
|
|
152
|
+
accli job my-project 1234 --json
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
---
|
|
@@ -93,19 +93,19 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
|
|
|
93
93
|
List root jobs, newest first, with each job's type (`standalone`, `children`, `callback` or `children+callback`), its own status and the status of its whole graph:
|
|
94
94
|
|
|
95
95
|
```bash
|
|
96
|
-
accli jobs
|
|
97
|
-
accli jobs
|
|
98
|
-
accli jobs -s failed
|
|
99
|
-
accli jobs -s processing,error
|
|
96
|
+
accli jobs my-project # latest 10
|
|
97
|
+
accli jobs my-project -n 50
|
|
98
|
+
accli jobs my-project -s failed # graphs with a failed job
|
|
99
|
+
accli jobs my-project -s processing,error
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
`--status` filters on the graph status: `processing` (or `active`), `error` (or `failed`) and `done`. A standalone job counts by its own status.
|
|
103
103
|
|
|
104
|
-
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <job>`:
|
|
104
|
+
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <project> <job>`:
|
|
105
105
|
|
|
106
106
|
```bash
|
|
107
|
-
accli job 1234
|
|
108
|
-
accli job 1234 --json
|
|
107
|
+
accli job my-project 1234
|
|
108
|
+
accli job my-project 1234 --json
|
|
109
109
|
```
|
|
110
110
|
|
|
111
111
|
---
|
|
@@ -228,9 +228,8 @@ LOG_POLL_SECONDS = 5
|
|
|
228
228
|
|
|
229
229
|
@app.command()
|
|
230
230
|
def logs(
|
|
231
|
+
project_slug: Annotated[str, typer.Argument(help="Unique Accelerator project slug.")],
|
|
231
232
|
job_id: Annotated[int, typer.Argument(help="Job ID.")],
|
|
232
|
-
project_slug: Annotated[
|
|
233
|
-
str, typer.Option(..., '-p', help="Project slug. Defaults to the last project used.")] = None,
|
|
234
233
|
follow: Annotated[
|
|
235
234
|
bool, typer.Option(..., '--follow', '-f', help="Keep printing new lines until the job finishes.")] = False,
|
|
236
235
|
source: Annotated[
|
|
@@ -241,17 +240,7 @@ def logs(
|
|
|
241
240
|
show_time: Annotated[bool, typer.Option('--time/--no-time', help="Show the time of each line.")] = True,
|
|
242
241
|
):
|
|
243
242
|
"""Print a job's merged log: executor output, scheduler output and cluster events."""
|
|
244
|
-
|
|
245
|
-
if not project_slug:
|
|
246
|
-
print("[bold red]ERROR: No project selected. Pass one with -p.[/bold red]")
|
|
247
|
-
raise typer.Exit(1)
|
|
248
|
-
|
|
249
|
-
_, access_token, _ = exchange_refresh_token(project_slug)
|
|
250
|
-
service = AcceleratorTerminalCliProjectService(
|
|
251
|
-
user_token=access_token,
|
|
252
|
-
server_url=get_server_url(),
|
|
253
|
-
verify_cert=(not ACCLI_DEBUG)
|
|
254
|
-
)
|
|
243
|
+
service = _cli_service_for(project_slug)
|
|
255
244
|
sources = ",".join(source) if source else None
|
|
256
245
|
limit = 100 if whole else None
|
|
257
246
|
|
|
@@ -297,19 +286,16 @@ def logs(
|
|
|
297
286
|
|
|
298
287
|
|
|
299
288
|
def _cli_service_for(project_slug):
|
|
300
|
-
"""
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
raise typer.Exit(1)
|
|
305
|
-
|
|
289
|
+
"""Check the project exists and is accessible (as `mount` does), then return a CLI service for it."""
|
|
290
|
+
server_url = get_server_url()
|
|
291
|
+
validate_project_slug(server_url, project_slug)
|
|
292
|
+
# The token from validation is cached in memory, so this exchange makes no extra request.
|
|
306
293
|
_, access_token, _ = exchange_refresh_token(project_slug)
|
|
307
|
-
|
|
294
|
+
return AcceleratorTerminalCliProjectService(
|
|
308
295
|
user_token=access_token,
|
|
309
|
-
server_url=
|
|
296
|
+
server_url=server_url,
|
|
310
297
|
verify_cert=(not ACCLI_DEBUG)
|
|
311
298
|
)
|
|
312
|
-
return project_slug, service
|
|
313
299
|
|
|
314
300
|
|
|
315
301
|
def _api_error_detail(err):
|
|
@@ -321,8 +307,7 @@ def _api_error_detail(err):
|
|
|
321
307
|
|
|
322
308
|
@app.command()
|
|
323
309
|
def jobs(
|
|
324
|
-
project_slug: Annotated[
|
|
325
|
-
str, typer.Option(..., '-p', help="Project slug. Defaults to the last project used.")] = None,
|
|
310
|
+
project_slug: Annotated[str, typer.Argument(help="Unique Accelerator project slug.")],
|
|
326
311
|
status: Annotated[
|
|
327
312
|
List[str], typer.Option(
|
|
328
313
|
..., '--status', '-s',
|
|
@@ -330,35 +315,25 @@ def jobs(
|
|
|
330
315
|
"Only jobs whose graph has this status: processing (or active), error (or failed), done. "
|
|
331
316
|
"A standalone job counts by its own status. Repeatable or comma separated."
|
|
332
317
|
))] = None,
|
|
333
|
-
limit: Annotated[
|
|
334
|
-
|
|
318
|
+
limit: Annotated[
|
|
319
|
+
int, typer.Option(..., '--limit', '-n', min=1, max=200, help="How many jobs to show, up to 200.")] = 10,
|
|
335
320
|
as_json: Annotated[bool, typer.Option(..., '--json', help="Print the raw JSON.")] = False,
|
|
336
321
|
):
|
|
337
322
|
"""List root jobs (newest first) with their type and status.
|
|
338
323
|
|
|
339
324
|
TYPE is standalone, children, callback or children+callback. GRAPH is the status
|
|
340
325
|
of everything below the root job, and what --status filters on. See a job's
|
|
341
|
-
status and everything it holds with `accli job <job>`.
|
|
326
|
+
status and everything it holds with `accli job <project> <job>`.
|
|
342
327
|
"""
|
|
343
|
-
|
|
328
|
+
service = _cli_service_for(project_slug)
|
|
344
329
|
status_filter = ",".join(t.strip() for s in (status or []) for t in s.split(",") if t.strip()) or None
|
|
345
|
-
page_size = 10 if whole else min(limit, 200)
|
|
346
330
|
|
|
347
|
-
results, count, cursor = [], 0, None
|
|
348
331
|
try:
|
|
349
|
-
|
|
350
|
-
page = service.list_root_jobs(project_slug, limit=page_size, cursor=cursor, status=status_filter)
|
|
351
|
-
results += page["results"]
|
|
352
|
-
count = page["count"]
|
|
353
|
-
cursor = page.get("next_cursor")
|
|
354
|
-
if not cursor or (not whole and len(results) >= limit):
|
|
355
|
-
break
|
|
332
|
+
page = service.list_root_jobs(project_slug, limit=limit, status=status_filter)
|
|
356
333
|
except AccAPIError as err:
|
|
357
334
|
print(f"[bold red]ERROR: {_api_error_detail(err)}[/bold red]")
|
|
358
335
|
raise typer.Exit(1)
|
|
359
|
-
|
|
360
|
-
if not whole:
|
|
361
|
-
results = results[:limit]
|
|
336
|
+
results, count = page["results"], page["count"]
|
|
362
337
|
|
|
363
338
|
if as_json:
|
|
364
339
|
typer.echo(json.dumps(dict(count=count, results=results), indent=2, default=str))
|
|
@@ -370,22 +345,21 @@ def jobs(
|
|
|
370
345
|
|
|
371
346
|
print(job_views.root_jobs_table(results))
|
|
372
347
|
if count > len(results):
|
|
373
|
-
print(f"[dim]Showing {len(results)} of {count} jobs. Use -n
|
|
348
|
+
print(f"[dim]Showing {len(results)} of {count} jobs. Use -n to see more.[/dim]")
|
|
374
349
|
|
|
375
350
|
|
|
376
351
|
@app.command()
|
|
377
352
|
def job(
|
|
353
|
+
project_slug: Annotated[str, typer.Argument(help="Unique Accelerator project slug.")],
|
|
378
354
|
job_id: Annotated[int, typer.Argument(help="Job ID: a single job, or one holding children and callbacks.")],
|
|
379
|
-
project_slug: Annotated[
|
|
380
|
-
str, typer.Option(..., '-p', help="Project slug. Defaults to the last project used.")] = None,
|
|
381
355
|
as_json: Annotated[bool, typer.Option(..., '--json', help="Print the raw JSON.")] = False,
|
|
382
356
|
):
|
|
383
357
|
"""Show a job's status and details. For a job holding children or callbacks, Graph status
|
|
384
358
|
covers the jobs below it, the same as GRAPH in `accli jobs`, and each one is listed.
|
|
385
359
|
|
|
386
|
-
See a job's output with `accli logs <job>`.
|
|
360
|
+
See a job's output with `accli logs <project> <job>`.
|
|
387
361
|
"""
|
|
388
|
-
|
|
362
|
+
service = _cli_service_for(project_slug)
|
|
389
363
|
try:
|
|
390
364
|
job_graph = service.get_job_graph(project_slug, job_id)
|
|
391
365
|
except AccAPIError as err:
|
|
@@ -396,7 +370,7 @@ def job(
|
|
|
396
370
|
typer.echo(json.dumps(job_graph, indent=2, default=str))
|
|
397
371
|
return
|
|
398
372
|
|
|
399
|
-
title, details = job_views.job_details(job_graph, job_id)
|
|
373
|
+
title, details = job_views.job_details(job_graph, job_id, project_slug)
|
|
400
374
|
print(title)
|
|
401
375
|
print(details)
|
|
402
376
|
if len(job_views.subtree(job_graph, job_id)) > 1:
|
|
@@ -272,11 +272,12 @@ class AccliGuiApp(tk.Tk):
|
|
|
272
272
|
self.server_entry.configure(state=tk.NORMAL)
|
|
273
273
|
self.status_bar_val.set("Session Status: Disconnected")
|
|
274
274
|
|
|
275
|
+
debug_status = "Enabled ($env:ACCLI_DEBUG='1')" if os.environ.get("ACCLI_DEBUG") else "Disabled"
|
|
275
276
|
details = [
|
|
276
277
|
f"Server URL: {server_url}",
|
|
277
278
|
f"Session Status: {'Connected / Logged In' if logged_in else 'Not Logged In'}",
|
|
278
279
|
f"Token DB Path: {db_path}",
|
|
279
|
-
f"Debug Mode: {
|
|
280
|
+
f"Debug Mode: {debug_status}"
|
|
280
281
|
]
|
|
281
282
|
self.conn_details_txt.configure(state=tk.NORMAL)
|
|
282
283
|
self.conn_details_txt.delete("1.0", tk.END)
|
|
@@ -170,7 +170,7 @@ def _own_status(job):
|
|
|
170
170
|
return text
|
|
171
171
|
|
|
172
172
|
|
|
173
|
-
def job_details(graph, job_id):
|
|
173
|
+
def job_details(graph, job_id, project_slug):
|
|
174
174
|
"""Key/value block for one job. Graph status covers the jobs below it, when it has any."""
|
|
175
175
|
job = next(j for j in graph["jobs"] if j["id"] == job_id)
|
|
176
176
|
jobs = subtree(graph, job_id)
|
|
@@ -199,10 +199,10 @@ def job_details(graph, job_id):
|
|
|
199
199
|
part_of.append(" ")
|
|
200
200
|
part_of.append_text(status_text(graph["graph_status"]))
|
|
201
201
|
part_of.append(f" {len(graph['jobs'])} jobs ", style="dim")
|
|
202
|
-
part_of.append(f"accli job {root['id']}", style="cyan")
|
|
202
|
+
part_of.append(f"accli job {project_slug} {root['id']}", style="cyan")
|
|
203
203
|
table.add_row("Part of", part_of)
|
|
204
204
|
|
|
205
|
-
table.add_row("Logs", Text(f"accli logs {job['id']}", style="cyan"))
|
|
205
|
+
table.add_row("Logs", Text(f"accli logs {project_slug} {job['id']}", style="cyan"))
|
|
206
206
|
|
|
207
207
|
title = Text()
|
|
208
208
|
title.append(f"Job #{job['id']}", style="bold")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: accli
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: IIASA Accelerator Client
|
|
5
5
|
Author-email: Wrufesh S <wrufesh@gmail.com>
|
|
6
6
|
License: The MIT License (MIT)
|
|
@@ -29,7 +29,7 @@ Project-URL: Bug Tracker, https://github.com/iiasa/accli/issues
|
|
|
29
29
|
Classifier: Programming Language :: Python :: 3
|
|
30
30
|
Classifier: License :: OSI Approved :: MIT License
|
|
31
31
|
Classifier: Operating System :: OS Independent
|
|
32
|
-
Requires-Python: >=3.
|
|
32
|
+
Requires-Python: >=3.8
|
|
33
33
|
Description-Content-Type: text/markdown
|
|
34
34
|
License-File: LICENSE
|
|
35
35
|
Requires-Dist: urllib3>=1.26.5
|
|
@@ -137,19 +137,19 @@ Usage: accli [OPTIONS] COMMAND [ARGS]...
|
|
|
137
137
|
List root jobs, newest first, with each job's type (`standalone`, `children`, `callback` or `children+callback`), its own status and the status of its whole graph:
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
|
-
accli jobs
|
|
141
|
-
accli jobs
|
|
142
|
-
accli jobs -s failed
|
|
143
|
-
accli jobs -s processing,error
|
|
140
|
+
accli jobs my-project # latest 10
|
|
141
|
+
accli jobs my-project -n 50
|
|
142
|
+
accli jobs my-project -s failed # graphs with a failed job
|
|
143
|
+
accli jobs my-project -s processing,error
|
|
144
144
|
```
|
|
145
145
|
|
|
146
146
|
`--status` filters on the graph status: `processing` (or `active`), `error` (or `failed`) and `done`. A standalone job counts by its own status.
|
|
147
147
|
|
|
148
|
-
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <job>`:
|
|
148
|
+
Show a job's status and details. For a job holding children or callbacks, Graph status covers the jobs below it (the same as GRAPH in `accli jobs`) and each one is listed; any job also links to the pipeline it is part of. Read a job's output with `accli logs <project> <job>`:
|
|
149
149
|
|
|
150
150
|
```bash
|
|
151
|
-
accli job 1234
|
|
152
|
-
accli job 1234 --json
|
|
151
|
+
accli job my-project 1234
|
|
152
|
+
accli job my-project 1234 --json
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
---
|
|
@@ -11,7 +11,7 @@ authors = [
|
|
|
11
11
|
description = "IIASA Accelerator Client"
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
license = {file = "LICENSE"}
|
|
14
|
-
requires-python = ">=3.
|
|
14
|
+
requires-python = ">=3.8"
|
|
15
15
|
dependencies = [
|
|
16
16
|
"urllib3>=1.26.5", # 2.0.7 is recent, but 1.26.5+ is widely compatible
|
|
17
17
|
"typer[all]>=0.4.0", # 0.4.0+ is stable and has most features
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"""`accli jobs` and `accli job` against a fake API."""
|
|
2
2
|
import json
|
|
3
3
|
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
4
6
|
from typer.testing import CliRunner
|
|
5
7
|
|
|
6
8
|
from accli import cli
|
|
@@ -31,11 +33,12 @@ class FakeService:
|
|
|
31
33
|
return self.graph
|
|
32
34
|
|
|
33
35
|
|
|
34
|
-
def run(monkeypatch, service, *args, exit_code=0):
|
|
36
|
+
def run(monkeypatch, service, command, *args, exit_code=0):
|
|
35
37
|
monkeypatch.setattr(cli, "exchange_refresh_token", lambda slug: ("", "token", 0))
|
|
38
|
+
monkeypatch.setattr(cli, "validate_project_slug", lambda server_url, slug: None)
|
|
36
39
|
monkeypatch.setattr(cli, "get_server_url", lambda: "https://acc.example")
|
|
37
40
|
monkeypatch.setattr(cli, "AcceleratorTerminalCliProjectService", lambda **kw: service)
|
|
38
|
-
result = CliRunner().invoke(cli.app, [
|
|
41
|
+
result = CliRunner().invoke(cli.app, [command, "demo", *args], env={"COLUMNS": "200"})
|
|
39
42
|
assert result.exit_code == exit_code, result.output
|
|
40
43
|
return result.output
|
|
41
44
|
|
|
@@ -48,7 +51,7 @@ def test_jobs_lists_types_and_joins_status_filters(monkeypatch):
|
|
|
48
51
|
])])
|
|
49
52
|
output = run(monkeypatch, service, "jobs", "-s", "error,processing", "--status", "done", "-n", "3")
|
|
50
53
|
|
|
51
|
-
assert service.calls == [dict(limit=3,
|
|
54
|
+
assert service.calls == [dict(limit=3, status="error,processing,done")]
|
|
52
55
|
rows = {line.split()[0]: line for line in output.splitlines() if line.strip()[:1].isdigit()}
|
|
53
56
|
assert "children+callback" in rows["3"] and "PROCESSING" in rows["3"]
|
|
54
57
|
assert "standalone" in rows["2"] and "ERROR" in rows["2"]
|
|
@@ -56,15 +59,17 @@ def test_jobs_lists_types_and_joins_status_filters(monkeypatch):
|
|
|
56
59
|
assert "Showing" not in output
|
|
57
60
|
|
|
58
61
|
|
|
59
|
-
def
|
|
60
|
-
service = FakeService([
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
]
|
|
64
|
-
|
|
62
|
+
def test_jobs_fetches_one_page_and_points_to_n(monkeypatch):
|
|
63
|
+
service = FakeService([dict(count=25, next_cursor="c1", results=[job(2, "b"), job(1, "a")])])
|
|
64
|
+
output = run(monkeypatch, service, "jobs")
|
|
65
|
+
|
|
66
|
+
assert service.calls == [dict(limit=10, status=None)]
|
|
67
|
+
assert "Showing 2 of 25 jobs. Use -n to see more." in output
|
|
65
68
|
|
|
66
|
-
|
|
67
|
-
|
|
69
|
+
|
|
70
|
+
def test_jobs_requires_project(monkeypatch):
|
|
71
|
+
result = CliRunner().invoke(cli.app, ["jobs"])
|
|
72
|
+
assert result.exit_code != 0 and "Missing argument 'project_slug'" in result.output
|
|
68
73
|
|
|
69
74
|
|
|
70
75
|
def test_jobs_shows_api_error(monkeypatch):
|
|
@@ -96,8 +101,8 @@ def test_leaf_job_shows_only_its_own_status(monkeypatch):
|
|
|
96
101
|
assert output.startswith("Job #13 after a")
|
|
97
102
|
assert "Status DONE" in output and "Graph status" not in output
|
|
98
103
|
assert "callback after #11" in output
|
|
99
|
-
assert "Part of #10 root ERROR 5 jobs accli job 10" in output
|
|
100
|
-
assert "accli logs 13" in output
|
|
104
|
+
assert "Part of #10 root ERROR 5 jobs accli job demo 10" in output
|
|
105
|
+
assert "accli logs demo 13" in output
|
|
101
106
|
assert "Jobs in" not in output
|
|
102
107
|
|
|
103
108
|
|
|
@@ -138,3 +143,18 @@ def test_graph_status_leaves_out_the_job_itself(monkeypatch):
|
|
|
138
143
|
output = run(monkeypatch, FakeService(graph=graph), "job", "1")
|
|
139
144
|
assert "Status KILLED" in output
|
|
140
145
|
assert "Graph status DONE 1 jobs below: 1 DONE" in output
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_commands_validate_the_project_first(monkeypatch):
|
|
149
|
+
checked = []
|
|
150
|
+
|
|
151
|
+
def validate(server_url, slug):
|
|
152
|
+
checked.append((server_url, slug))
|
|
153
|
+
raise cli.typer.Exit(1)
|
|
154
|
+
|
|
155
|
+
monkeypatch.setattr(cli, "get_server_url", lambda: "https://acc.example")
|
|
156
|
+
monkeypatch.setattr(cli, "validate_project_slug", validate)
|
|
157
|
+
monkeypatch.setattr(cli, "AcceleratorTerminalCliProjectService", lambda **kw: pytest.fail("API called"))
|
|
158
|
+
for args in (["jobs", "nope"], ["job", "nope", "1"], ["logs", "nope", "1"]):
|
|
159
|
+
assert CliRunner().invoke(cli.app, args).exit_code == 1
|
|
160
|
+
assert checked == [("https://acc.example", "nope")] * 3
|
|
@@ -36,10 +36,11 @@ def chunk(seq, url, end):
|
|
|
36
36
|
def run(monkeypatch, pages, *args):
|
|
37
37
|
service = FakeService(pages)
|
|
38
38
|
monkeypatch.setattr(cli, "exchange_refresh_token", lambda slug: ("", "token", 0))
|
|
39
|
+
monkeypatch.setattr(cli, "validate_project_slug", lambda server_url, slug: None)
|
|
39
40
|
monkeypatch.setattr(cli, "get_server_url", lambda: "https://acc.example")
|
|
40
41
|
monkeypatch.setattr(cli, "AcceleratorTerminalCliProjectService", lambda **kw: service)
|
|
41
42
|
monkeypatch.setattr(cli.time, "sleep", lambda s: None)
|
|
42
|
-
result = CliRunner().invoke(cli.app, ["logs", "
|
|
43
|
+
result = CliRunner().invoke(cli.app, ["logs", "demo", "42", "--no-time", *args])
|
|
43
44
|
assert result.exit_code == 0, result.output
|
|
44
45
|
return result.output.splitlines(), service
|
|
45
46
|
|
|
@@ -65,3 +66,4 @@ def test_without_follow_prints_partial_line_once(monkeypatch):
|
|
|
65
66
|
|
|
66
67
|
assert [line.split(None, 1)[1] for line in output] == ["starting", "downloading 4"]
|
|
67
68
|
assert len(service.calls) == 1
|
|
69
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|