vortex-cli 4.16.3__tar.gz → 4.17.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.
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/PKG-INFO +1 -1
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/setup.cfg +1 -1
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/cli.py +39 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/clone.py +4 -84
- vortex_cli-4.17.1/vortex/commands/export.py +105 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/new.py +0 -2
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/main.py +10 -1
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/models.py +9 -6
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/PKG-INFO +1 -1
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/SOURCES.txt +1 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/LICENSE +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/README.md +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/setup.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/__init__.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/__main__.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/colour.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/__init__.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/clean.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/code.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/config.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/copy.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/db.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/delete.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/docs.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/execute.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/find.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/grep.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/list.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/log.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/watch.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/constants.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/docs/Blackbook.pdf +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/lib/puakma-6.0.37.jar +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/logging.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/soap.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/spinner.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/util.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/workspace.py +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/dependency_links.txt +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/entry_points.txt +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/requires.txt +0 -0
- {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/top_level.txt +0 -0
|
@@ -274,6 +274,45 @@ def add_clone_parser(
|
|
|
274
274
|
return clone_parser
|
|
275
275
|
|
|
276
276
|
|
|
277
|
+
def add_export_parser(
|
|
278
|
+
command_parser: _SubParsersAction[ArgumentParser],
|
|
279
|
+
) -> ArgumentParser:
|
|
280
|
+
export_parser = command_parser.add_parser(
|
|
281
|
+
"export",
|
|
282
|
+
help="Export Puakma Applications as a .pmx file",
|
|
283
|
+
)
|
|
284
|
+
export_parser.add_argument(
|
|
285
|
+
"app_ids",
|
|
286
|
+
nargs="+",
|
|
287
|
+
metavar="APP_ID",
|
|
288
|
+
help="The ID(s) of the Puakma Application(s) to export",
|
|
289
|
+
type=int,
|
|
290
|
+
)
|
|
291
|
+
export_parser.add_argument(
|
|
292
|
+
"--out-dir",
|
|
293
|
+
"-o",
|
|
294
|
+
metavar="DIR",
|
|
295
|
+
dest="export_dir",
|
|
296
|
+
help="The destination folder. Default is 'exports' within the workspace.",
|
|
297
|
+
type=Path,
|
|
298
|
+
)
|
|
299
|
+
export_parser.add_argument(
|
|
300
|
+
"--timeout",
|
|
301
|
+
"-t",
|
|
302
|
+
type=int,
|
|
303
|
+
default=100,
|
|
304
|
+
help="Set the timeout duration in seconds. Default is %(default)s.",
|
|
305
|
+
)
|
|
306
|
+
export_parser.add_argument(
|
|
307
|
+
"--exclude-source",
|
|
308
|
+
action="store_false",
|
|
309
|
+
help="Exclude the .java source files",
|
|
310
|
+
dest="include_source",
|
|
311
|
+
)
|
|
312
|
+
_add_server_option(export_parser)
|
|
313
|
+
return export_parser
|
|
314
|
+
|
|
315
|
+
|
|
277
316
|
def add_watch_parser(command_parser: _SubParsersAction[ArgumentParser]) -> None:
|
|
278
317
|
watch_parser = command_parser.add_parser(
|
|
279
318
|
"watch",
|
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
|
-
import datetime
|
|
5
4
|
import logging
|
|
6
5
|
import xml.etree.ElementTree as ET
|
|
7
|
-
import zipfile
|
|
8
|
-
from io import BytesIO
|
|
9
6
|
from pathlib import Path
|
|
10
7
|
from typing import Any
|
|
11
8
|
|
|
@@ -23,64 +20,6 @@ from vortex.workspace import Workspace
|
|
|
23
20
|
logger = logging.getLogger("vortex")
|
|
24
21
|
|
|
25
22
|
|
|
26
|
-
async def _aexport_pmx(
|
|
27
|
-
server: PuakmaServer,
|
|
28
|
-
app_ids: list[int],
|
|
29
|
-
output_dir: Path,
|
|
30
|
-
timeout: int, # noqa: ASYNC109
|
|
31
|
-
) -> int:
|
|
32
|
-
tasks = []
|
|
33
|
-
|
|
34
|
-
async with server as s:
|
|
35
|
-
await s.server_designer.ainitiate_connection()
|
|
36
|
-
for app_id in app_ids:
|
|
37
|
-
task = asyncio.create_task(
|
|
38
|
-
_aexport_app_pmx(server, app_id, output_dir, timeout)
|
|
39
|
-
)
|
|
40
|
-
tasks.append(task)
|
|
41
|
-
|
|
42
|
-
ret = 0
|
|
43
|
-
for done in asyncio.as_completed(tasks):
|
|
44
|
-
try:
|
|
45
|
-
ret |= await done
|
|
46
|
-
except (KeyboardInterrupt, Exception):
|
|
47
|
-
for task in tasks:
|
|
48
|
-
task.cancel()
|
|
49
|
-
raise
|
|
50
|
-
except asyncio.CancelledError:
|
|
51
|
-
logger.error("Operation Cancelled")
|
|
52
|
-
for task in tasks:
|
|
53
|
-
task.cancel()
|
|
54
|
-
ret = 1
|
|
55
|
-
break
|
|
56
|
-
return ret
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
async def _aexport_app_pmx(
|
|
60
|
-
server: PuakmaServer,
|
|
61
|
-
app_id: int,
|
|
62
|
-
output_dir: Path,
|
|
63
|
-
timeout: int, # noqa: ASYNC109
|
|
64
|
-
) -> int:
|
|
65
|
-
ret_bytes = await server.download_designer.adownload_pmx(app_id, True, timeout)
|
|
66
|
-
with zipfile.ZipFile(BytesIO(ret_bytes)) as zip_file:
|
|
67
|
-
fname = zip_file.namelist()[0].replace(".pma", "")
|
|
68
|
-
try:
|
|
69
|
-
_, group, app_name = fname.split("~")
|
|
70
|
-
except ValueError:
|
|
71
|
-
# no group
|
|
72
|
-
logger.warning(f"app has no group {fname}")
|
|
73
|
-
_, app_name = fname.split("~")
|
|
74
|
-
group = ""
|
|
75
|
-
|
|
76
|
-
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
77
|
-
export_name = f"{server.host}~{group}~{app_name}~{app_id}~{now}.pmx"
|
|
78
|
-
output_path = output_dir / export_name
|
|
79
|
-
await asyncio.to_thread(_save_bytes, ret_bytes, output_path)
|
|
80
|
-
logger.info(f"Successfully exported {group}/{app_name} [{app_id}] to {output_dir}")
|
|
81
|
-
return 0
|
|
82
|
-
|
|
83
|
-
|
|
84
23
|
def _save_objs(
|
|
85
24
|
workspace: Workspace, objs: list[DesignObject], save_resources: bool
|
|
86
25
|
) -> None:
|
|
@@ -205,11 +144,6 @@ def _match_and_validate_design_objs(
|
|
|
205
144
|
return new_objects
|
|
206
145
|
|
|
207
146
|
|
|
208
|
-
def _save_bytes(data: bytes, output_path: Path) -> None:
|
|
209
|
-
with open(output_path, "wb") as f:
|
|
210
|
-
f.write(data)
|
|
211
|
-
|
|
212
|
-
|
|
213
147
|
async def _aclone_app(
|
|
214
148
|
workspace: Workspace,
|
|
215
149
|
server: PuakmaServer,
|
|
@@ -292,31 +226,17 @@ def clone(
|
|
|
292
226
|
server: PuakmaServer,
|
|
293
227
|
app_ids: list[int],
|
|
294
228
|
*,
|
|
295
|
-
timeout: int,
|
|
296
229
|
get_resources: bool = False,
|
|
297
230
|
open_urls: bool = False,
|
|
298
231
|
reclone: bool = False,
|
|
299
|
-
export_path: Path | None = None,
|
|
300
232
|
) -> int:
|
|
301
233
|
if reclone:
|
|
302
234
|
app_ids.extend(app.id for app in workspace.listapps(server))
|
|
303
235
|
|
|
304
|
-
action = "Cloning" if export_path is None else "Exporting"
|
|
305
|
-
|
|
306
236
|
with (
|
|
307
237
|
workspace.exclusive_lock(),
|
|
308
|
-
Spinner(f"{
|
|
238
|
+
Spinner(f"Cloning{len(app_ids)} application(s)..."),
|
|
309
239
|
):
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
export_path.mkdir(exist_ok=True)
|
|
314
|
-
elif not export_path.is_dir():
|
|
315
|
-
logger.error(f"'{export_path}' is not a directory.")
|
|
316
|
-
return 1
|
|
317
|
-
ret = asyncio.run(_aexport_pmx(server, app_ids, export_path, timeout))
|
|
318
|
-
else:
|
|
319
|
-
ret = asyncio.run(
|
|
320
|
-
_aclone_apps(workspace, server, app_ids, get_resources, open_urls)
|
|
321
|
-
)
|
|
322
|
-
return ret
|
|
240
|
+
return asyncio.run(
|
|
241
|
+
_aclone_apps(workspace, server, app_ids, get_resources, open_urls)
|
|
242
|
+
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import datetime
|
|
5
|
+
import logging
|
|
6
|
+
import zipfile
|
|
7
|
+
from io import BytesIO
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from vortex.models import PuakmaServer
|
|
11
|
+
from vortex.spinner import Spinner
|
|
12
|
+
from vortex.workspace import Workspace
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger("vortex")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _save_bytes(data: bytes, output_path: Path) -> None:
|
|
18
|
+
with open(output_path, "wb") as f:
|
|
19
|
+
f.write(data)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
async def _aexport(
|
|
23
|
+
server: PuakmaServer,
|
|
24
|
+
app_ids: list[int],
|
|
25
|
+
output_dir: Path,
|
|
26
|
+
timeout: int,
|
|
27
|
+
include_source: bool,
|
|
28
|
+
) -> int:
|
|
29
|
+
tasks = []
|
|
30
|
+
|
|
31
|
+
async with server as s:
|
|
32
|
+
await s.server_designer.ainitiate_connection()
|
|
33
|
+
for app_id in app_ids:
|
|
34
|
+
task = asyncio.create_task(
|
|
35
|
+
_aexport_app_pmx(server, app_id, output_dir, timeout, include_source)
|
|
36
|
+
)
|
|
37
|
+
tasks.append(task)
|
|
38
|
+
|
|
39
|
+
ret = 0
|
|
40
|
+
for done in asyncio.as_completed(tasks):
|
|
41
|
+
try:
|
|
42
|
+
ret |= await done
|
|
43
|
+
except (KeyboardInterrupt, Exception):
|
|
44
|
+
for task in tasks:
|
|
45
|
+
task.cancel()
|
|
46
|
+
raise
|
|
47
|
+
except asyncio.CancelledError:
|
|
48
|
+
logger.error("Operation Cancelled")
|
|
49
|
+
for task in tasks:
|
|
50
|
+
task.cancel()
|
|
51
|
+
ret = 1
|
|
52
|
+
break
|
|
53
|
+
return ret
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
async def _aexport_app_pmx(
|
|
57
|
+
server: PuakmaServer,
|
|
58
|
+
app_id: int,
|
|
59
|
+
output_dir: Path,
|
|
60
|
+
timeout: int, # noqa: ASYNC109
|
|
61
|
+
include_source: bool,
|
|
62
|
+
) -> int:
|
|
63
|
+
ret_bytes = await server.download_designer.adownload_pmx(
|
|
64
|
+
app_id, include_source, timeout
|
|
65
|
+
)
|
|
66
|
+
with zipfile.ZipFile(BytesIO(ret_bytes)) as zip_file:
|
|
67
|
+
fname = zip_file.namelist()[0].replace(".pma", "")
|
|
68
|
+
try:
|
|
69
|
+
_, group, app_name = fname.split("~")
|
|
70
|
+
except ValueError:
|
|
71
|
+
# no group
|
|
72
|
+
_, app_name = fname.split("~")
|
|
73
|
+
group = ""
|
|
74
|
+
|
|
75
|
+
now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
76
|
+
name = f"{group}~{app_name}" if group else app_name
|
|
77
|
+
export_name = f"{server.host}~{name}~{app_id}~{now}.pmx"
|
|
78
|
+
output_path = output_dir / export_name
|
|
79
|
+
await asyncio.to_thread(_save_bytes, ret_bytes, output_path)
|
|
80
|
+
logger.info(
|
|
81
|
+
f"Successfully exported {group}/{app_name} [{app_id}] to {output_dir.resolve()}"
|
|
82
|
+
)
|
|
83
|
+
return 0
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def export(
|
|
87
|
+
workspace: Workspace,
|
|
88
|
+
server: PuakmaServer,
|
|
89
|
+
app_ids: list[int],
|
|
90
|
+
*,
|
|
91
|
+
timeout: int,
|
|
92
|
+
include_source: bool,
|
|
93
|
+
export_dir: Path | None = None,
|
|
94
|
+
) -> int:
|
|
95
|
+
with (
|
|
96
|
+
workspace.exclusive_lock(),
|
|
97
|
+
Spinner(f"Exoprting {len(app_ids)} application(s)..."),
|
|
98
|
+
):
|
|
99
|
+
if export_dir is None:
|
|
100
|
+
export_dir = workspace.exports_dir
|
|
101
|
+
export_dir.mkdir(exist_ok=True)
|
|
102
|
+
ret = asyncio.run(
|
|
103
|
+
_aexport(server, app_ids, export_dir, timeout, include_source)
|
|
104
|
+
)
|
|
105
|
+
return ret
|
|
@@ -181,7 +181,6 @@ def _validate_update_objs(
|
|
|
181
181
|
design_obj_ids: list[int],
|
|
182
182
|
**kwargs: Any,
|
|
183
183
|
) -> tuple[tuple[DesignObject, ...], bool]:
|
|
184
|
-
|
|
185
184
|
_universal_keys = ("name", "design_type", "comment", "inherit_from")
|
|
186
185
|
objs = []
|
|
187
186
|
rows = []
|
|
@@ -228,7 +227,6 @@ def _update_objects(
|
|
|
228
227
|
design_obj_ids: list[int],
|
|
229
228
|
**kwargs: Any,
|
|
230
229
|
) -> int:
|
|
231
|
-
|
|
232
230
|
try:
|
|
233
231
|
updated_objs, recreate_params = _validate_update_objs(
|
|
234
232
|
workspace, server, design_obj_ids, **kwargs
|
|
@@ -21,6 +21,7 @@ from vortex.commands.db import db
|
|
|
21
21
|
from vortex.commands.delete import delete
|
|
22
22
|
from vortex.commands.docs import docs
|
|
23
23
|
from vortex.commands.execute import execute
|
|
24
|
+
from vortex.commands.export import export
|
|
24
25
|
from vortex.commands.find import find
|
|
25
26
|
from vortex.commands.grep import grep
|
|
26
27
|
from vortex.commands.list import list_
|
|
@@ -94,6 +95,7 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
94
95
|
cli.add_watch_parser(command_parser)
|
|
95
96
|
cli.add_docs_parser(command_parser)
|
|
96
97
|
cli.add_execute_parser(command_parser)
|
|
98
|
+
cli.add_export_parser(command_parser)
|
|
97
99
|
clone_parser = cli.add_clone_parser(command_parser)
|
|
98
100
|
code_parser = cli.add_code_parser(command_parser)
|
|
99
101
|
db_parser = cli.add_db_parser(command_parser)
|
|
@@ -187,8 +189,15 @@ def main(argv: Sequence[str] | None = None) -> int:
|
|
|
187
189
|
get_resources=args.get_resources,
|
|
188
190
|
open_urls=args.open_urls,
|
|
189
191
|
reclone=args.reclone,
|
|
190
|
-
|
|
192
|
+
)
|
|
193
|
+
elif args.command == "export":
|
|
194
|
+
return export(
|
|
195
|
+
workspace,
|
|
196
|
+
server,
|
|
197
|
+
args.app_ids,
|
|
191
198
|
timeout=args.timeout,
|
|
199
|
+
export_dir=args.export_dir,
|
|
200
|
+
include_source=args.include_source,
|
|
192
201
|
)
|
|
193
202
|
elif args.command == "watch":
|
|
194
203
|
return watch(workspace, server)
|
|
@@ -15,7 +15,6 @@ from io import StringIO
|
|
|
15
15
|
from pathlib import Path
|
|
16
16
|
from types import TracebackType
|
|
17
17
|
from typing import Any
|
|
18
|
-
from typing import Literal
|
|
19
18
|
from typing import NamedTuple
|
|
20
19
|
from typing import TYPE_CHECKING
|
|
21
20
|
|
|
@@ -196,18 +195,22 @@ class PuakmaServer:
|
|
|
196
195
|
exc_type: type[BaseException] | None,
|
|
197
196
|
exc_val: BaseException | None,
|
|
198
197
|
exc_tb: TracebackType | None = None,
|
|
199
|
-
) ->
|
|
200
|
-
|
|
201
|
-
|
|
198
|
+
) -> None:
|
|
199
|
+
try:
|
|
200
|
+
await asyncio.wait_for(
|
|
201
|
+
asyncio.shield(self._aclient.aclose()),
|
|
202
|
+
timeout=10.0,
|
|
203
|
+
)
|
|
204
|
+
except asyncio.TimeoutError:
|
|
205
|
+
logger.error("Timeout occurred while closing the client.")
|
|
202
206
|
|
|
203
207
|
def __exit__(
|
|
204
208
|
self,
|
|
205
209
|
exc_type: type[BaseException] | None,
|
|
206
210
|
exc_val: BaseException | None,
|
|
207
211
|
exc_tb: TracebackType | None = None,
|
|
208
|
-
) ->
|
|
212
|
+
) -> None:
|
|
209
213
|
self._client.close()
|
|
210
|
-
return False
|
|
211
214
|
|
|
212
215
|
def fetch_all_apps(
|
|
213
216
|
self,
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|