vortex-cli 4.12.1__tar.gz → 4.13.0__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.12.1 → vortex_cli-4.13.0}/PKG-INFO +1 -1
  2. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/setup.cfg +1 -1
  3. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/clone.py +8 -2
  4. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/logging.py +1 -1
  5. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/main.py +19 -16
  6. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/models.py +12 -4
  7. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/soap.py +1 -1
  8. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/util.py +19 -17
  9. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/workspace.py +3 -3
  10. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex_cli.egg-info/PKG-INFO +1 -1
  11. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/LICENSE +0 -0
  12. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/README.md +0 -0
  13. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/setup.py +0 -0
  14. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/__init__.py +0 -0
  15. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/__main__.py +0 -0
  16. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/cli.py +0 -0
  17. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/colour.py +0 -0
  18. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/__init__.py +0 -0
  19. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/clean.py +0 -0
  20. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/code.py +0 -0
  21. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/config.py +0 -0
  22. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/copy.py +0 -0
  23. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/db.py +0 -0
  24. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/delete.py +0 -0
  25. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/docs.py +0 -0
  26. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/execute.py +0 -0
  27. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/find.py +0 -0
  28. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/grep.py +0 -0
  29. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/list.py +0 -0
  30. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/log.py +0 -0
  31. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/new.py +0 -0
  32. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/commands/watch.py +0 -0
  33. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/constants.py +0 -0
  34. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/docs/Blackbook.pdf +0 -0
  35. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/lib/puakma.jar +0 -0
  36. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex/spinner.py +0 -0
  37. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex_cli.egg-info/SOURCES.txt +0 -0
  38. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
  39. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex_cli.egg-info/entry_points.txt +0 -0
  40. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/vortex_cli.egg-info/requires.txt +0 -0
  41. {vortex_cli-4.12.1 → vortex_cli-4.13.0}/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.12.1
3
+ Version: 4.13.0
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.12.1
3
+ version = 4.13.0
4
4
  description = Vortex CLI
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -4,6 +4,8 @@ import asyncio
4
4
  import datetime
5
5
  import logging
6
6
  import xml.etree.ElementTree as ET
7
+ import zipfile
8
+ from io import BytesIO
7
9
  from pathlib import Path
8
10
  from typing import Any
9
11
 
@@ -61,10 +63,14 @@ async def _aexport_app_pmx(
61
63
  timeout: int, # noqa: ASYNC109
62
64
  ) -> int:
63
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
+ _, group, app_name = fname.split("~")
64
69
  now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
65
- output_path = output_dir / f"{now}_{server.host}_{app_id}.pmx"
70
+ export_name = f"{server.host}~{group}~{app_name}~{app_id}~{now}.pmx"
71
+ output_path = output_dir / export_name
66
72
  await asyncio.to_thread(_save_bytes, ret_bytes, output_path)
67
- logger.info(f"Successfully exported {app_id} to {output_dir}")
73
+ logger.info(f"Successfully exported {group}/{app_name} [{app_id}] to {output_dir}")
68
74
  return 0
69
75
 
70
76
 
@@ -114,7 +114,7 @@ class LoggingHandler(logging.Handler):
114
114
 
115
115
 
116
116
  @contextlib.contextmanager
117
- def logging_handler(verbose: bool) -> Generator[None, None, None]:
117
+ def logging_handler(verbose: bool) -> Generator[None]:
118
118
  handler = LoggingHandler()
119
119
  httpx_logger = logging.getLogger("httpx")
120
120
  watchfiles_logger = logging.getLogger("watchfiles")
@@ -37,7 +37,7 @@ logger = logging.getLogger("vortex")
37
37
 
38
38
 
39
39
  @contextlib.contextmanager
40
- def error_handler() -> Generator[None, None, None]:
40
+ def error_handler() -> Generator[None]:
41
41
  try:
42
42
  yield
43
43
  except (WorkspaceError, HTTPStatusError) as e:
@@ -114,7 +114,6 @@ def main(argv: Sequence[str] | None = None) -> int:
114
114
  server_name = getattr(args, "server", None)
115
115
 
116
116
  workspace = Workspace(workspace_path, args.init)
117
- server = workspace.read_server_from_config(server_name)
118
117
 
119
118
  LoggingHandler.workspace = workspace
120
119
 
@@ -123,6 +122,7 @@ def main(argv: Sequence[str] | None = None) -> int:
123
122
  print(workspace.path)
124
123
  return 0
125
124
 
125
+ # Non server commands
126
126
  if args.command == "clean":
127
127
  return clean(workspace)
128
128
  elif args.command == "code":
@@ -131,6 +131,23 @@ def main(argv: Sequence[str] | None = None) -> int:
131
131
  util.print_row_break()
