vortex-cli 4.10.1__tar.gz → 4.11.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 (41) hide show
  1. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/PKG-INFO +1 -1
  2. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/setup.cfg +1 -1
  3. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/cli.py +10 -0
  4. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/clone.py +8 -3
  5. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/copy.py +1 -1
  6. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/main.py +6 -0
  7. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/models.py +17 -9
  8. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/soap.py +17 -13
  9. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/spinner.py +5 -0
  10. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/workspace.py +18 -21
  11. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex_cli.egg-info/PKG-INFO +1 -1
  12. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/LICENSE +0 -0
  13. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/README.md +0 -0
  14. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/setup.py +0 -0
  15. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/__init__.py +0 -0
  16. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/__main__.py +0 -0
  17. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/colour.py +0 -0
  18. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/__init__.py +0 -0
  19. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/clean.py +0 -0
  20. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/code.py +0 -0
  21. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/config.py +0 -0
  22. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/db.py +0 -0
  23. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/delete.py +0 -0
  24. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/docs.py +0 -0
  25. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/execute.py +0 -0
  26. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/find.py +0 -0
  27. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/grep.py +0 -0
  28. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/list.py +0 -0
  29. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/log.py +0 -0
  30. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/new.py +0 -0
  31. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/commands/watch.py +0 -0
  32. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/constants.py +0 -0
  33. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/docs/Blackbook.pdf +0 -0
  34. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/lib/puakma.jar +0 -0
  35. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/logging.py +0 -0
  36. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex/util.py +0 -0
  37. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex_cli.egg-info/SOURCES.txt +0 -0
  38. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex_cli.egg-info/dependency_links.txt +0 -0
  39. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex_cli.egg-info/entry_points.txt +0 -0
  40. {vortex_cli-4.10.1 → vortex_cli-4.11.1}/vortex_cli.egg-info/requires.txt +0 -0
  41. {vortex_cli-4.10.1 → vortex_cli-4.11.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.10.1
3
+ Version: 4.11.1
4
4
  Summary: Vortex CLI
5
5
  Home-page: https://github.com/jordanamos/vortex-cli
6
6
  Author: Jordan Amos
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = vortex_cli
3
- version = 4.10.1
3
+ version = 4.11.1
4
4
  description = Vortex CLI
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -248,6 +248,16 @@ def add_clone_parser(
248
248
  ),
249
249
  type=Path,
250
250
  )
251
+ clone_parser.add_argument(
252
+ "--timeout",
253
+ "-t",
254
+ type=int,
255
+ default=100,
256
+ help=(
257
+ "Set the timeout duration in seconds when using --export. "
258
+ "Default is %(default)s."
259
+ ),
260
+ )
251
261
  _add_server_option(clone_parser)
252
262
  return clone_parser
253
263
 
@@ -25,13 +25,16 @@ async def _aexport_pmx(
25
25
  server: PuakmaServer,
26
26
  app_ids: list[int],
27
27
  output_dir: Path,
28
+ timeout: int, # noqa: ASYNC109
28
29
  ) -> int:
29
30
  tasks = []
30
31
 
31
32
  async with server as s:
32
33
  await s.server_designer.ainitiate_connection()
33
34
  for app_id in app_ids:
34
- task = asyncio.create_task(_aexport_app_pmx(server, app_id, output_dir))
35
+ task = asyncio.create_task(
36
+ _aexport_app_pmx(server, app_id, output_dir, timeout)
37
+ )
35
38
  tasks.append(task)
36
39
 
37
40
  ret = 0
@@ -55,8 +58,9 @@ async def _aexport_app_pmx(
55
58
  server: PuakmaServer,
56
59
  app_id: int,
57
60
  output_dir: Path,
61
+ timeout: int, # noqa: ASYNC109
58
62
  ) -> int:
59
- ret_bytes = await server.download_designer.adownload_pmx(app_id, True)
63
+ ret_bytes = await server.download_designer.adownload_pmx(app_id, True, timeout)
60
64
  now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
61
65
  output_path = output_dir / f"{now}_{server.host}_{app_id}.pmx"
62
66
 
