Skip to content

Commit f1a1213

Browse files
authored
fix: output action properties at completion of command runner (#62)
Currently ActionRunner when used without --async does not produce any output, making it impossible to see result of `bl server action uptime` command. This PR changes behaviour of ActionRunner so that: - it prints the action's result_data (if any) as part of the final progress message on stderr - any of `--output table/plain/tsv/json` can be used to print the completed action to stdout
1 parent 7981b8d commit f1a1213

5 files changed

Lines changed: 51 additions & 15 deletions

File tree

src/binarylane/console/printers/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from enum import Enum
66

77
from binarylane.console.printers.json_printer import JsonPrinter
8+
from binarylane.console.printers.none_printer import NonePrinter
89
from binarylane.console.printers.plain_printer import PlainPrinter
910
from binarylane.console.printers.printer import Printer
1011
from binarylane.console.printers.table_printer import TablePrinter
@@ -23,6 +24,7 @@ class PrinterType(Enum):
2324
TABLE = TablePrinter
2425
TSV = TsvPrinter
2526
JSON = JsonPrinter
27+
NONE = NonePrinter
2628

2729

2830
def create_printer(name: str) -> Printer:
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from __future__ import annotations
2+
3+
from typing import Any, List, Optional
4+
5+
from binarylane.console.printers.printer import Printer
6+
7+
8+
class NonePrinter(Printer):
9+
def print(self, response: Any, fields: Optional[List[str]] = None) -> None:
10+
pass

src/binarylane/console/runners/action.py

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import shutil
55
import sys
66
import time
7-
from typing import TYPE_CHECKING, Any
7+
from typing import TYPE_CHECKING, Any, Tuple
88

99
from binarylane.console.runners.command import CommandRunner
1010

@@ -21,6 +21,10 @@ class ActionRunner(CommandRunner):
2121
_async: bool = False
2222
_quiet: bool = False
2323

24+
@property
25+
def _default_output(self) -> str:
26+
return "none"
27+
2428
def configure(self, parser: Parser) -> None:
2529
super().configure(parser)
2630

@@ -51,17 +55,14 @@ def _progress(self, progress: str) -> None:
5155
blanks = " " * (shutil.get_terminal_size().columns - 1)
5256
print(f"\r{blanks}\r{progress}", end="", file=sys.stderr)
5357

54-
def response(self, status_code: int, received: Any) -> None:
55-
# If async is requested, use standard handler
56-
if self._async:
57-
super().response(status_code, received)
58-
return
58+
def wait_for_action(self, status_code: int, received: Any) -> Tuple[int, Any]:
59+
"""While received is an ActionResponse, show progress and wait for the action to complete"""
5960

6061
from binarylane.api.actions.get_v2_actions_action_id import sync_detailed
6162
from binarylane.models.action_response import ActionResponse
6263

6364
# FIXME: Extract _get_action(id: int) -> Tuple[int, Any] method so that derived class can call that instead
64-
# Derived class may provide an Action ID directly:
65+
# Caller may provide an Action ID directly:
6566
if isinstance(received, int):
6667
response = sync_detailed(received, client=self._client)
6768
status_code, received = response.status_code, response.parsed
@@ -72,14 +73,25 @@ def response(self, status_code: int, received: Any) -> None:
7273
status = f"{received.action.type}: {step} ({received.action.progress.percent_complete}%) ... "
7374
self._progress(status)
7475

75-
# If action has completed, provide final status and return to caller
76+
# Check if action has completed
7677
if received.action.completed_at:
77-
self._progress(f"{received.action.status}.\n")
78-
return
78+
self._progress(f"{received.action.type}: {received.action.result_data or received.action.status}\n")
79+
break
7980

81+
# Wait and then refresh action response
8082
time.sleep(5)
8183
response = sync_detailed(received.action.id, client=self._client)
8284
status_code, received = response.status_code, response.parsed
8385

84-
# Action did not complete so something went wrong - use standard error handling routine:
86+
return status_code, received
87+
88+
def response(self, status_code: int, received: Any) -> None:
89+
# If async is requested, use standard handler
90+
if self._async:
91+
super().response(status_code, received)
92+
return
93+
94+
status_code, received = self.wait_for_action(status_code, received)
95+
96+
# Action has now completed (or errored), process final response
8597
super().response(status_code, received)

src/binarylane/console/runners/actionlink.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
class ActionLinkRunner(ActionRunner):
99
"""ActionLinkRunner handles command responses with an optional action ID attached"""
1010

11+
@property
12+
def _default_output(self) -> str:
13+
return "table"
14+
1115
def response(self, status_code: int, received: Any) -> None:
1216
from binarylane.models.actions_links import ActionsLinks
1317

@@ -19,7 +23,7 @@ def response(self, status_code: int, received: Any) -> None:
1923
# Show action progress on stdout
2024
if not self._async:
2125
action_id = links.actions[0].id
22-
super().response(status_code, action_id)
26+
self.wait_for_action(status_code, action_id)
2327

2428
# Print the 'other' object (e.g. server) from the response
2529
self._printer.print(received)

src/binarylane/console/runners/command.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from binarylane.models.validation_problem_details import ValidationProblemDetails
99

1010
from binarylane.console.printers import Printer, PrinterType, create_printer
11-
from binarylane.console.runners import ExitCode, Runner
11+
from binarylane.console.runners import Context, ExitCode, Runner
1212
from binarylane.console.runners.httpx_wrapper import CurlCommand
1313
from binarylane.console.util import create_client
1414

@@ -32,9 +32,17 @@ class CommandRunner(Runner):
3232
_client: AuthenticatedClient
3333

3434
_print_curl: Optional[bool]
35-
_output: str = _DEFAULT_OUTPUT
35+
_output: str
3636
_header: bool = _DEFAULT_HEADER
3737

38+
def __init__(self, context: Context) -> None:
39+
super().__init__(context)
40+
self._output = self._default_output
41+
42+
@property
43+
def _default_output(self) -> str:
44+
return _DEFAULT_OUTPUT
45+
3846
@abstractmethod
3947
def create_mapping(self) -> Mapping:
4048
"""Add supported CLI arguments"""
@@ -57,7 +65,7 @@ def configure(self, parser: Parser) -> None:
5765
parser.add_argument(
5866
"--output",
5967
dest="runner_output",
60-
default=_DEFAULT_OUTPUT,
68+
default=self._default_output,
6169
metavar="OUTPUT",
6270
choices=printers,
6371
help='Desired output format [%(choices)s] (default: "%(default)s")',

0 commit comments

Comments
 (0)