132
132
  remaining_args.insert(0, "--help")
133
133
  return code(workspace, remaining_args)
134
+ elif args.command == "docs":
135
+ return docs()
136
+
137
+ server = workspace.read_server_from_config(server_name)
138
+
139
+ if args.command == "log":
140
+ return log(
141
+ server,
142
+ args.limit,
143
+ args.source,
144
+ args.message,
145
+ args.errors_only,
146
+ args.info_only,
147
+ args.debug_only,
148
+ args.keep_alive,
149
+ args.delay,
150
+ )
134
151
  elif args.command == "config":
135
152
  return config(
136
153
  workspace,
@@ -144,8 +161,6 @@ def main(argv: Sequence[str] | None = None) -> int:
144
161
  list_servers=args.list_servers,
145
162
  set=args.set,
146
163
  )
147
- elif args.command == "docs":
148
- return docs()
149
164
  elif args.command in ("list", "ls"):
150
165
  local_only = args.show_local_only or args.command == "ls"
151
166
  return list_(
@@ -177,18 +192,6 @@ def main(argv: Sequence[str] | None = None) -> int:
177
192
  )
178
193
  elif args.command == "watch":
179
194
  return watch(workspace, server)
180
- elif args.command == "log":
181
- return log(
182
- server,
183
- args.limit,
184
- args.source,
185
- args.message,
186
- args.errors_only,
187
- args.info_only,
188
- args.debug_only,
189
- args.keep_alive,
190
- args.delay,
191
- )
192
195
  elif args.command == "find":
193
196
  return find(
194
197
  workspace,
@@ -308,6 +308,9 @@ class PuakmaApplication:
308
308
  self.id = id
309
309
  self.name = name
310
310
  self.group = group
311
+ # Some apps dont have a group. Adding a palce holder will
312
+ # allow for consistent paths locally
313
+ self.group_safe = group or "~"
311
314
  self.inherit_from = inherit_from
312
315
  self.template_name = template_name
313
316
  self.host = host
@@ -321,7 +324,7 @@ class PuakmaApplication:
321
324
  return self.host == other.host and self.id == other.id
322
325
 
323
326
  def __str__(self) -> str:
324
- return f"{self.group}/{self.name}"
327
+ return f"{self.group_safe}/{self.name}"
325
328
 
326
329
  def __hash__(self) -> int:
327
330
  return hash((self.host, self.id))
@@ -329,11 +332,12 @@ class PuakmaApplication:
329
332
  @property
330
333
  def path(self) -> Path:
331
334
  # the 'puakma' application doesn't have a group
332
- return Path(self.host, self.group or "_", self.name)
335
+ return Path(self.host, self.group_safe, self.name)
333
336
 
334
337
  @property
335
338
  def url(self) -> str:
336
- return f"http://{self.host}/{self.group}/{self.name}.pma"
339
+ base = f"{self.host}/{self.group}" if self.group else self.host
340
+ return f"http://{base}/{self.name}.pma"
337
341
 
338
342
  @property
339
343
  def web_design_url(self) -> str:
@@ -648,6 +652,8 @@ class DesignPath:
648
652
  server_dir, group_dir, app_dir, design_dir, _rem = str(rel_path).split(
649
653
  os.path.sep, maxsplit=4
650
654
  )
655
+
656
+ # path_parts =
651
657
  app_dir_path = workspace.path / server_dir / group_dir / app_dir
652
658
  self.app = PuakmaApplication.from_dir(app_dir_path)
653
659
  if must_exist and not path.exists():
@@ -661,7 +667,9 @@ class DesignPath:
661
667
  self.design_dir = design_dir
662
668
  self.fname = path.name
663
669
  self.design_name, self.ext = os.path.splitext(self.fname)
664
- self.server_path = f"{self.app.group}/{self.app.name}.pma/{self.design_name}"
670
+ # Some apps dont have a group
671
+ group = f"{self.app.group}/" if self.app.group else ""
672
+ self.server_path = f"{group}{self.app.name}.pma/{self.design_name}"
665
673
 
666
674
  def __str__(self) -> str:
667
675
  return str(self.path)
@@ -359,7 +359,7 @@ class DatabaseDesigner(_PuakmaSOAPService):
359
359
  db_conn_id: int,
360
360
  query: str,
361
361
  is_update: bool = False,
362
- ) -> Generator[dict[str, Any], None, None]:
362
+ ) -> Generator[dict[str, Any]]:
363
363
  """Returns a Generator of dicts representing a return row"""
364
364
  operation = "executeQuery"