@@ -268,6 +272,7 @@ def clone(
268
272
  server: PuakmaServer,
269
273
  app_ids: list[int],
270
274
  *,
275
+ timeout: int,
271
276
  get_resources: bool = False,
272
277
  open_urls: bool = False,
273
278
  reclone: bool = False,
@@ -289,7 +294,7 @@ def clone(
289
294
  elif not export_path.is_dir():
290
295
  logger.error(f"'{export_path}' is not a directory.")
291
296
  return 1
292
- ret = asyncio.run(_aexport_pmx(server, app_ids, export_path))
297
+ ret = asyncio.run(_aexport_pmx(server, app_ids, export_path, timeout))
293
298
  else:
294
299
  ret = asyncio.run(
295
300
  _aclone_apps(workspace, server, app_ids, get_resources, open_urls)
@@ -75,7 +75,7 @@ async def _acopy_obj(
75
75
  spinner.stop()
76
76
  print(f"Design Object {obj} already exists in {obj.app}.")
77
77
  print("Only the Design data/source will be updated.")
78
- if input("[Y/y] to continue:") not in ["Y", "y"]:
78
+ if input("[Y/y] to continue:") not in ["Y", "y"]: # noqa: ASYNC250
79
79
  logger.error("Operation Cancelled")
80
80
  spinner.start()
81
81
  return 1
@@ -81,6 +81,11 @@ def main(argv: Sequence[str] | None = None) -> int:
81
81
  action="store_true",
82
82
  help="Initialise the workspace and config files, if they don't already exist",
83
83
  )
84
+ parser.add_argument(
85
+ "--no-spin",
86
+ action="store_true",
87
+ help="Disable the spinner",
88
+ )
84
89
 
85
90
  command_parser = parser.add_subparsers(dest="command")
86
91
  cli.add_list_parser(command_parser)
@@ -173,6 +178,7 @@ def main(argv: Sequence[str] | None = None) -> int:
173
178
  open_urls=args.open_urls,
174
179
  reclone=args.reclone,
175
180
  export_path=args.export_path,
181
+ timeout=args.timeout,
176
182
  )
177
183
  elif args.command == "watch":
178
184
  return watch(workspace, server)
@@ -140,9 +140,14 @@ class PuakmaServer:
140
140
  self.soap_path = soap_path
141
141
  self.webdesign_path = webdesign_path
142
142
  self.puakma_db_conn_id = puakma_db_conn_id
143
- self.username = username or input("Enter your Username: ")
144
- self.password = password or getpass.getpass("Enter your Password: ")
145
-
143
+ self.username = (
144
+ username or os.getenv("VORTEX_USERNAME") or input("Enter your Username: ")
145
+ )
146
+ self.password = (
147
+ password
148
+ or os.getenv("VORTEX_PASSWORD")
149
+ or getpass.getpass("Enter your Password: ")
150
+ )
146
151
  self._aclient = httpx.AsyncClient(auth=self.auth)
147
152
  self._client = httpx.Client(auth=self.auth)
148
153
  self.app_designer = AppDesigner(self)
@@ -322,8 +327,9 @@ class PuakmaApplication:
322
327
  return hash((self.host, self.id))
323
328
 
324
329
  @property
325
- def dir_name(self) -> str:
326
- return f"{self.host}_{self.group}_{self.name}"
330
+ def path(self) -> Path:
331
+ # the 'puakma' application doesn't have a group
332
+ return Path(self.host, self.group or "_", self.name)
327
333
 
328
334
  @property
329
335
  def url(self) -> str:
@@ -520,7 +526,7 @@ class DesignObject:
520
526
  def design_path(self, workspace: Workspace) -> DesignPath:
521
527
  return DesignPath(
522
528
  workspace,
523
- workspace.path / self.app.dir_name / self.design_dir / self.file_name,
529
+ workspace.path / self.app.path / self.design_dir / self.file_name,
524
530
  )
525
531
 
526
532
  async def aupload(
@@ -630,7 +636,7 @@ class InvalidDesignPathError(Exception):
630
636
  class DesignPath:
631
637
  """
632
638
  Represents a path to a Design Object. Expects format:
633
- /path/to/workspace/app_dir/design_dir/.../obj
639
+ /path/to/workspace/server/group/app_dir/design_dir/.../obj
634
640
  otherwise a InvalidDesignPathError is raised
635
641
  """
636
642
 
@@ -639,8 +645,10 @@ class DesignPath:
639
645
  ) -> None:
640
646
  try:
641
647
  rel_path = path.relative_to(workspace.path)
642
- app_dir, design_dir, _rem = str(rel_path).split(os.path.sep, maxsplit=2)
643
- app_dir_path = workspace.path / app_dir
648
+ server_dir, group_dir, app_dir, design_dir, _rem = str(rel_path).split(
649
+ os.path.sep, maxsplit=4
650
+ )
651
+ app_dir_path = workspace.path / server_dir / group_dir / app_dir
644
652
  self.app = PuakmaApplication.from_dir(app_dir_path)
645
653
  if must_exist and not path.exists():
646
654
  raise ValueError(f"Design Object '{path}' does not exist")
@@ -33,13 +33,16 @@ _XSI_NULL: Literal["xsi:null"] = "xsi:null"
33
33
  class _SOAPParam(NamedTuple):
34
34
  name: str
35
35
  value: Any
36
- xsi_type: Literal[
37
- "xsd:integer",
38
- "xsd:string",
39
- "xsd:boolean",
40
- "xsd:base64Binary",
41
- "xsd:int",
42
- ] | None = None
36
+ xsi_type: (
37
+ Literal[
38
+ "xsd:integer",
39
+ "xsd:string",
40
+ "xsd:boolean",
41
+ "xsd:base64Binary",
42
+ "xsd:int",
43
+ ]
44
+ | None
45
+ ) = None
43
46
  array_type: Literal["xsd:string"] | None = None
44
47
 
45
48
 
@@ -179,7 +182,7 @@ class _PuakmaSOAPService(ABC):
179
182
  operation: str,
180
183
  params: list[_SOAPParam] | None = None,
181
184
  *,
182
- timeout: int = 20,
185
+ timeout: int = 20, # noqa: ASYNC109
183
186
  ) -> ET.Element:
184
187
  """
185
188
  Builds and sends a SOAP envelope to the service endpoint and returns the parsed
@@ -343,9 +346,9 @@ class DatabaseDesigner(_PuakmaSOAPService):
343
346
  for row in resp.findall(".//row"):
344
347
  ret.append(
345
348
  {
346
- col_lookup[int(col.attrib["index"]) - 1]: col.text
347
- if col.text
348
- else ""
349
+ col_lookup[int(col.attrib["index"]) - 1]: (
350
+ col.text if col.text else ""
351
+ )
349
352
  for col in row
350
353
  }
351
354
  )
@@ -400,6 +403,7 @@ class DownloadDesigner(_PuakmaSOAPService):
400
403
  self,
401
404
  app_id: int,
402
405
  include_source: bool = False,
406
+ timeout: int = 100, # noqa: ASYNC109
403
407
  ) -> bytes:
404
408
  """Returns bytes xml"""
405
409
  operation = "downloadPmx"
@@ -408,9 +412,9 @@ class DownloadDesigner(_PuakmaSOAPService):
408
412
  _SOAPParam("p2", include_source, _XSD_BOOLEAN),
409
413
  ]
410
414
  # This operation takes a while for big applications, set timeout to 100
411
- resp = await self._apost(operation, params, timeout=100)
415
+ resp = await self._apost(operation, params, timeout=timeout)
412
416
  if resp.text is not None:
413
- return base64.b64decode(resp.text)
417
+ return base64.b64decode(resp.text, validate=True)
414
418
  else:
415
419
  return b""
416
420
 
@@ -17,6 +17,7 @@ class Spinner:
17
17
  self.delay = 0.1
18
18
  self.running = False
19
19
  self.thread: threading.Thread | None = None
20
+ self.disabled = not sys.stdout.isatty()
20
21
 
21
22
  def _spin(self) -> None:
22
23
  while self.running:
@@ -25,6 +26,8 @@ class Spinner:
25
26
  time.sleep(0.1)
26
27
 
27
28
  def start(self) -> None:
29
+ if self.disabled:
30
+ return
28
31
  self.running = True
29
32
  self.thread = threading.Thread(target=self._spin)
30
33
  self.thread.start()
@@ -34,6 +37,8 @@ class Spinner:
34
37
  sys.stdout.flush()
35
38
 
36
39
  def stop(self) -> None:
40
+ if self.disabled:
41
+ return
37
42
  self.running = False
38
43
  if self.thread:
39
44
  self.thread.join()
@@ -141,16 +141,16 @@ class Workspace:
141
141
  def mkdir(self, app: PuakmaApplication, force_recreate: bool = False) -> Path:
142
142
  """
143
143
  Creates a .PuakmaApplication.pickle file within a newly created app directory
144
- with the format 'host_group_name' inside the workspace.
144
+ with the format 'host/group/name' inside the workspace.
145
145
  Returns the full path to the new app directory
146
146
  """
147
- app_path = self.path / app.dir_name
148
- if app_path.exists() and force_recreate:
149
- shutil.rmtree(app_path)
150
- app_path.mkdir(exist_ok=True)
151
- with open(app_path / app.MANIFEST_FILE, "wb") as f:
147
+ full_path = self.path / app.path
148
+ if full_path.exists() and force_recreate:
149
+ shutil.rmtree(full_path)
150
+ full_path.mkdir(exist_ok=True, parents=True)
151
+ with open(full_path / app.MANIFEST_FILE, "wb") as f:
152
152
  pickle.dump(app, f)
153
- return app_path
153
+ return full_path
154
154
 
155
155
  def listdir(
156
156
  self, server: PuakmaServer | None = None, *, strict: bool = True
@@ -163,20 +163,17 @@ class Workspace:
163
163
  contain a .PuakmaApplication.pickle file.
164
164
  """
165
165
  ret = []
166
- for sub_dir in self.path.iterdir():
167
- if sub_dir.is_dir():
168
- if strict:
169
- try:
170
- app = PuakmaApplication.from_dir(sub_dir)
171
- if server and app.host != server.host:
172
- continue
173
- except ValueError:
174
- continue
175
- else:
176
- MANIFEST_FILE = sub_dir / PuakmaApplication.MANIFEST_FILE
177
- if not MANIFEST_FILE.exists():
178
- continue
179
- ret.append(sub_dir)
166
+ pattern = "**/.pma"
167
+ if server:
168
+ pattern = f"{server.host}/**/.pma"
169
+ for app_file in self.path.glob(pattern):
170
+ app_dir = app_file.parent
171
+ if strict:
172
+ try:
173
+ PuakmaApplication.from_dir(app_dir)
174
+ except ValueError:
175
+ continue
176
+ ret.append(app_dir)
180
177
  return ret
181
178
 
182
179
  def lookup_design_obj(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.10.1
3
+ Version: 4.11.1
4
4
  Summary: Vortex CLI
5
5
  Home-page: https://github.com/jordanamos/vortex-cli
6
6
  Author: Jordan Amos
File without changes
File without changes
File without changes
File without changes