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.
Files changed (42) hide show
  1. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/PKG-INFO +1 -1
  2. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/setup.cfg +1 -1
  3. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/cli.py +39 -0
  4. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/clone.py +4 -84
  5. vortex_cli-4.17.1/vortex/commands/export.py +105 -0
  6. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/new.py +0 -2
  7. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/main.py +10 -1
  8. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/models.py +9 -6
  9. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/PKG-INFO +1 -1
  10. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/SOURCES.txt +1 -0
  11. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/LICENSE +0 -0
  12. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/README.md +0 -0
  13. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/setup.py +0 -0
  14. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/__init__.py +0 -0
  15. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/__main__.py +0 -0
  16. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/colour.py +0 -0
  17. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/__init__.py +0 -0
  18. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/clean.py +0 -0
  19. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/code.py +0 -0
  20. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/config.py +0 -0
  21. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/copy.py +0 -0
  22. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/db.py +0 -0
  23. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/delete.py +0 -0
  24. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/docs.py +0 -0
  25. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/execute.py +0 -0
  26. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/find.py +0 -0
  27. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/grep.py +0 -0
  28. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/list.py +0 -0
  29. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/log.py +0 -0
  30. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/commands/watch.py +0 -0
  31. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/constants.py +0 -0
  32. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/docs/Blackbook.pdf +0 -0
  33. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/lib/puakma-6.0.37.jar +0 -0
  34. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/logging.py +0 -0
  35. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/soap.py +0 -0
  36. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/spinner.py +0 -0
  37. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/util.py +0 -0
  38. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex/workspace.py +0 -0
  39. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/dependency_links.txt +0 -0
  40. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/entry_points.txt +0 -0
  41. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/requires.txt +0 -0
  42. {vortex_cli-4.16.3 → vortex_cli-4.17.1}/vortex_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.16.3
3
+ Version: 4.17.1
4
4
  Home-page: https://github.com/jordanamos/vortex-cli
5
5
  Author: Jordan Amos
6
6
  Author-email: jordan.amos@gmail.com
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = vortex_cli
3
- version = 4.16.3
3
+ version = 4.17.1
4
4
  long_description = file: README.md
5
5
  long_description_content_type = text/markdown
6
6
  url = https://github.com/jordanamos/vortex-cli
@@ -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"{action} {len(app_ids)} application(s)..."),
238
+ Spinner(f"Cloning{len(app_ids)} application(s)..."),
309
239
  ):
310
- if export_path is not None:
311
- if export_path == Path():
312
- export_path = workspace.exports_dir
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
- export_path=args.export_path,
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
- ) -> Literal[False]:
200
- await self._aclient.aclose()
201
- return False
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
- ) -> Literal[False]:
212
+ ) -> None:
209
213
  self._client.close()
210
- return False
211
214
 
212
215
  def fetch_all_apps(
213
216
  self,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.16.3
3
+ Version: 4.17.1
4
4
  Home-page: https://github.com/jordanamos/vortex-cli
5
5
  Author: Jordan Amos
6
6
  Author-email: jordan.amos@gmail.com
@@ -24,6 +24,7 @@ vortex/commands/db.py
24
24
  vortex/commands/delete.py
25
25
  vortex/commands/docs.py
26
26
  vortex/commands/execute.py
27
+ vortex/commands/export.py
27
28
  vortex/commands/find.py
28
29
  vortex/commands/grep.py
29
30
  vortex/commands/list.py
File without changes
File without changes
File without changes
File without changes
File without changes