Skip to content

Commit 64c8696

Browse files
vdk-control-cli: pass the printer class in JobDeploy creation and add additional memory printer (#2477)
What:Introduced an enhanced feature, the MemoryPrinter, it conserves text for future use, instead of instant printing. Along with this, adapted the JobDeploy class to receive a printer object instead of an OutputFormat enum. Why: The inclusion of MemoryPrinter caters to use cases where an output needs to be stored for later representation, such as in a Jupyter UI. This modification in the JobDeploy class enables the printer object's use beyond JobDeploy: the printer can be initialised first, utilised within JobDeploy, and then accessed again to retrieve the stored output. This stops the need for accessing private fields within JobDeploy and promotes better coding practices. Signed-off-by: Duygu Hasan [[email protected]](mailto:[email protected]) --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent 0e4c5a9 commit 64c8696

File tree

4 files changed

+100
-20
lines changed

4 files changed

+100
-20
lines changed

Diff for: projects/vdk-control-cli/src/vdk/internal/control/command_groups/job/deploy_cli.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from vdk.internal.control.command_groups.job.deploy_cli_impl import JobDeploy
1010
from vdk.internal.control.configuration.defaults_config import load_default_team_name
1111
from vdk.internal.control.utils import cli_utils
12+
from vdk.internal.control.utils import output_printer
1213
from vdk.internal.control.utils.cli_utils import get_or_prompt
1314

1415

@@ -169,7 +170,7 @@ def deploy(
169170
rest_api_url: str,
170171
output: str,
171172
):
172-
cmd = JobDeploy(rest_api_url, output)
173+
cmd = JobDeploy(rest_api_url, output_printer.create_printer(output))
173174
if operation == DeployOperation.UPDATE.value or enabled is not None:
174175
name = get_or_prompt("Job Name", name)
175176
team = get_or_prompt("Job Team", team)

Diff for: projects/vdk-control-cli/src/vdk/internal/control/command_groups/job/deploy_cli_impl.py

+10-10
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@
1919
from vdk.internal.control.job.job_config import JobConfig
2020
from vdk.internal.control.rest_lib.factory import ApiClientFactory
2121
from vdk.internal.control.rest_lib.rest_client_errors import ApiClientErrorDecorator
22-
from vdk.internal.control.utils import output_printer
2322
from vdk.internal.control.utils.cli_utils import get_or_prompt
24-
from vdk.internal.control.utils.output_printer import OutputFormat
23+
from vdk.internal.control.utils.output_printer import Printer
24+
from vdk.internal.control.utils.output_printer import PrinterJson
25+
from vdk.internal.control.utils.output_printer import PrinterText
2526

2627
log = logging.getLogger(__name__)
2728

@@ -30,16 +31,15 @@ class JobDeploy:
3031
ZIP_ARCHIVE_TYPE = "zip"
3132
ARCHIVE_SUFFIX = "-archive"
3233

33-
def __init__(self, rest_api_url: str, output_format: str):
34+
def __init__(self, rest_api_url: str, printer: Printer):
3435
self.deploy_api = ApiClientFactory(rest_api_url).get_deploy_api()
3536
self.jobs_api = ApiClientFactory(rest_api_url).get_jobs_api()
3637
self.job_sources_api = ApiClientFactory(rest_api_url).get_jobs_sources_api()
3738
# support for multiple deployments is not implemented yet so we can put anything here.
3839
# Ultimately this will be user facing parameter (possibly fetched from config.ini)
3940
self.__deployment_id = "production"
4041
self.__job_archive = JobArchive()
41-
self.__output_format = output_format
42-
self.__printer = output_printer.create_printer(self.__output_format)
42+
self.__printer = printer
4343

4444
@staticmethod
4545
def __detect_keytab_files_in_job_directory(job_path: str) -> None:
@@ -193,7 +193,7 @@ def __update_deployment(self, name: str, team: str, deployment: DataJobDeploymen
193193
self.deploy_api.deployment_update(
194194
team_name=team, job_name=name, data_job_deployment=deployment
195195
)
196-
if self.__output_format == OutputFormat.TEXT.value:
196+
if isinstance(self.__printer, PrinterText):
197197
log.info(
198198
f"Request to deploy Data Job {name} using version {deployment.job_version} finished successfully.\n"
199199
f"It would take a few minutes for the Data Job to be deployed in the server.\n"
@@ -239,7 +239,7 @@ def show(self, name: str, team: str) -> None:
239239
),
240240
deployments,
241241
)
242-
if self.__output_format == OutputFormat.TEXT.value:
242+
if isinstance(self.__printer, PrinterText):
243243
click.echo(
244244
"You can compare the version seen here to the one seen when "
245245
"deploying to verify your deployment was successful."
@@ -283,7 +283,7 @@ def create(
283283
"Team Name", team or job_config.get_team() or load_default_team_name()
284284
)
285285

286-
if self.__output_format == OutputFormat.TEXT.value:
286+
if isinstance(self.__printer, PrinterText):
287287
log.info(
288288
f"Deploy Data Job with name {name} from directory {job_path} ... \n"
289289
)
@@ -294,10 +294,10 @@ def create(
294294
try:
295295
job_archive_binary = self.__archive_binary(archive_path)
296296

297-
if self.__output_format == OutputFormat.TEXT.value:
297+
if isinstance(self.__printer, PrinterText):
298298
log.info("Uploading the data job might take some time ...")
299299
with click_spinner.spinner(
300-
disable=(self.__output_format == OutputFormat.JSON.value)
300+
disable=(isinstance(self.__printer, PrinterJson))
301301
):
302302
data_job_version = self.job_sources_api.sources_upload(
303303
team_name=team,

Diff for: projects/vdk-control-cli/src/vdk/internal/control/utils/output_printer.py

+32-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Copyright 2023-2023 VMware, Inc.
22
# SPDX-License-Identifier: Apache-2.0
33
import abc
4+
import io
45
import json
56
from enum import Enum
67
from enum import unique
@@ -60,7 +61,7 @@ def decorator(cls):
6061

6162

6263
@printer("text")
63-
class _PrinterText(Printer):
64+
class PrinterText(Printer):
6465
def print_table(self, table: Optional[List[Dict[str, Any]]]) -> None:
6566
if table and len(table) > 0:
6667
click.echo(tabulate(table, headers="keys", tablefmt="fancy_grid"))
@@ -93,7 +94,7 @@ def json_serial(obj):
9394

9495

9596
@printer("json")
96-
class _PrinterJson(Printer):
97+
class PrinterJson(Printer):
9798
def print_table(self, data: List[Dict[str, Any]]) -> None:
9899
if data:
99100
click.echo(json_format(data))
@@ -107,6 +108,35 @@ def print_dict(self, data: Dict[str, Any]) -> None:
107108
click.echo("{}")
108109

109110

111+
class InMemoryTextPrinter(Printer):
112+
def __init__(self):
113+
self.__output_buffer = io.StringIO()
114+
115+
def print_table(self, table: Optional[List[Dict[str, Any]]]) -> None:
116+
if table and len(table) > 0:
117+
print(
118+
tabulate(table, headers="keys", tablefmt="fancy_grid"),
119+
file=self.__output_buffer,
120+
)
121+
else:
122+
print("No Data.", file=self.__output_buffer)
123+
124+
def print_dict(self, data: Optional[Dict[str, Any]]) -> None:
125+
if data:
126+
print(
127+
tabulate(
128+
[[k, v] for k, v in data.items()],
129+
headers=("key", "value"),
130+
),
131+
file=self.__output_buffer,
132+
)
133+
else:
134+
print("No Data.", file=self.__output_buffer)
135+
136+
def get_memory(self):
137+
return self.__output_buffer.getvalue()
138+
139+
110140
def create_printer(output_format: str) -> Printer:
111141
"""
112142
Creates a printer instance for the given output format.

Diff for: projects/vdk-control-cli/tests/vdk/internal/control/utils/test_output_printer.py

+56-7
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,24 @@
11
# Copyright 2023-2023 VMware, Inc.
22
# SPDX-License-Identifier: Apache-2.0
3+
import unittest
34
from typing import Any
45
from typing import Dict
56
from typing import List
67
from unittest.mock import patch
78

89
import pytest
910
from vdk.internal.control.utils import output_printer
10-
from vdk.internal.control.utils.output_printer import _PrinterJson
11-
from vdk.internal.control.utils.output_printer import _PrinterText
1211
from vdk.internal.control.utils.output_printer import create_printer
12+
from vdk.internal.control.utils.output_printer import InMemoryTextPrinter
1313
from vdk.internal.control.utils.output_printer import Printer
14+
from vdk.internal.control.utils.output_printer import PrinterJson
15+
from vdk.internal.control.utils.output_printer import PrinterText
1416

1517

1618
class TestPrinterText:
1719
def test_print_dict(self):
1820
with patch("click.echo") as mock_echo:
19-
printer = _PrinterText()
21+
printer = PrinterText()
2022
data = {"key": "value"}
2123

2224
printer.print_dict(data)
@@ -26,7 +28,7 @@ def test_print_dict(self):
2628

2729
def test_print_table_with_data(self):
2830
with patch("click.echo") as mock_echo:
29-
printer = _PrinterText()
31+
printer = PrinterText()
3032

3133
data = [{"key1": "value1", "key2": 2}, {"key1": "value3", "key2": 4}]
3234

@@ -45,7 +47,7 @@ def test_print_table_with_data(self):
4547

4648
def test_print_table_with_no_data(self):
4749
with patch("click.echo") as mock_echo:
48-
printer = _PrinterText()
50+
printer = PrinterText()
4951
data = []
5052

5153
printer.print_table(data)
@@ -57,7 +59,7 @@ def test_print_table_with_no_data(self):
5759
class TestPrinterJson:
5860
def test_print_dict(self):
5961
with patch("click.echo") as mock_echo:
60-
printer = _PrinterJson()
62+
printer = PrinterJson()
6163

6264
data = {"key": "value"}
6365

@@ -68,7 +70,7 @@ def test_print_dict(self):
6870

6971
def test_print_table(self):
7072
with patch("click.echo") as mock_echo:
71-
printer = _PrinterJson()
73+
printer = PrinterJson()
7274
data = [
7375
{"key1": "value1", "key2": "value2"},
7476
{"key1": "value3", "key2": "value4"},
@@ -79,6 +81,53 @@ def test_print_table(self):
7981
mock_echo.assert_called_once_with(expected_output)
8082

8183

84+
class TestMemoryPrinter(unittest.TestCase):
85+
def setUp(self):
86+
self.printer = InMemoryTextPrinter()
87+
88+
def test_print_dict(self):
89+
data = {"key": "value"}
90+
91+
self.printer.print_dict(data)
92+
93+
output = self.printer.get_memory().strip()
94+
95+
self.assertIn("key", output)
96+
self.assertIn("value", output)
97+
98+
def test_print_table(self):
99+
data = [
100+
{"key1": "value1", "key2": "value2"},
101+
{"key1": "value3", "key2": "value4"},
102+
]
103+
self.printer.print_table(data)
104+
105+
output = self.printer.get_memory().strip()
106+
107+
self.assertIn("key1", output)
108+
self.assertIn("key2", output)
109+
self.assertIn("value1", output)
110+
self.assertIn("value2", output)
111+
self.assertIn("value3", output)
112+
self.assertIn("value4", output)
113+
114+
def test_print_dict_no_data(self):
115+
self.printer.print_dict(None)
116+
117+
expected_output = "No Data."
118+
actual_output = self.printer.get_memory().strip()
119+
120+
self.assertEqual(actual_output, expected_output)
121+
122+
def test_print_table_no_data(self):
123+
self.printer.print_table(None)
124+
125+
expected_output = "No Data."
126+
actual_output = self.printer.get_memory().strip()
127+
128+
self.assertEqual(actual_output, expected_output)
129+
130+
82131
class TestCreatePrinter:
83132
def test_create_printer_with_registered_format(self):
84133
class MockPrinter(Printer):

0 commit comments

Comments
 (0)