kaqing 2.0.14__py3-none-any.whl → 2.0.189__py3-none-any.whl
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.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +20 -6
- adam/batch.py +16 -6
- adam/checks/check_utils.py +19 -49
- adam/checks/compactionstats.py +1 -1
- adam/checks/cpu.py +9 -3
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +3 -4
- adam/checks/gossip.py +1 -1
- adam/checks/memory.py +3 -3
- adam/checks/status.py +1 -1
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +66 -0
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +8 -14
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +9 -12
- adam/commands/{show → app}/show_app_queues.py +8 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/__init__.py +0 -0
- adam/commands/audit/audit.py +67 -0
- adam/commands/audit/audit_repair_tables.py +72 -0
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +36 -0
- adam/commands/audit/show_slow10.py +36 -0
- adam/commands/audit/show_top10.py +36 -0
- adam/commands/audit/utils_show_top10.py +71 -0
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +36 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +36 -0
- adam/commands/cd.py +14 -88
- adam/commands/check.py +18 -21
- adam/commands/cli_commands.py +11 -7
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +57 -0
- adam/commands/command.py +220 -19
- adam/commands/commands_utils.py +28 -31
- adam/commands/cql/__init__.py +0 -0
- adam/commands/cql/completions_c.py +28 -0
- adam/commands/{cqlsh.py → cql/cqlsh.py} +13 -32
- adam/commands/cql/utils_cql.py +305 -0
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +5 -5
- adam/commands/deploy/deploy.py +4 -40
- adam/commands/deploy/deploy_frontend.py +15 -18
- adam/commands/deploy/deploy_pg_agent.py +4 -7
- adam/commands/deploy/deploy_pod.py +74 -77
- adam/commands/deploy/deploy_utils.py +16 -26
- adam/commands/deploy/undeploy.py +4 -40
- adam/commands/deploy/undeploy_frontend.py +5 -8
- adam/commands/deploy/undeploy_pg_agent.py +7 -8
- adam/commands/deploy/undeploy_pod.py +16 -17
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +149 -0
- adam/commands/devices/device_app.py +163 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +179 -0
- adam/commands/devices/device_export.py +87 -0
- adam/commands/devices/device_postgres.py +160 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +39 -0
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +39 -0
- adam/commands/export/drop_export_databases.py +37 -0
- adam/commands/export/export.py +37 -0
- adam/commands/export/export_databases.py +246 -0
- adam/commands/export/export_select.py +34 -0
- adam/commands/export/export_sessions.py +209 -0
- adam/commands/export/export_use.py +49 -0
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +332 -0
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +44 -0
- adam/commands/export/importer.py +81 -0
- adam/commands/export/importer_athena.py +148 -0
- adam/commands/export/importer_sqlite.py +67 -0
- adam/commands/export/show_column_counts.py +45 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +39 -0
- adam/commands/export/show_export_sessions.py +37 -0
- adam/commands/export/utils_export.py +344 -0
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +14 -9
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +14 -40
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +26 -25
- adam/commands/logs.py +5 -7
- adam/commands/ls.py +11 -115
- adam/commands/medusa/medusa.py +4 -46
- adam/commands/medusa/medusa_backup.py +22 -29
- adam/commands/medusa/medusa_restore.py +51 -49
- adam/commands/medusa/medusa_show_backupjobs.py +20 -21
- adam/commands/medusa/medusa_show_restorejobs.py +16 -21
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +8 -17
- adam/commands/param_get.py +11 -14
- adam/commands/param_set.py +9 -13
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +49 -73
- adam/commands/postgres/postgres_databases.py +270 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +10 -69
- adam/commands/pwd.py +14 -43
- adam/commands/reaper/reaper.py +6 -49
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +8 -15
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -46
- adam/commands/repair/repair_log.py +6 -12
- adam/commands/repair/repair_run.py +29 -36
- adam/commands/repair/repair_scan.py +33 -41
- adam/commands/repair/repair_stop.py +6 -13
- adam/commands/report.py +25 -21
- adam/commands/restart.py +27 -28
- adam/commands/rollout.py +20 -25
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +15 -46
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +48 -52
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +33 -0
- adam/commands/show/show_login.py +23 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +27 -30
- adam/config.py +8 -6
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +13 -5
- adam/repl.py +136 -120
- adam/repl_commands.py +66 -24
- adam/repl_session.py +8 -1
- adam/repl_state.py +343 -73
- adam/sql/__init__.py +0 -0
- adam/sql/lark_completer.py +284 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/sql_completer.py +118 -0
- adam/sql/sql_state_machine.py +630 -0
- adam/sql/term_completer.py +76 -0
- adam/sso/authn_ad.py +7 -9
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +4 -6
- adam/sso/idp.py +10 -13
- adam/utils.py +539 -11
- adam/utils_athena.py +145 -0
- adam/utils_audits.py +102 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/__init__.py +0 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +36 -0
- adam/utils_k8s/cassandra_clusters.py +44 -0
- adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
- adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
- adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
- adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
- adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/{k8s_utils → utils_k8s}/kube_context.py +3 -3
- adam/{k8s_utils → utils_k8s}/pods.py +132 -83
- adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
- adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
- adam/{k8s_utils → utils_k8s}/services.py +2 -2
- adam/{k8s_utils → utils_k8s}/statefulsets.py +9 -16
- adam/utils_local.py +4 -0
- adam/utils_net.py +24 -0
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +172 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
- kaqing-2.0.189.dist-info/RECORD +253 -0
- kaqing-2.0.189.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/app.py +0 -67
- adam/commands/bash.py +0 -87
- adam/commands/cp.py +0 -95
- adam/commands/cql_utils.py +0 -53
- adam/commands/devices.py +0 -89
- adam/commands/postgres/postgres_session.py +0 -247
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -53
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- adam/k8s_utils/cassandra_clusters.py +0 -48
- kaqing-2.0.14.dist-info/RECORD +0 -167
- kaqing-2.0.14.dist-info/top_level.txt +0 -1
- /adam/{k8s_utils → commands/app}/__init__.py +0 -0
- /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
- /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
2
|
+
import copy
|
|
3
|
+
import inspect
|
|
4
|
+
import re
|
|
5
|
+
import threading
|
|
6
|
+
import traceback
|
|
7
|
+
from typing import Iterable, TypeVar, cast
|
|
8
|
+
from prompt_toolkit.completion import CompleteEvent, Completer, Completion, NestedCompleter, WordCompleter
|
|
9
|
+
from prompt_toolkit.document import Document
|
|
10
|
+
|
|
11
|
+
from adam.utils import debug, debug_complete, log2, log_timing
|
|
12
|
+
from adam.utils_repl.appendable_completer import AppendableCompleter
|
|
13
|
+
|
|
14
|
+
import nest_asyncio
|
|
15
|
+
nest_asyncio.apply()
|
|
16
|
+
|
|
17
|
+
import asyncio
|
|
18
|
+
|
|
19
|
+
__all__ = [
|
|
20
|
+
"ReplCompleter",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
T = TypeVar('T')
|
|
24
|
+
|
|
25
|
+
def merge_completions(dict1, dict2):
|
|
26
|
+
if isinstance(dict1, dict):
|
|
27
|
+
target = dict1.copy()
|
|
28
|
+
else:
|
|
29
|
+
target = copy.copy(dict1)
|
|
30
|
+
|
|
31
|
+
try:
|
|
32
|
+
for key, value in dict2.items():
|
|
33
|
+
if key in target:
|
|
34
|
+
debug_complete(f'[{key}] {type(dict2)} is being merged to {type(target[key])} completions')
|
|
35
|
+
if isinstance(value, dict):
|
|
36
|
+
if isinstance(target[key], dict):
|
|
37
|
+
target[key] = merge_completions(target[key], value)
|
|
38
|
+
elif isinstance(target[key], AppendableCompleter):
|
|
39
|
+
cast(AppendableCompleter, target[key]).append_completions(key, value)
|
|
40
|
+
elif isinstance(value, AppendableCompleter):
|
|
41
|
+
if isinstance(target[key], dict):
|
|
42
|
+
cast(AppendableCompleter, value).append_completions(key, target[key])
|
|
43
|
+
target[key] = value
|
|
44
|
+
else:
|
|
45
|
+
log2(f'* {key} of {type(value)} is overriding existing {type(target[key])} completions')
|
|
46
|
+
else:
|
|
47
|
+
target[key] = value
|
|
48
|
+
else:
|
|
49
|
+
target[key] = value
|
|
50
|
+
|
|
51
|
+
except Exception as e:
|
|
52
|
+
traceback.print_exc()
|
|
53
|
+
|
|
54
|
+
return target
|
|
55
|
+
|
|
56
|
+
class ReplCompleter(NestedCompleter):
|
|
57
|
+
def get_completions(
|
|
58
|
+
self, document: Document, complete_event: CompleteEvent
|
|
59
|
+
) -> Iterable[Completion]:
|
|
60
|
+
# Split document.
|
|
61
|
+
text = document.text_before_cursor.lstrip()
|
|
62
|
+
stripped_len = len(document.text_before_cursor) - len(text)
|
|
63
|
+
|
|
64
|
+
# If there is a space, check for the first term, and use a
|
|
65
|
+
# subcompleter.
|
|
66
|
+
if " " in text:
|
|
67
|
+
first_term = text.split()[0]
|
|
68
|
+
completer = self.options.get(first_term)
|
|
69
|
+
|
|
70
|
+
# If we have a sub completer, use this for the completions.
|
|
71
|
+
if completer is not None:
|
|
72
|
+
remaining_text = text[len(first_term) :].lstrip()
|
|
73
|
+
move_cursor = len(text) - len(remaining_text) + stripped_len
|
|
74
|
+
|
|
75
|
+
new_document = Document(
|
|
76
|
+
remaining_text,
|
|
77
|
+
cursor_position=document.cursor_position - move_cursor,
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
for c in completer.get_completions(new_document, complete_event):
|
|
81
|
+
yield c
|
|
82
|
+
|
|
83
|
+
# No space in the input: behave exactly like `WordCompleter`.
|
|
84
|
+
else:
|
|
85
|
+
completer = WordCompleter(
|
|
86
|
+
# Allow dot in the middle or a word
|
|
87
|
+
list(self.options.keys()), ignore_case=self.ignore_case, pattern=re.compile(r"([a-zA-Z0-9_\.\@\&]+|[^a-zA-Z0-9_\.\@\&\s]+)")
|
|
88
|
+
)
|
|
89
|
+
for c in completer.get_completions(document, complete_event):
|
|
90
|
+
yield c
|
|
91
|
+
|
|
92
|
+
lock = threading.Lock()
|
|
93
|
+
in_queue = set()
|
|
94
|
+
|
|
95
|
+
def preload(action: callable, log_key: str = None):
|
|
96
|
+
with lock:
|
|
97
|
+
if not LazyNestedCompleter.loop:
|
|
98
|
+
LazyNestedCompleter.loop = asyncio.new_event_loop()
|
|
99
|
+
LazyNestedCompleter.async_exec = ThreadPoolExecutor(max_workers=6, thread_name_prefix='async')
|
|
100
|
+
LazyNestedCompleter.loop.set_default_executor(LazyNestedCompleter.async_exec)
|
|
101
|
+
|
|
102
|
+
# some lib does not handle asyncio loop properly, as sync exec submit does not work, use another async loop
|
|
103
|
+
async def a():
|
|
104
|
+
try:
|
|
105
|
+
arg_needed = len(action.__code__.co_varnames)
|
|
106
|
+
|
|
107
|
+
if log_key:
|
|
108
|
+
with log_timing(log_key):
|
|
109
|
+
r = action(None) if arg_needed else action()
|
|
110
|
+
else:
|
|
111
|
+
r = action(None) if arg_needed else action()
|
|
112
|
+
if inspect.isawaitable(r):
|
|
113
|
+
await r
|
|
114
|
+
|
|
115
|
+
in_queue.remove(log_key)
|
|
116
|
+
except Exception as e:
|
|
117
|
+
log2('preloading error', e, inspect.getsourcelines(action)[0][0])
|
|
118
|
+
traceback.print_exc()
|
|
119
|
+
|
|
120
|
+
if log_key not in in_queue:
|
|
121
|
+
in_queue.add(log_key)
|
|
122
|
+
LazyNestedCompleter.async_exec.submit(lambda: LazyNestedCompleter.loop.run_until_complete(a()))
|
|
123
|
+
|
|
124
|
+
class LazyNestedCompleter(NestedCompleter):
|
|
125
|
+
loop: asyncio.AbstractEventLoop = None
|
|
126
|
+
async_exec: ThreadPoolExecutor = None
|
|
127
|
+
|
|
128
|
+
def __init__(self, name: str, options_lambda: callable, ignore_case: bool = True, auto: str = 'lazy') -> None:
|
|
129
|
+
super().__init__(None, ignore_case)
|
|
130
|
+
self.options_lambda = options_lambda
|
|
131
|
+
if auto == 'lazy':
|
|
132
|
+
preload(options_lambda, log_key=name)
|
|
133
|
+
|
|
134
|
+
def __repr__(self) -> str:
|
|
135
|
+
return "LazyNestedCompleter(%r, ignore_case=%r)" % (self.options, self.ignore_case)
|
|
136
|
+
|
|
137
|
+
def get_completions(
|
|
138
|
+
self, document: Document, complete_event: CompleteEvent
|
|
139
|
+
) -> Iterable[Completion]:
|
|
140
|
+
if not self.options:
|
|
141
|
+
self.options = self.options_lambda()
|
|
142
|
+
|
|
143
|
+
# Split document.
|
|
144
|
+
text = document.text_before_cursor.lstrip()
|
|
145
|
+
stripped_len = len(document.text_before_cursor) - len(text)
|
|
146
|
+
|
|
147
|
+
# If there is a space, check for the first term, and use a
|
|
148
|
+
# subcompleter.
|
|
149
|
+
if " " in text:
|
|
150
|
+
first_term = text.split()[0]
|
|
151
|
+
completer = self.options.get(first_term)
|
|
152
|
+
|
|
153
|
+
# If we have a sub completer, use this for the completions.
|
|
154
|
+
if completer is not None:
|
|
155
|
+
remaining_text = text[len(first_term) :].lstrip()
|
|
156
|
+
move_cursor = len(text) - len(remaining_text) + stripped_len
|
|
157
|
+
|
|
158
|
+
new_document = Document(
|
|
159
|
+
remaining_text,
|
|
160
|
+
cursor_position=document.cursor_position - move_cursor,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
for c in completer.get_completions(new_document, complete_event):
|
|
164
|
+
yield c
|
|
165
|
+
|
|
166
|
+
# No space in the input: behave exactly like `WordCompleter`.
|
|
167
|
+
else:
|
|
168
|
+
completer = WordCompleter(
|
|
169
|
+
list(self.options.keys()), ignore_case=self.ignore_case
|
|
170
|
+
)
|
|
171
|
+
for c in completer.get_completions(document, complete_event):
|
|
172
|
+
yield c
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
from abc import abstractmethod
|
|
2
|
+
from typing import Generic, TypeVar
|
|
3
|
+
|
|
4
|
+
from adam.utils import log_exc
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
'State',
|
|
8
|
+
'StateMachine',
|
|
9
|
+
]
|
|
10
|
+
|
|
11
|
+
T = TypeVar('T')
|
|
12
|
+
|
|
13
|
+
class State:
|
|
14
|
+
def __init__(self, state: str, comeback_token: str = None, comeback_state: str = None):
|
|
15
|
+
self.state = state
|
|
16
|
+
self.comeback_token = comeback_token
|
|
17
|
+
self.comeback_state = comeback_state
|
|
18
|
+
self.context: dict[str, str] = {}
|
|
19
|
+
|
|
20
|
+
def __str__(self):
|
|
21
|
+
return f'{self.state if self.state else None} comeback[{self.comeback_token} {self.comeback_state}]'
|
|
22
|
+
|
|
23
|
+
class StateMachine(Generic[T]):
|
|
24
|
+
@abstractmethod
|
|
25
|
+
def spec(self) -> str:
|
|
26
|
+
return None
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def keywords(self) -> list[str]:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
def expandable_names(self):
|
|
33
|
+
return []
|
|
34
|
+
|
|
35
|
+
def incomplete_name_transition_condition(self, from_s: str, token: str, to_s: str, suggestions: str) -> list[str]:
|
|
36
|
+
if not suggestions:
|
|
37
|
+
return None
|
|
38
|
+
|
|
39
|
+
tokens = [token]
|
|
40
|
+
if '|' in token:
|
|
41
|
+
tokens = token.split('|')
|
|
42
|
+
|
|
43
|
+
if 'name' not in tokens:
|
|
44
|
+
return None
|
|
45
|
+
|
|
46
|
+
return tokens
|
|
47
|
+
|
|
48
|
+
def __init__(self, indent=0, push_level = 0, debug = False):
|
|
49
|
+
self.states: dict[str, State] = {}
|
|
50
|
+
self.suggestions: dict[str, str] = {}
|
|
51
|
+
|
|
52
|
+
self.indent = indent
|
|
53
|
+
self.push_level = push_level
|
|
54
|
+
self.comebacks: dict[int, str] = {}
|
|
55
|
+
self.debug = debug
|
|
56
|
+
|
|
57
|
+
from_ss_to_add = []
|
|
58
|
+
from_ss = ['']
|
|
59
|
+
words: str = None
|
|
60
|
+
for l in self.spec():
|
|
61
|
+
t_and_w = l.split('^')
|
|
62
|
+
if len(t_and_w) > 1:
|
|
63
|
+
words = t_and_w[1].strip()
|
|
64
|
+
else:
|
|
65
|
+
words = None
|
|
66
|
+
|
|
67
|
+
tks = t_and_w[0].strip(' ').split('>')
|
|
68
|
+
if not l.startswith('-'):
|
|
69
|
+
if words:
|
|
70
|
+
self.suggestions[tks[0].strip(' ')] = words
|
|
71
|
+
|
|
72
|
+
if len(tks) == 1:
|
|
73
|
+
from_ss_to_add.append(tks[0].strip(' '))
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
from_ss = []
|
|
77
|
+
from_ss.extend(from_ss_to_add)
|
|
78
|
+
from_ss_to_add = []
|
|
79
|
+
from_ss.append(tks[0].strip(' '))
|
|
80
|
+
|
|
81
|
+
self.add_transitions(from_ss, tks, words)
|
|
82
|
+
|
|
83
|
+
def add_transitions(self, from_ss: list[str], tks: list[str], words: str):
|
|
84
|
+
token = tks[1].strip(' ')
|
|
85
|
+
if len(tks) > 2:
|
|
86
|
+
to_s = tks[2].strip(' ')
|
|
87
|
+
for from_s in from_ss:
|
|
88
|
+
self.add_whitespace_transition(from_s, to_s)
|
|
89
|
+
self.add_transition(from_s, token, to_s)
|
|
90
|
+
self.add_incomplete_name_transition(from_s, token, to_s, words)
|
|
91
|
+
elif '<' in tks[0]:
|
|
92
|
+
from_and_token = tks[0].split('<')
|
|
93
|
+
if len(from_and_token) > 1:
|
|
94
|
+
for from_s in from_ss:
|
|
95
|
+
self.add_comeback_transition(from_s, from_and_token[1], tks[1].strip(' '))
|
|
96
|
+
|
|
97
|
+
def add_whitespace_transition(self, from_s: str, to_s: str):
|
|
98
|
+
if self.witespace_transition_condition(from_s, to_s):
|
|
99
|
+
if self.debug:
|
|
100
|
+
print(f'{from_s[:-1]} > _ = {to_s}')
|
|
101
|
+
self.states[f'{from_s[:-1]} > _'] = State(from_s)
|
|
102
|
+
|
|
103
|
+
def witespace_transition_condition(self, from_s: str, to_s: str):
|
|
104
|
+
return from_s.endswith('_')
|
|
105
|
+
|
|
106
|
+
def add_incomplete_name_transition(self, from_s: str, token: str, to_s: str, words: str):
|
|
107
|
+
if tokens := self.incomplete_name_transition_condition(from_s, token, to_s, words):
|
|
108
|
+
self.suggestions[to_s] = words
|
|
109
|
+
for token in tokens:
|
|
110
|
+
if self.debug:
|
|
111
|
+
print(f'{to_s} > {token} = {to_s}')
|
|
112
|
+
self.states[f'{to_s} > {token}'] = State(to_s)
|
|
113
|
+
|
|
114
|
+
def add_transition(self, from_s: str, token: str, to_s: str):
|
|
115
|
+
tokens = [token]
|
|
116
|
+
if '|' in token:
|
|
117
|
+
tokens = token.split('|')
|
|
118
|
+
|
|
119
|
+
for t in tokens:
|
|
120
|
+
if t == '_or_':
|
|
121
|
+
t = '||'
|
|
122
|
+
elif t == 'pipe':
|
|
123
|
+
t = '|'
|
|
124
|
+
elif t == '_rdr0_':
|
|
125
|
+
t = '<'
|
|
126
|
+
elif t == '_rdr1_':
|
|
127
|
+
t = '>'
|
|
128
|
+
elif t == '_rdr2_':
|
|
129
|
+
t = '2>'
|
|
130
|
+
|
|
131
|
+
if self.debug:
|
|
132
|
+
print(f'{from_s} > {t} = {to_s}')
|
|
133
|
+
self.states[f'{from_s} > {t}'] = State(to_s)
|
|
134
|
+
|
|
135
|
+
def add_comeback_transition(self, from_s: str, token: str, to_s: str):
|
|
136
|
+
key = f'{from_s} > ('
|
|
137
|
+
orig = self.states[key]
|
|
138
|
+
if not orig:
|
|
139
|
+
raise Exception(f'from state not found for {key}')
|
|
140
|
+
|
|
141
|
+
orig.comeback_token = token
|
|
142
|
+
orig.comeback_state = to_s
|
|
143
|
+
if self.debug:
|
|
144
|
+
print(f'{from_s} > ) = {to_s}')
|
|
145
|
+
self.states[key] = orig
|
|
146
|
+
|
|
147
|
+
def traverse_tokens(self, tokens: list[str], state: State = State('')):
|
|
148
|
+
for token in tokens[:-1]:
|
|
149
|
+
if not token:
|
|
150
|
+
continue
|
|
151
|
+
|
|
152
|
+
if self.debug:
|
|
153
|
+
print(f'{token} ', end='')
|
|
154
|
+
|
|
155
|
+
last_name = None
|
|
156
|
+
|
|
157
|
+
if (t := token.lower()) in self.keywords():
|
|
158
|
+
token = t
|
|
159
|
+
elif token in ['*', ',', '.']:
|
|
160
|
+
pass
|
|
161
|
+
else:
|
|
162
|
+
last_name = token
|
|
163
|
+
token = 'word'
|
|
164
|
+
|
|
165
|
+
with log_exc():
|
|
166
|
+
context = state.context
|
|
167
|
+
state = self.states[f'{state.state} > {token}']
|
|
168
|
+
state.context = context
|
|
169
|
+
|
|
170
|
+
if last_name:
|
|
171
|
+
state.context['last_name'] = last_name
|
|
172
|
+
|
|
173
|
+
return state
|
adam/utils_sqlite.py
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
from collections.abc import Callable
|
|
2
|
+
|
|
3
|
+
import functools
|
|
4
|
+
import glob
|
|
5
|
+
import os
|
|
6
|
+
import sqlite3
|
|
7
|
+
import pandas
|
|
8
|
+
|
|
9
|
+
from adam.config import Config
|
|
10
|
+
from adam.utils import tabulize, log, wait_log
|
|
11
|
+
|
|
12
|
+
class CursorHandler:
|
|
13
|
+
def __init__(self, conn: sqlite3.Connection):
|
|
14
|
+
self.conn = conn
|
|
15
|
+
self.cursor = None
|
|
16
|
+
|
|
17
|
+
def __enter__(self):
|
|
18
|
+
self.cursor = self.conn.cursor()
|
|
19
|
+
|
|
20
|
+
return self.cursor
|
|
21
|
+
|
|
22
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
23
|
+
if self.cursor:
|
|
24
|
+
self.cursor.close()
|
|
25
|
+
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
class SQLiteConnectionHandler:
|
|
29
|
+
def __init__(self, database: str, keyspace = None):
|
|
30
|
+
self.database = database
|
|
31
|
+
self.keyspace = keyspace
|
|
32
|
+
self.conn = None
|
|
33
|
+
|
|
34
|
+
def __enter__(self) -> sqlite3.Connection:
|
|
35
|
+
self.conn = SQLite.connect(self.database, self.keyspace)
|
|
36
|
+
|
|
37
|
+
self.conn.__enter__()
|
|
38
|
+
|
|
39
|
+
return self.conn
|
|
40
|
+
|
|
41
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
42
|
+
if self.conn:
|
|
43
|
+
self.conn.__exit__(exc_type, exc_val, exc_tb)
|
|
44
|
+
|
|
45
|
+
return False
|
|
46
|
+
|
|
47
|
+
def sqlite(database: str, keyspace = None):
|
|
48
|
+
return SQLiteConnectionHandler(database, keyspace=keyspace)
|
|
49
|
+
|
|
50
|
+
# no state utility class
|
|
51
|
+
class SQLite:
|
|
52
|
+
def cursor(conn: sqlite3.Connection):
|
|
53
|
+
return CursorHandler(conn)
|
|
54
|
+
|
|
55
|
+
def local_db_dir():
|
|
56
|
+
return Config().get('export.sqlite.local-db-dir', '/tmp/qing-db')
|
|
57
|
+
|
|
58
|
+
def keyspace(database: str):
|
|
59
|
+
return '_'.join(database.replace(".db", "").split('_')[1:])
|
|
60
|
+
|
|
61
|
+
@functools.lru_cache()
|
|
62
|
+
def database_names(prefix: str = None):
|
|
63
|
+
wait_log('Inspecting export databases...')
|
|
64
|
+
|
|
65
|
+
pattern = f'{SQLite.local_db_dir()}/s*.db'
|
|
66
|
+
if prefix:
|
|
67
|
+
pattern = f'{SQLite.local_db_dir()}/{prefix}*'
|
|
68
|
+
return [os.path.basename(f) for f in glob.glob(pattern)]
|
|
69
|
+
|
|
70
|
+
def clear_cache(cache: str = None):
|
|
71
|
+
SQLite.database_names.cache_clear()
|
|
72
|
+
SQLite.table_names.cache_clear()
|
|
73
|
+
|
|
74
|
+
@functools.lru_cache()
|
|
75
|
+
def table_names(database: str):
|
|
76
|
+
tokens = database.replace('.db', '').split('_')
|
|
77
|
+
ts_prefix = tokens[0]
|
|
78
|
+
keyspace = '_'.join(tokens[1:])
|
|
79
|
+
|
|
80
|
+
conn = None
|
|
81
|
+
tables = []
|
|
82
|
+
try:
|
|
83
|
+
conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{ts_prefix}_{keyspace}.db')
|
|
84
|
+
with SQLite.cursor(conn) as cursor:
|
|
85
|
+
cursor.execute("SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;")
|
|
86
|
+
|
|
87
|
+
tables = [row[0] for row in cursor.fetchall() if row[0] != "sqlite_sequence"]
|
|
88
|
+
|
|
89
|
+
return tables
|
|
90
|
+
except sqlite3.Error as e:
|
|
91
|
+
print(f"Error connecting to or querying the database: {e}")
|
|
92
|
+
return []
|
|
93
|
+
finally:
|
|
94
|
+
if conn:
|
|
95
|
+
conn.close()
|
|
96
|
+
|
|
97
|
+
def connect(database: str, keyspace: str = None):
|
|
98
|
+
os.makedirs(SQLite.local_db_dir(), exist_ok=True)
|
|
99
|
+
|
|
100
|
+
if keyspace:
|
|
101
|
+
return sqlite3.connect(f'{SQLite.local_db_dir()}/{database}_{keyspace}.db')
|
|
102
|
+
else:
|
|
103
|
+
conn = sqlite3.connect(f'{SQLite.local_db_dir()}/{database}_root.db')
|
|
104
|
+
with SQLite.cursor(conn) as cursor:
|
|
105
|
+
for d in SQLite.database_names(database):
|
|
106
|
+
if d != f'{database}.db':
|
|
107
|
+
q = f"ATTACH DATABASE '{SQLite.local_db_dir()}/{d}' AS {SQLite.keyspace(d)}"
|
|
108
|
+
cursor.execute(q)
|
|
109
|
+
|
|
110
|
+
return conn
|
|
111
|
+
|
|
112
|
+
@functools.lru_cache()
|
|
113
|
+
def column_names(tables: list[str] = [], database: str = None, function: str = 'audit', partition_cols_only = False):
|
|
114
|
+
pass
|
|
115
|
+
|
|
116
|
+
def run_query(query: str, database: str = None, output: Callable[[str], str] = None) -> tuple[int, str]:
|
|
117
|
+
with sqlite(database) as conn:
|
|
118
|
+
return SQLite.run_query_with_conn(conn, query, output=output)
|
|
119
|
+
|
|
120
|
+
def run_query_with_conn(conn, query: str, output: Callable[[str], str] = None) -> tuple[int, str]:
|
|
121
|
+
log_file = None
|
|
122
|
+
|
|
123
|
+
df = SQLite.query(conn, query)
|
|
124
|
+
lines = ['\t'.join(map(str, line)) for line in df.values.tolist()]
|
|
125
|
+
out = tabulize(lines, header='\t'.join(df.columns.tolist()), separator='\t', to=0)
|
|
126
|
+
if output:
|
|
127
|
+
log_file = output(out)
|
|
128
|
+
else:
|
|
129
|
+
log(out)
|
|
130
|
+
|
|
131
|
+
return len(lines), log_file
|
|
132
|
+
|
|
133
|
+
def query(conn, sql: str) -> tuple[str, str, list]:
|
|
134
|
+
return pandas.read_sql_query(sql, conn)
|
|
135
|
+
|
|
136
|
+
def log_prefix():
|
|
137
|
+
return Config().get('export.log-prefix', '/tmp/qing')
|
adam/version.py
CHANGED