vortex-cli 4.12.0__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.
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/PKG-INFO +1 -1
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/setup.cfg +1 -1
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/clone.py +8 -2
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/logging.py +1 -1
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/main.py +19 -16
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/models.py +12 -4
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/soap.py +1 -1
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/util.py +19 -17
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/workspace.py +7 -5
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/PKG-INFO +1 -1
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/LICENSE +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/README.md +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/setup.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/__init__.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/__main__.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/cli.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/colour.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/__init__.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/clean.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/code.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/config.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/copy.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/db.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/delete.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/docs.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/execute.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/find.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/grep.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/list.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/log.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/new.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/commands/watch.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/constants.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/docs/Blackbook.pdf +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/lib/puakma.jar +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex/spinner.py +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/SOURCES.txt +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/dependency_links.txt +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/entry_points.txt +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/requires.txt +0 -0
- {vortex_cli-4.12.0 → vortex_cli-4.13.0}/vortex_cli.egg-info/top_level.txt +0 -0
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
335
|
+
return Path(self.host, self.group_safe, self.name)
|
|
333
336
|
|
|
334
337
|
@property
|
|
335
338
|
def url(self) -> str:
|
|
336
|
-
|
|
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
|
-
|
|
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]
|
|
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
|
|
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
|
|
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]
|
|
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
|
|
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[
|
|
210
|
+
exc: tuple[type[BaseException], BaseException, TracebackType],
|
|
211
211
|
) -> None:
|
|
212
212
|
# Avoid some race conditions
|
|
213
213
|
excvalue = exc[1]
|
|
214
|
-
if isinstance(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
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)
|
|
@@ -25,12 +25,14 @@ SAMPLE_CONFIG = """\
|
|
|
25
25
|
[dev]
|
|
26
26
|
host =
|
|
27
27
|
port = 80
|
|
28
|
+
username =
|
|
29
|
+
password =
|
|
28
30
|
soap_path = system/SOAPDesigner.pma
|
|
29
31
|
web_design_path = system/webdesign.pma
|
|
30
32
|
puakma_db_conn_id =
|
|
31
33
|
lib_path =
|
|
32
|
-
|
|
33
|
-
|
|
34
|
+
java_home =
|
|
35
|
+
java_environment_name =
|
|
34
36
|
"""
|
|
35
37
|
|
|
36
38
|
|
|
@@ -50,7 +52,7 @@ class PuakmaApplicationNotFound(WorkspaceError):
|
|
|
50
52
|
pass
|
|
51
53
|
|
|
52
54
|
|
|
53
|
-
class _CaseInsensitiveDict(dict[
|
|
55
|
+
class _CaseInsensitiveDict(dict[str, Any]):
|
|
54
56
|
def __getitem__(self, key: Any) -> Any:
|
|
55
57
|
return super().__getitem__(key.lower())
|
|
56
58
|
|
|
@@ -131,7 +133,7 @@ class Workspace:
|
|
|
131
133
|
logger.info(f"Initialised workspace {self.path}")
|
|
132
134
|
|
|
133
135
|
@contextlib.contextmanager
|
|
134
|
-
def exclusive_lock(self) -> Generator[None
|
|
136
|
+
def exclusive_lock(self) -> Generator[None]:
|
|
135
137
|
def _blocked_cb() -> NoReturn:
|
|
136
138
|
raise WorkspaceInUseError(f"The workspace '{self.path}' is already in use.")
|
|
137
139
|
|
|
@@ -140,7 +142,7 @@ class Workspace:
|
|
|
140
142
|
|
|
141
143
|
def mkdir(self, app: PuakmaApplication, force_recreate: bool = False) -> Path:
|
|
142
144
|
"""
|
|
143
|
-
Creates a .
|
|
145
|
+
Creates a .pma file within a newly created app directory
|
|
144
146
|
with the format 'host/group/name' inside the workspace.
|
|
145
147
|
Returns the full path to the new app directory
|
|
146
148
|
"""
|
|
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
|