kaqing 2.0.174__py3-none-any.whl → 2.0.186__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/app_session.py +2 -2
- adam/apps.py +18 -4
- adam/batch.py +1 -1
- adam/checks/check_utils.py +3 -1
- adam/commands/__init__.py +4 -2
- adam/commands/app/__init__.py +0 -0
- adam/commands/app/app.py +38 -0
- adam/commands/app/app_ping.py +38 -0
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/app/show_app_id.py +44 -0
- adam/commands/app/show_app_queues.py +38 -0
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +9 -27
- adam/commands/audit/audit_repair_tables.py +5 -7
- adam/commands/audit/audit_run.py +1 -1
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +2 -14
- adam/commands/audit/show_slow10.py +2 -13
- adam/commands/audit/show_top10.py +2 -11
- adam/commands/audit/utils_show_top10.py +14 -1
- adam/commands/bash/bash.py +1 -1
- adam/commands/cat.py +3 -7
- adam/commands/check.py +2 -2
- adam/commands/cli_commands.py +6 -1
- adam/commands/{cp.py → clipboard_copy.py} +18 -12
- adam/commands/code.py +2 -2
- adam/commands/command.py +61 -11
- adam/commands/commands_utils.py +19 -12
- adam/commands/cql/completions_c.py +28 -0
- adam/commands/cql/cqlsh.py +3 -7
- adam/commands/cql/utils_cql.py +22 -58
- adam/commands/deploy/deploy_pg_agent.py +2 -2
- adam/commands/deploy/undeploy_pg_agent.py +2 -2
- adam/commands/devices/device.py +39 -8
- adam/commands/devices/device_app.py +18 -28
- adam/commands/devices/device_auit_log.py +3 -3
- adam/commands/devices/device_cass.py +16 -22
- adam/commands/devices/device_export.py +6 -3
- adam/commands/devices/device_postgres.py +79 -63
- adam/commands/download_file.py +47 -0
- adam/commands/export/clean_up_all_export_sessions.py +3 -3
- adam/commands/export/clean_up_export_sessions.py +5 -10
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/export.py +0 -16
- adam/commands/export/export_databases.py +26 -9
- adam/commands/export/export_select.py +9 -58
- adam/commands/export/export_sessions.py +90 -5
- adam/commands/export/export_use.py +13 -10
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +60 -22
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +8 -4
- adam/commands/export/importer.py +7 -0
- adam/commands/export/importer_athena.py +101 -34
- adam/commands/export/importer_sqlite.py +30 -5
- adam/commands/export/show_column_counts.py +11 -11
- adam/commands/export/show_export_databases.py +5 -3
- adam/commands/export/show_export_session.py +5 -6
- adam/commands/export/show_export_sessions.py +4 -11
- adam/commands/export/utils_export.py +42 -14
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +2 -2
- adam/commands/intermediate_command.py +6 -3
- adam/commands/ls.py +1 -1
- adam/commands/medusa/medusa_backup.py +12 -14
- adam/commands/medusa/medusa_restore.py +20 -15
- adam/commands/medusa/medusa_show_backupjobs.py +6 -4
- adam/commands/medusa/medusa_show_restorejobs.py +5 -3
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +3 -8
- adam/commands/param_get.py +2 -3
- adam/commands/param_set.py +1 -1
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +14 -21
- adam/commands/postgres/postgres_databases.py +270 -0
- adam/commands/postgres/utils_postgres.py +29 -20
- adam/commands/preview_table.py +3 -1
- adam/commands/pwd.py +3 -3
- adam/commands/reaper/reaper_forward.py +2 -2
- adam/commands/reaper/reaper_runs.py +3 -3
- adam/commands/reaper/reaper_schedule_activate.py +6 -2
- adam/commands/reaper/reaper_schedule_start.py +1 -2
- adam/commands/reaper/reaper_schedule_stop.py +1 -2
- adam/commands/reaper/utils_reaper.py +13 -6
- adam/commands/repair/repair_scan.py +0 -2
- adam/commands/repair/repair_stop.py +0 -1
- adam/commands/shell.py +7 -5
- adam/commands/show/show.py +1 -1
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +5 -3
- adam/commands/show/show_cassandra_status.py +27 -20
- adam/commands/show/{show_commands.py → show_cli_commands.py} +2 -2
- adam/commands/show/show_login.py +2 -2
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +15 -14
- adam/commands/show/show_storage.py +9 -8
- adam/config.py +1 -0
- adam/embedded_params.py +1 -1
- adam/repl.py +16 -9
- adam/repl_commands.py +16 -9
- adam/repl_session.py +8 -1
- adam/repl_state.py +33 -10
- adam/sql/lark_completer.py +280 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/sql_state_machine.py +8 -2
- adam/utils.py +116 -29
- adam/utils_athena.py +7 -8
- adam/utils_issues.py +2 -2
- adam/utils_k8s/app_clusters.py +2 -2
- adam/utils_k8s/app_pods.py +5 -2
- adam/utils_k8s/cassandra_clusters.py +11 -3
- adam/utils_k8s/cassandra_nodes.py +2 -2
- adam/utils_k8s/k8s.py +9 -0
- adam/utils_k8s/kube_context.py +2 -2
- adam/utils_k8s/pods.py +23 -5
- adam/utils_k8s/statefulsets.py +5 -2
- adam/utils_local.py +4 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/repl_completer.py +128 -2
- adam/utils_sqlite.py +2 -2
- adam/version.py +1 -1
- {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/METADATA +1 -1
- kaqing-2.0.186.dist-info/RECORD +250 -0
- adam/commands/cql/cql_completions.py +0 -32
- adam/commands/export/export_select_x.py +0 -54
- adam/commands/postgres/postgres_context.py +0 -272
- adam/commands/postgres/psql_completions.py +0 -10
- kaqing-2.0.174.dist-info/RECORD +0 -230
- {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/WHEEL +0 -0
- {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.174.dist-info → kaqing-2.0.186.dist-info}/top_level.txt +0 -0
adam/embedded_params.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
def config():
|
|
2
|
-
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, '
|
|
2
|
+
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-prefix': '/tmp/qing'}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'local-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', 'nodetool': {'workers': 96, 'commands_in_line': 40, 'status': {'workers': 32, 'samples': 3, 'commands_in_line': 40}}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu-metrics,mem', 'header': 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT'}, 'processes-qing': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3/*'}, 'c': {'auto-enter': 'cluster'}, 'x': {'auto-enter': 'latest'}, 'history': {'push-cat-log-file': True, 'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'auto-complete': {'c': {'tables': 'lazy'}, 'x': {'tables': 'lazy'}, 'cli': {'cp': 'jit'}, 'export': {'databases': 'jit'}, 'medusa': {'backups': 'jit'}, 'reaper': {'schedules': 'lazy'}}, 'debug': False, 'debugs': {'timings': False, 'exit-on-error': False, 'show-parallelism': False}}
|
adam/repl.py
CHANGED
|
@@ -16,11 +16,16 @@ from adam.log import Log
|
|
|
16
16
|
from adam.repl_commands import ReplCommands
|
|
17
17
|
from adam.repl_session import ReplSession
|
|
18
18
|
from adam.repl_state import ReplState
|
|
19
|
-
from adam.utils import clear_wait_log_flag, debug_trace,
|
|
19
|
+
from adam.utils import clear_wait_log_flag, debug_trace, deep_sort_dict, tabulize, log2, log_exc, log_timing
|
|
20
20
|
from adam.apps import Apps
|
|
21
|
-
from adam.utils_repl.repl_completer import ReplCompleter
|
|
21
|
+
from adam.utils_repl.repl_completer import ReplCompleter, merge_completions
|
|
22
22
|
from . import __version__
|
|
23
23
|
|
|
24
|
+
import nest_asyncio
|
|
25
|
+
nest_asyncio.apply()
|
|
26
|
+
|
|
27
|
+
import asyncio
|
|
28
|
+
|
|
24
29
|
def enter_repl(state: ReplState):
|
|
25
30
|
if os.getenv('QING_DROPPED', 'false') == 'true':
|
|
26
31
|
log2('You have dropped to bash from another qing instance. Please enter "exit" to go back to qing.')
|
|
@@ -68,7 +73,7 @@ def enter_repl(state: ReplState):
|
|
|
68
73
|
|
|
69
74
|
for c in sorted_cmds:
|
|
70
75
|
with log_exc(f'* {c.command()} command returned None completions.'):
|
|
71
|
-
completions = log_timing(c.command(), lambda: deep_sort_dict(
|
|
76
|
+
completions = log_timing(c.command(), lambda: deep_sort_dict(merge_completions(completions, c.completion(state))))
|
|
72
77
|
|
|
73
78
|
# print(json.dumps(completions, indent=4))
|
|
74
79
|
completer = ReplCompleter.from_nested_dict(completions)
|
|
@@ -88,17 +93,17 @@ def enter_repl(state: ReplState):
|
|
|
88
93
|
return state, cmd
|
|
89
94
|
|
|
90
95
|
if state.device == ReplState.A and state.app_app or state.device == ReplState.P:
|
|
91
|
-
state.push()
|
|
96
|
+
state.push(pod_targetted=True)
|
|
92
97
|
|
|
93
98
|
state.app_pod = arry[0].strip('@')
|
|
94
99
|
cmd = ' '.join(arry[1:])
|
|
95
100
|
elif state.device == ReplState.P:
|
|
96
|
-
state.push()
|
|
101
|
+
state.push(pod_targetted=True)
|
|
97
102
|
|
|
98
103
|
state.app_pod = arry[0].strip('@')
|
|
99
104
|
cmd = ' '.join(arry[1:])
|
|
100
105
|
elif state.sts:
|
|
101
|
-
state.push()
|
|
106
|
+
state.push(pod_targetted=True)
|
|
102
107
|
|
|
103
108
|
state.pod = arry[0].strip('@')
|
|
104
109
|
cmd = ' '.join(arry[1:])
|
|
@@ -143,8 +148,7 @@ def try_device_default_action(state: ReplState, cmds: Command, cmd_list: list[Co
|
|
|
143
148
|
if not action_taken:
|
|
144
149
|
log2(f'* Invalid command: {cmd}')
|
|
145
150
|
log2()
|
|
146
|
-
|
|
147
|
-
log2(lines_to_tabular(lines, separator='\t'))
|
|
151
|
+
tabulize([c.help(state) for c in cmd_list if c.help(state)], separator='\t', to=2)
|
|
148
152
|
|
|
149
153
|
return result
|
|
150
154
|
|
|
@@ -176,6 +180,9 @@ def repl(kubeconfig: str, config: str, param: list[str], cluster:str, namespace:
|
|
|
176
180
|
if not KubeContext.init_params(config, param):
|
|
177
181
|
return
|
|
178
182
|
|
|
179
|
-
state = ReplState(
|
|
183
|
+
state = ReplState(ns_sts=cluster, namespace=namespace, in_repl=True)
|
|
180
184
|
state, _ = state.apply_device_arg(extra_args)
|
|
185
|
+
if not state.device:
|
|
186
|
+
state.device=Config().get('repl.start-drive', 'a')
|
|
187
|
+
|
|
181
188
|
enter_repl(state)
|
adam/repl_commands.py
CHANGED
|
@@ -7,6 +7,7 @@ from adam.commands.app.show_app_queues import ShowAppQueues
|
|
|
7
7
|
from adam.commands.audit.audit import Audit
|
|
8
8
|
from adam.commands.cat import Cat
|
|
9
9
|
from adam.commands.code import Code
|
|
10
|
+
from adam.commands.download_file import DownloadFile
|
|
10
11
|
from adam.commands.deploy.code_start import CodeStart
|
|
11
12
|
from adam.commands.deploy.code_stop import CodeStop
|
|
12
13
|
from adam.commands.deploy.deploy import Deploy
|
|
@@ -22,22 +23,27 @@ from adam.commands.devices.device_auit_log import DeviceAuditLog
|
|
|
22
23
|
from adam.commands.devices.device_cass import DeviceCass
|
|
23
24
|
from adam.commands.devices.device_export import DeviceExport
|
|
24
25
|
from adam.commands.devices.device_postgres import DevicePostgres
|
|
26
|
+
from adam.commands.export.download_export_session import DownloadExportSession
|
|
25
27
|
from adam.commands.export.drop_export_database import DropExportDatabase
|
|
26
28
|
from adam.commands.export.export import ExportTables
|
|
29
|
+
from adam.commands.export.import_files import ImportCSVFiles
|
|
27
30
|
from adam.commands.export.import_session import ImportSession
|
|
28
31
|
from adam.commands.export.clean_up_export_sessions import CleanUpExportSessions
|
|
29
32
|
from adam.commands.export.clean_up_all_export_sessions import CleanUpAllExportSessions
|
|
30
33
|
from adam.commands.export.drop_export_databases import DropExportDatabases
|
|
31
|
-
from adam.commands.export.
|
|
34
|
+
from adam.commands.export.export_x_select import ExportXSelect
|
|
32
35
|
from adam.commands.export.export_use import ExportUse
|
|
33
|
-
from adam.commands.export.
|
|
36
|
+
from adam.commands.export.export_select import ExportSelect
|
|
34
37
|
from adam.commands.export.show_column_counts import ShowColumnCounts
|
|
35
38
|
from adam.commands.export.show_export_databases import ShowExportDatabases
|
|
36
39
|
from adam.commands.export.show_export_session import ShowExportSession
|
|
37
40
|
from adam.commands.export.show_export_sessions import ShowExportSessions
|
|
41
|
+
from adam.commands.find_files import FindLocalFiles
|
|
42
|
+
from adam.commands.find_processes import FindProcesses
|
|
43
|
+
from adam.commands.head import Head
|
|
38
44
|
from adam.commands.kubectl import Kubectl
|
|
39
45
|
from adam.commands.shell import Shell
|
|
40
|
-
from adam.commands.
|
|
46
|
+
from adam.commands.clipboard_copy import ClipboardCopy
|
|
41
47
|
from adam.commands.bash.bash import Bash
|
|
42
48
|
from adam.commands.cd import Cd
|
|
43
49
|
from adam.commands.check import Check
|
|
@@ -61,7 +67,7 @@ from adam.commands.param_set import SetParam
|
|
|
61
67
|
from adam.commands.show.show import Show
|
|
62
68
|
from adam.commands.show.show_cassandra_status import ShowCassandraStatus
|
|
63
69
|
from adam.commands.show.show_cassandra_version import ShowCassandraVersion
|
|
64
|
-
from adam.commands.show.
|
|
70
|
+
from adam.commands.show.show_cli_commands import ShowKubectlCommands
|
|
65
71
|
from adam.commands.show.show_host import ShowHost
|
|
66
72
|
from adam.commands.show.show_login import ShowLogin
|
|
67
73
|
from adam.commands.show.show_params import ShowParams
|
|
@@ -93,16 +99,17 @@ class ReplCommands:
|
|
|
93
99
|
return deduped
|
|
94
100
|
|
|
95
101
|
def navigation() -> list[Command]:
|
|
96
|
-
return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(),
|
|
102
|
+
return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(),
|
|
103
|
+
Cd(), Cat(), Head(), DownloadFile(), FindLocalFiles(), FindProcesses(), Pwd(), ClipboardCopy(),
|
|
97
104
|
GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam(), ShowHost()]
|
|
98
105
|
|
|
99
106
|
def cassandra_ops() -> list[Command]:
|
|
100
107
|
return [Cqlsh(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
|
|
101
108
|
Check(), Issues(), NodeTool(), Report(), AlterTables(), Bash(),
|
|
102
|
-
ExportTables(),
|
|
109
|
+
ExportTables(), ExportXSelect(), ExportUse(), ShowExportDatabases(), ShowColumnCounts(),
|
|
103
110
|
DropExportDatabase(), DropExportDatabases(),
|
|
104
|
-
ShowExportSessions(), ShowExportSession(),
|
|
105
|
-
CleanUpExportSessions(), CleanUpAllExportSessions(), ImportSession()] + \
|
|
111
|
+
ShowExportSessions(), ShowExportSession(), DownloadExportSession(),
|
|
112
|
+
CleanUpExportSessions(), CleanUpAllExportSessions(), ImportSession(), ImportCSVFiles()] + \
|
|
106
113
|
Medusa().cmd_list() + [Restart(), RollOut(), Watch()] + Reaper().cmd_list() + Repair().cmd_list()
|
|
107
114
|
|
|
108
115
|
def postgres_ops() -> list[Command]:
|
|
@@ -115,7 +122,7 @@ class ReplCommands:
|
|
|
115
122
|
return [Audit()] + Audit().cmd_list()
|
|
116
123
|
|
|
117
124
|
def export_ops() -> list[Command]:
|
|
118
|
-
return [
|
|
125
|
+
return [ExportSelect(), DropExportDatabase(), DropExportDatabases(), ShowColumnCounts()]
|
|
119
126
|
|
|
120
127
|
def tools() -> list[Command]:
|
|
121
128
|
return [Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod(), Kubectl(), Code()]
|
adam/repl_session.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from prompt_toolkit import PromptSession
|
|
2
2
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
3
3
|
|
|
4
|
+
from adam.config import Config
|
|
5
|
+
|
|
4
6
|
class ReplSession:
|
|
5
7
|
# the singleton pattern
|
|
6
8
|
def __new__(cls, *args, **kwargs):
|
|
@@ -10,4 +12,9 @@ class ReplSession:
|
|
|
10
12
|
|
|
11
13
|
def __init__(self):
|
|
12
14
|
if not hasattr(self, 'prompt_session'):
|
|
13
|
-
self.prompt_session = PromptSession(auto_suggest=AutoSuggestFromHistory())
|
|
15
|
+
self.prompt_session = PromptSession(auto_suggest=AutoSuggestFromHistory())
|
|
16
|
+
|
|
17
|
+
def append_history(self, entry: str):
|
|
18
|
+
if entry and Config().get('repl.history.push-cat-remote-log-file', True):
|
|
19
|
+
if self.prompt_session:
|
|
20
|
+
self.prompt_session.history.append_string(entry)
|
adam/repl_state.py
CHANGED
|
@@ -3,7 +3,6 @@ from enum import Enum
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import Callable
|
|
5
5
|
|
|
6
|
-
from adam.commands.postgres.postgres_context import PostgresContext
|
|
7
6
|
from adam.utils_k8s.app_clusters import AppClusters
|
|
8
7
|
from adam.utils_k8s.app_pods import AppPods
|
|
9
8
|
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
@@ -62,6 +61,7 @@ class ReplState:
|
|
|
62
61
|
self.bash_session = bash_session
|
|
63
62
|
self.remote_dir = remote_dir
|
|
64
63
|
self.original_state: ReplState = None
|
|
64
|
+
self.pod_targetted = False
|
|
65
65
|
|
|
66
66
|
self.export_session: str = None
|
|
67
67
|
|
|
@@ -82,11 +82,11 @@ class ReplState:
|
|
|
82
82
|
msg = ''
|
|
83
83
|
if self.device == ReplState.P:
|
|
84
84
|
msg = f'{ReplState.P}:'
|
|
85
|
-
|
|
86
|
-
if
|
|
87
|
-
msg +=
|
|
88
|
-
elif
|
|
89
|
-
msg +=
|
|
85
|
+
host, database = self.pg_host_n_database()
|
|
86
|
+
if database:
|
|
87
|
+
msg += database
|
|
88
|
+
elif host:
|
|
89
|
+
msg += host
|
|
90
90
|
elif self.device == ReplState.A:
|
|
91
91
|
msg = f'{ReplState.A}:'
|
|
92
92
|
if self.app_env:
|
|
@@ -314,8 +314,8 @@ class ReplState:
|
|
|
314
314
|
if self.device != ReplState.P:
|
|
315
315
|
return (False, None)
|
|
316
316
|
|
|
317
|
-
|
|
318
|
-
if not
|
|
317
|
+
_, database = self.pg_host_n_database()
|
|
318
|
+
if not database:
|
|
319
319
|
def error():
|
|
320
320
|
if self.in_repl:
|
|
321
321
|
log2('cd to a database first.')
|
|
@@ -346,7 +346,10 @@ class ReplState:
|
|
|
346
346
|
if not self.export_session:
|
|
347
347
|
def error():
|
|
348
348
|
if self.in_repl:
|
|
349
|
-
|
|
349
|
+
if self.device == ReplState.C:
|
|
350
|
+
log2("Select an export database first with 'use' command.")
|
|
351
|
+
else:
|
|
352
|
+
log2('cd to an export database first.')
|
|
350
353
|
else:
|
|
351
354
|
log2('* export database is missing.')
|
|
352
355
|
log2()
|
|
@@ -390,9 +393,10 @@ class ReplState:
|
|
|
390
393
|
self.pop()
|
|
391
394
|
self.bash_session = None
|
|
392
395
|
|
|
393
|
-
def push(self):
|
|
396
|
+
def push(self, pod_targetted=False):
|
|
394
397
|
if not self.original_state:
|
|
395
398
|
self.original_state = copy(self)
|
|
399
|
+
self.pod_targetted = pod_targetted
|
|
396
400
|
|
|
397
401
|
def pop(self):
|
|
398
402
|
if o := self.original_state:
|
|
@@ -407,6 +411,25 @@ class ReplState:
|
|
|
407
411
|
self.namespace = o.namespace
|
|
408
412
|
|
|
409
413
|
self.original_state = None
|
|
414
|
+
self.pod_targetted = False
|
|
415
|
+
|
|
416
|
+
def pg_host_n_database(self):
|
|
417
|
+
host = None
|
|
418
|
+
database = None
|
|
419
|
+
|
|
420
|
+
if self.pg_path:
|
|
421
|
+
host_n_db = self.pg_path.split('/')
|
|
422
|
+
host = host_n_db[0]
|
|
423
|
+
if len(host_n_db) > 1:
|
|
424
|
+
database = host_n_db[1]
|
|
425
|
+
|
|
426
|
+
return host, database
|
|
427
|
+
|
|
428
|
+
def with_no_pod(self):
|
|
429
|
+
state1 = copy(self)
|
|
430
|
+
state1.pod = None
|
|
431
|
+
|
|
432
|
+
return state1
|
|
410
433
|
|
|
411
434
|
class DevicePodService:
|
|
412
435
|
def __init__(self, handler: 'DeviceExecHandler'):
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import os
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
from prompt_toolkit.completion import CompleteEvent, Completer, Completion, NestedCompleter, WordCompleter
|
|
5
|
+
from prompt_toolkit.document import Document
|
|
6
|
+
|
|
7
|
+
from adam.config import Config
|
|
8
|
+
from adam.sql.lark_parser import LarkParser
|
|
9
|
+
from adam.utils import debug, log_timing, offload
|
|
10
|
+
from adam.utils_repl.appendable_completer import AppendableCompleter
|
|
11
|
+
from adam.utils_repl.repl_completer import merge_completions, preload
|
|
12
|
+
|
|
13
|
+
__all__ = [
|
|
14
|
+
"LarkCompleter",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def default_columns(x: list[str]):
|
|
18
|
+
return 'id,x.,y.,z.'.split(',')
|
|
19
|
+
|
|
20
|
+
class LarkCompleter(Completer, AppendableCompleter):
|
|
21
|
+
SYSTEM = 'system'
|
|
22
|
+
|
|
23
|
+
def __init__(self,
|
|
24
|
+
dml: str = None,
|
|
25
|
+
expandables: dict = {},
|
|
26
|
+
variant: str = 'c',
|
|
27
|
+
|
|
28
|
+
name: str = None,
|
|
29
|
+
options_lambda: callable = None,
|
|
30
|
+
auto: str = 'lazy',
|
|
31
|
+
debug = False
|
|
32
|
+
) -> None:
|
|
33
|
+
self.nested: NestedCompleter = None
|
|
34
|
+
self.options_lambda = options_lambda
|
|
35
|
+
if options_lambda and auto == 'lazy':
|
|
36
|
+
preload(options_lambda, log_key=name)
|
|
37
|
+
|
|
38
|
+
self.variant = variant
|
|
39
|
+
self.parser = None
|
|
40
|
+
self.dml = dml
|
|
41
|
+
self.expandables = expandables
|
|
42
|
+
|
|
43
|
+
self.display_dict = {}
|
|
44
|
+
self.meta_dict = {}
|
|
45
|
+
self.WORD = None
|
|
46
|
+
self.sentence = False
|
|
47
|
+
self.match_middle = False
|
|
48
|
+
self.pattern = None
|
|
49
|
+
|
|
50
|
+
self.debug = debug
|
|
51
|
+
|
|
52
|
+
if variant:
|
|
53
|
+
self.parser = LarkCompleter.lark_parser(variant)
|
|
54
|
+
self.preload_lazy_auto_completes()
|
|
55
|
+
|
|
56
|
+
def __repr__(self):
|
|
57
|
+
return f"LarkCompleter.{self.variant}"
|
|
58
|
+
|
|
59
|
+
def preload_lazy_auto_completes(self):
|
|
60
|
+
for key, value in self.expandables.items():
|
|
61
|
+
if callable(value):
|
|
62
|
+
if self.auto_complete(key) == 'lazy':
|
|
63
|
+
preload(value, log_key=key)
|
|
64
|
+
|
|
65
|
+
def from_lambda(name: str, options_lambda: callable, auto: str = 'lazy'):
|
|
66
|
+
return LarkCompleter(name=name, options_lambda=options_lambda, auto=auto, variant=None)
|
|
67
|
+
|
|
68
|
+
@functools.lru_cache()
|
|
69
|
+
def lark_parser(variant: str):
|
|
70
|
+
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
71
|
+
with open(dir_path + f"/qingl.lark") as f:
|
|
72
|
+
grammar: str = None
|
|
73
|
+
with log_timing(f'lark.{variant}.file-read'):
|
|
74
|
+
grammar = f.read()
|
|
75
|
+
|
|
76
|
+
common_contexts = {
|
|
77
|
+
'cd_command.file_name': 'direct-dirs',
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if variant in ['a', 'c0', 'p0', 'x0']:
|
|
81
|
+
grammar = grammar.replace('start: statement_sequence', f'start: qing_{variant}_statement')
|
|
82
|
+
contexts_by_path = {
|
|
83
|
+
} | common_contexts
|
|
84
|
+
|
|
85
|
+
debug(f'* GRAMMAR replaced to start: qing_{variant}_statement')
|
|
86
|
+
|
|
87
|
+
return LarkParser(grammar, contexts_by_path)
|
|
88
|
+
elif variant == 'system':
|
|
89
|
+
grammar = grammar.replace('start: statement_sequence', f'start: qing_{variant}_statement')
|
|
90
|
+
contexts_by_path = {
|
|
91
|
+
} | common_contexts
|
|
92
|
+
|
|
93
|
+
return LarkParser(grammar, contexts_by_path)
|
|
94
|
+
|
|
95
|
+
grammar = grammar.replace('qing_statement: qing_p_statement', f'qing_statement: qing_{variant}_statement')
|
|
96
|
+
debug(f'* GRAMMAR replaced to qing_statement: qing_{variant}_statement')
|
|
97
|
+
|
|
98
|
+
bash_contexts = {
|
|
99
|
+
'bash_statement.host_name': 'hosts',
|
|
100
|
+
'bash_statement.bash_command': 'bash-commands',
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
contexts_by_path = {
|
|
104
|
+
'describe_keyspace.keyspace_name': 'keyspaces',
|
|
105
|
+
'keyspace_ref.keyspace_path.namespace_ref.identifier_ref': 'tables',
|
|
106
|
+
'preview_table_statement.path.identifier_ref': 'tables',
|
|
107
|
+
|
|
108
|
+
'insert_statement.insert_select': 'column-names',
|
|
109
|
+
'update_statement.set_clause.path.identifier_ref': 'column-names',
|
|
110
|
+
'update_statement.where_clause.cond.expr.path.identifier_ref': 'column-names',
|
|
111
|
+
'delete_statement.where_clause.cond.expr.path.identifier_ref': 'column-names',
|
|
112
|
+
|
|
113
|
+
'select_clause.projection.result_expr.expr.path.identifier_ref': 'columns',
|
|
114
|
+
'select_from.where_clause.cond.expr.path.identifier_ref': 'columns',
|
|
115
|
+
'select_from.where_clause.cond.expr.logical_term.and_expr.cond.expr.path.identifier_ref': 'columns',
|
|
116
|
+
'select_from.group_by_clause.group_term.expr.path.identifier_ref': 'columns',
|
|
117
|
+
'select_statement.order_by_clause.ordering_term.expr.path.identifier_ref': 'columns',
|
|
118
|
+
'select_from.from_clause.from_terms.join_clause.ansi_join_clause.ansi_join_predicate.expr.path.identifier_ref': 'columns',
|
|
119
|
+
'select_from.from_clause.from_terms.join_clause.ansi_join_clause.ansi_join_predicate.expr.comparison_term.relational_expr.expr.path.identifier_ref': 'columns',
|
|
120
|
+
|
|
121
|
+
'select_from.from_clause.from_terms.from_generic.alias.identifier_ref': 'column-aliases',
|
|
122
|
+
|
|
123
|
+
'select_statement.limit_clause.expr.literal.nbr.digit': 'limits',
|
|
124
|
+
} | common_contexts
|
|
125
|
+
|
|
126
|
+
if variant == 'p':
|
|
127
|
+
contexts_by_path = bash_contexts | contexts_by_path
|
|
128
|
+
elif variant == 'c':
|
|
129
|
+
contexts_by_path = {
|
|
130
|
+
'export_table.path.identifier_ref': 'tables',
|
|
131
|
+
'show_column_counts_command.path.identifier_ref': 'tables',
|
|
132
|
+
'export_statement.export_tables.keyspace_name': 'keyspaces',
|
|
133
|
+
|
|
134
|
+
'alter_cql_table_statement.properties.property.property_name': 'table-props',
|
|
135
|
+
'alter_tables_statement.properties.property.property_name': 'table-props',
|
|
136
|
+
'alter_cql_table_statement.properties.property.property_value.literal': 'table-props-value',
|
|
137
|
+
|
|
138
|
+
'select_clause.projection.result_expr.expr.path.identifier_ref': 'columns',
|
|
139
|
+
'export_statement.export_tables.export_table.column_name_list.column_name': 'columns',
|
|
140
|
+
|
|
141
|
+
'consistency_statement.consistency': 'consistencies',
|
|
142
|
+
'export_statement.export_to.export_database_type': 'export-database-types',
|
|
143
|
+
'drop_export_database.export_database_name': 'export-databases',
|
|
144
|
+
'use_export_db_statement.export_database_name': 'export-databases',
|
|
145
|
+
'clean_up_export_session_statement.clean_up_export_sessions.export_session_name': 'export-sessions',
|
|
146
|
+
'show_export_command.export_session_name': 'export-sessions',
|
|
147
|
+
'import_statement.import_session.export_session_name': 'export-sessions-incomplete',
|
|
148
|
+
'download_session_statement.export_session_name': 'export-sessions-incomplete',
|
|
149
|
+
} | bash_contexts | contexts_by_path
|
|
150
|
+
elif variant == 'l':
|
|
151
|
+
contexts_by_path = {
|
|
152
|
+
'add_partition_action.partition_ref.partition_name': 'partition-columns',
|
|
153
|
+
'show_topn_statement.topn_count': 'topn-counts',
|
|
154
|
+
'show_topn_statement.topn_type': 'topn-types',
|
|
155
|
+
'show_topn_statement.topn_window': 'topn-windows'
|
|
156
|
+
} | contexts_by_path
|
|
157
|
+
elif variant == 'x':
|
|
158
|
+
contexts_by_path = {
|
|
159
|
+
'show_column_counts_command.path.identifier_ref': 'tables',
|
|
160
|
+
'drop_export_database.export_database_name': 'export-databases',
|
|
161
|
+
'use_export_db_statement.export_database_name': 'export-databases',
|
|
162
|
+
} | contexts_by_path
|
|
163
|
+
|
|
164
|
+
grammar = grammar.replace('select_clause: "SELECT"i hint_comment? projection', 'select_clause: ("SELECT"i | "XELECT"i) hint_comment? projection')
|
|
165
|
+
|
|
166
|
+
with offload():
|
|
167
|
+
with open('/tmp/grammar.lark', 'wt') as f:
|
|
168
|
+
f.write(grammar)
|
|
169
|
+
|
|
170
|
+
return LarkParser(grammar, contexts_by_path)
|
|
171
|
+
|
|
172
|
+
def get_completions(
|
|
173
|
+
self, document: Document, complete_event: CompleteEvent
|
|
174
|
+
) -> Iterable[Completion]:
|
|
175
|
+
if not self.nested and self.options_lambda:
|
|
176
|
+
# for lazy completions
|
|
177
|
+
self.nested = NestedCompleter.from_nested_dict(self.options_lambda())
|
|
178
|
+
|
|
179
|
+
if self.nested:
|
|
180
|
+
# from NestedCompleter
|
|
181
|
+
|
|
182
|
+
# Split document.
|
|
183
|
+
text = document.text_before_cursor.lstrip()
|
|
184
|
+
stripped_len = len(document.text_before_cursor) - len(text)
|
|
185
|
+
|
|
186
|
+
# If there is a space, check for the first term, and use a
|
|
187
|
+
# subcompleter.
|
|
188
|
+
if " " in text:
|
|
189
|
+
first_term = text.split()[0]
|
|
190
|
+
completer = self.nested.options.get(first_term)
|
|
191
|
+
|
|
192
|
+
# If we have a sub completer, use this for the completions.
|
|
193
|
+
if completer is not None:
|
|
194
|
+
remaining_text = text[len(first_term) :].lstrip()
|
|
195
|
+
move_cursor = len(text) - len(remaining_text) + stripped_len
|
|
196
|
+
|
|
197
|
+
new_document = Document(
|
|
198
|
+
remaining_text,
|
|
199
|
+
cursor_position=document.cursor_position - move_cursor,
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
for c in completer.get_completions(new_document, complete_event):
|
|
203
|
+
yield c
|
|
204
|
+
|
|
205
|
+
# No space in the input: behave exactly like `WordCompleter`.
|
|
206
|
+
else:
|
|
207
|
+
completer = WordCompleter(
|
|
208
|
+
list(self.nested.options.keys()), ignore_case=self.nested.ignore_case
|
|
209
|
+
)
|
|
210
|
+
for c in completer.get_completions(document, complete_event):
|
|
211
|
+
yield c
|
|
212
|
+
|
|
213
|
+
if self.parser:
|
|
214
|
+
full = document.text_before_cursor
|
|
215
|
+
if self.dml:
|
|
216
|
+
full = self.dml + ' ' + full
|
|
217
|
+
|
|
218
|
+
words0 = []
|
|
219
|
+
words1 = []
|
|
220
|
+
context = {}
|
|
221
|
+
for word in self.parser.next_terminals(full, context=context):
|
|
222
|
+
if ex := self.expandable(word):
|
|
223
|
+
if ex in self.expandables:
|
|
224
|
+
e = self.expandables[ex]
|
|
225
|
+
if callable(e):
|
|
226
|
+
if self.auto_complete(ex) != 'off':
|
|
227
|
+
ctx = None
|
|
228
|
+
if 'last-id' in context:
|
|
229
|
+
ctx = context['last-id']
|
|
230
|
+
e = e(ctx)
|
|
231
|
+
words0.extend(e)
|
|
232
|
+
else:
|
|
233
|
+
words0.extend(e)
|
|
234
|
+
else:
|
|
235
|
+
words1.append(word)
|
|
236
|
+
words = words0 + words1
|
|
237
|
+
|
|
238
|
+
word_before_cursor = document.get_word_before_cursor(
|
|
239
|
+
WORD=self.WORD, pattern=self.pattern
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
word_before_cursor = word_before_cursor.lower()
|
|
243
|
+
|
|
244
|
+
def word_matches(word: str) -> bool:
|
|
245
|
+
return word.lower().startswith(word_before_cursor)
|
|
246
|
+
|
|
247
|
+
for a in words:
|
|
248
|
+
if word_matches(a):
|
|
249
|
+
display = self.display_dict.get(a, a)
|
|
250
|
+
display_meta = self.meta_dict.get(a, "")
|
|
251
|
+
yield Completion(
|
|
252
|
+
a,
|
|
253
|
+
-len(word_before_cursor),
|
|
254
|
+
display=display,
|
|
255
|
+
display_meta=display_meta,
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def completions_for_nesting(self, dml: str = None):
|
|
259
|
+
if dml:
|
|
260
|
+
return {dml: LarkCompleter(dml, expandables=self.expandables, variant=self.variant)}
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
word.text.lower(): LarkCompleter(word.text, expandables=self.expandables, variant=self.variant)
|
|
264
|
+
for word in self.get_completions(Document(''), None)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
def expandable(self, word: str):
|
|
268
|
+
return word.strip('`') if word.startswith('`') else None
|
|
269
|
+
|
|
270
|
+
def append_completions(self, key: str, value: dict[str, any]):
|
|
271
|
+
if isinstance(value, LarkCompleter) and self.variant == value.variant:
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
if self.nested:
|
|
275
|
+
self.nested = NestedCompleter.from_nested_dict(merge_completions(self.nested.options, value))
|
|
276
|
+
else:
|
|
277
|
+
self.nested = NestedCompleter.from_nested_dict(value)
|
|
278
|
+
|
|
279
|
+
def auto_complete(self, key: str, default = 'lazy'):
|
|
280
|
+
return Config().get(f'auto-complete.{self.variant}.{key}', default=default)
|