365
365
  params = [
@@ -33,7 +33,7 @@ if sys.platform == "win32":
33
33
  def _locked(
34
34
  file: IO[Any],
35
35
  blocked_cb: Callable[[], None],
36
- ) -> Generator[None, None, None]:
36
+ ) -> Generator[None]:
37
37
  fileno = file.fileno()
38
38
  _region = 1
39
39
 
@@ -71,7 +71,7 @@ else:
71
71
  def _locked(
72
72
  file: IO[Any],
73
73
  blocked_cb: Callable[[], None],
74
- ) -> Generator[None, None, None]:
74
+ ) -> Generator[None]:
75
75
  fileno = file.fileno()
76
76
  try:
77
77
  fcntl.flock(fileno, fcntl.LOCK_EX | fcntl.LOCK_NB)
@@ -94,7 +94,7 @@ def file_lock(
94
94
  path: os.PathLike[str],
95
95
  blocked_cb: Callable[[], None],
96
96
  mode: str = "a+",
97
- ) -> Generator[IO[Any], None, None]:
97
+ ) -> Generator[IO[Any]]:
98
98
  with open(path, mode) as f:
99
99
  with _locked(f, blocked_cb):
100
100
  yield f
@@ -106,7 +106,7 @@ def execute_cmd(cmd: str, args: list[str]) -> int:
106
106
 
107
107
 
108
108
  @contextlib.contextmanager
109
- def clean_dir_on_failure(path: Path) -> Generator[None, None, None]:
109
+ def clean_dir_on_failure(path: Path) -> Generator[None]:
110
110
  """Cleans up the directory when an exception is raised"""
111
111
  try:
112
112
  yield
@@ -207,22 +207,24 @@ def rmtree(path: Path) -> None:
207
207
  def _handle_race(
208
208
  func: Callable[..., Any],
209
209
  path: str,
210
- exc: tuple[type[OSError], OSError, TracebackType],
210
+ exc: tuple[type[BaseException], BaseException, TracebackType],
211
211
  ) -> None:
212
212
  # Avoid some race conditions
213
213
  excvalue = exc[1]
214
- if isinstance(excvalue, FileNotFoundError):
215
- logger.debug(excvalue.strerror)
216
- elif (
217
- excvalue.errno == errno.ENOTEMPTY
218
- and os.path.exists(path)
219
- and os.path.isdir(path)
220
- ):
221
- logger.debug(f"Failed to remove {path}: {excvalue.strerror}. Retrying...")
222
- shutil.rmtree(path)
223
- logger.debug(f"Managed to remove {path}.")
224
- else:
225
- raise excvalue
214
+ if isinstance(exc, OSError):
215
+ if isinstance(excvalue, FileNotFoundError):
216
+ logger.debug(excvalue.strerror)
217
+ elif (
218
+ excvalue.errno == errno.ENOTEMPTY
219
+ and os.path.exists(path)
220
+ and os.path.isdir(path)
221
+ ):
222
+ logger.debug(
223
+ f"Failed to remove {path}: {excvalue.strerror}. Retrying..."
224
+ )
225
+ shutil.rmtree(path)
226
+ logger.debug(f"Managed to remove {path}.")
227
+ raise excvalue
226
228
 
227
229
  logger.info(f"Cleaning up {path}...")
228
230
  shutil.rmtree(path, ignore_errors=False, onerror=_handle_race)
@@ -52,7 +52,7 @@ class PuakmaApplicationNotFound(WorkspaceError):
52
52
  pass
53
53
 
54
54
 
55
- class _CaseInsensitiveDict(dict[Any, Any]):
55
+ class _CaseInsensitiveDict(dict[str, Any]):
56
56
  def __getitem__(self, key: Any) -> Any:
57
57
  return super().__getitem__(key.lower())
58
58
 
@@ -133,7 +133,7 @@ class Workspace:
133
133
  logger.info(f"Initialised workspace {self.path}")
134
134
 
135
135
  @contextlib.contextmanager
136
- def exclusive_lock(self) -> Generator[None, None, None]:
136
+ def exclusive_lock(self) -> Generator[None]:
137
137
  def _blocked_cb() -> NoReturn:
138
138
  raise WorkspaceInUseError(f"The workspace '{self.path}' is already in use.")
139
139
 
@@ -142,7 +142,7 @@ class Workspace:
142
142
 
143
143
  def mkdir(self, app: PuakmaApplication, force_recreate: bool = False) -> Path:
144
144
  """
145
- Creates a .PuakmaApplication.pickle file within a newly created app directory
145
+ Creates a .pma file within a newly created app directory
146
146
  with the format 'host/group/name' inside the workspace.
147
147
  Returns the full path to the new app directory
148
148
  """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: vortex_cli
3
- Version: 4.12.1
3
+ Version: 4.13.0
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