pltr-cli 0.1.2__py3-none-any.whl → 0.3.0__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.
- pltr/__main__.py +31 -0
- pltr/cli.py +21 -1
- pltr/commands/admin.py +530 -0
- pltr/commands/alias.py +241 -0
- pltr/commands/completion.py +383 -0
- pltr/commands/dataset.py +20 -3
- pltr/commands/ontology.py +502 -0
- pltr/commands/shell.py +126 -0
- pltr/commands/sql.py +358 -0
- pltr/commands/verify.py +2 -1
- pltr/config/aliases.py +254 -0
- pltr/services/__init__.py +4 -0
- pltr/services/admin.py +314 -0
- pltr/services/ontology.py +442 -0
- pltr/services/sql.py +340 -0
- pltr/utils/alias_resolver.py +56 -0
- pltr/utils/completion.py +178 -0
- pltr/utils/formatting.py +208 -0
- pltr/utils/progress.py +1 -1
- pltr_cli-0.3.0.dist-info/METADATA +280 -0
- pltr_cli-0.3.0.dist-info/RECORD +41 -0
- pltr_cli-0.1.2.dist-info/METADATA +0 -203
- pltr_cli-0.1.2.dist-info/RECORD +0 -28
- {pltr_cli-0.1.2.dist-info → pltr_cli-0.3.0.dist-info}/WHEEL +0 -0
- {pltr_cli-0.1.2.dist-info → pltr_cli-0.3.0.dist-info}/entry_points.txt +0 -0
- {pltr_cli-0.1.2.dist-info → pltr_cli-0.3.0.dist-info}/licenses/LICENSE +0 -0
pltr/services/sql.py
ADDED
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SQL service wrapper for Foundry SDK SQL queries.
|
|
3
|
+
Provides a high-level interface for executing SQL queries against Foundry datasets.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import time
|
|
7
|
+
from typing import Any, Dict, List, Optional, Union
|
|
8
|
+
import json
|
|
9
|
+
|
|
10
|
+
from foundry_sdk.v2.sql_queries.models import (
|
|
11
|
+
RunningQueryStatus,
|
|
12
|
+
SucceededQueryStatus,
|
|
13
|
+
FailedQueryStatus,
|
|
14
|
+
CanceledQueryStatus,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
from .base import BaseService
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class SqlService(BaseService):
|
|
21
|
+
"""Service wrapper for Foundry SQL query operations."""
|
|
22
|
+
|
|
23
|
+
def _get_service(self) -> Any:
|
|
24
|
+
"""Get the Foundry SQL queries service."""
|
|
25
|
+
return self.client.sql_queries.SqlQuery
|
|
26
|
+
|
|
27
|
+
def execute_query(
|
|
28
|
+
self,
|
|
29
|
+
query: str,
|
|
30
|
+
fallback_branch_ids: Optional[List[str]] = None,
|
|
31
|
+
timeout: int = 300,
|
|
32
|
+
format: str = "table",
|
|
33
|
+
) -> Dict[str, Any]:
|
|
34
|
+
"""
|
|
35
|
+
Execute a SQL query and wait for completion.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
query: SQL query string
|
|
39
|
+
fallback_branch_ids: Optional list of branch IDs for fallback
|
|
40
|
+
timeout: Maximum time to wait for query completion (seconds)
|
|
41
|
+
format: Output format for results ('table', 'json', 'raw')
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
Dictionary containing query results and metadata
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
RuntimeError: If query execution fails or times out
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
# Submit the query
|
|
51
|
+
status = self.service.execute(
|
|
52
|
+
query=query, fallback_branch_ids=fallback_branch_ids
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# If the query completed immediately
|
|
56
|
+
if isinstance(status, SucceededQueryStatus):
|
|
57
|
+
return self._format_completed_query(status.query_id, format)
|
|
58
|
+
elif isinstance(status, FailedQueryStatus):
|
|
59
|
+
raise RuntimeError(f"Query failed: {status.error_message}")
|
|
60
|
+
elif isinstance(status, CanceledQueryStatus):
|
|
61
|
+
raise RuntimeError("Query was canceled")
|
|
62
|
+
elif isinstance(status, RunningQueryStatus):
|
|
63
|
+
# Wait for completion
|
|
64
|
+
return self._wait_for_query_completion(status.query_id, timeout, format)
|
|
65
|
+
else:
|
|
66
|
+
raise RuntimeError(f"Unknown query status type: {type(status)}")
|
|
67
|
+
|
|
68
|
+
except Exception as e:
|
|
69
|
+
if isinstance(e, RuntimeError):
|
|
70
|
+
raise
|
|
71
|
+
raise RuntimeError(f"Failed to execute query: {e}")
|
|
72
|
+
|
|
73
|
+
def submit_query(
|
|
74
|
+
self, query: str, fallback_branch_ids: Optional[List[str]] = None
|
|
75
|
+
) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
Submit a SQL query without waiting for completion.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
query: SQL query string
|
|
81
|
+
fallback_branch_ids: Optional list of branch IDs for fallback
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dictionary containing query ID and initial status
|
|
85
|
+
|
|
86
|
+
Raises:
|
|
87
|
+
RuntimeError: If query submission fails
|
|
88
|
+
"""
|
|
89
|
+
try:
|
|
90
|
+
status = self.service.execute(
|
|
91
|
+
query=query, fallback_branch_ids=fallback_branch_ids
|
|
92
|
+
)
|
|
93
|
+
return self._format_query_status(status)
|
|
94
|
+
except Exception as e:
|
|
95
|
+
raise RuntimeError(f"Failed to submit query: {e}")
|
|
96
|
+
|
|
97
|
+
def get_query_status(self, query_id: str) -> Dict[str, Any]:
|
|
98
|
+
"""
|
|
99
|
+
Get the status of a submitted query.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
query_id: Query identifier
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Dictionary containing query status information
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
RuntimeError: If status check fails
|
|
109
|
+
"""
|
|
110
|
+
try:
|
|
111
|
+
status = self.service.get_status(query_id)
|
|
112
|
+
return self._format_query_status(status)
|
|
113
|
+
except Exception as e:
|
|
114
|
+
raise RuntimeError(f"Failed to get query status: {e}")
|
|
115
|
+
|
|
116
|
+
def get_query_results(self, query_id: str, format: str = "table") -> Dict[str, Any]:
|
|
117
|
+
"""
|
|
118
|
+
Get the results of a completed query.
|
|
119
|
+
|
|
120
|
+
Args:
|
|
121
|
+
query_id: Query identifier
|
|
122
|
+
format: Output format ('table', 'json', 'raw')
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Dictionary containing query results
|
|
126
|
+
|
|
127
|
+
Raises:
|
|
128
|
+
RuntimeError: If results retrieval fails
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
# First check if the query has completed successfully
|
|
132
|
+
status = self.service.get_status(query_id)
|
|
133
|
+
if not isinstance(status, SucceededQueryStatus):
|
|
134
|
+
status_info = self._format_query_status(status)
|
|
135
|
+
if isinstance(status, FailedQueryStatus):
|
|
136
|
+
raise RuntimeError(f"Query failed: {status.error_message}")
|
|
137
|
+
elif isinstance(status, CanceledQueryStatus):
|
|
138
|
+
raise RuntimeError("Query was canceled")
|
|
139
|
+
elif isinstance(status, RunningQueryStatus):
|
|
140
|
+
raise RuntimeError("Query is still running")
|
|
141
|
+
else:
|
|
142
|
+
raise RuntimeError(f"Query status: {status_info['status']}")
|
|
143
|
+
|
|
144
|
+
# Get the results
|
|
145
|
+
results_bytes = self.service.get_results(query_id)
|
|
146
|
+
return self._format_query_results(results_bytes, format)
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
if isinstance(e, RuntimeError):
|
|
150
|
+
raise
|
|
151
|
+
raise RuntimeError(f"Failed to get query results: {e}")
|
|
152
|
+
|
|
153
|
+
def cancel_query(self, query_id: str) -> Dict[str, Any]:
|
|
154
|
+
"""
|
|
155
|
+
Cancel a running query.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
query_id: Query identifier
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Dictionary containing cancellation status
|
|
162
|
+
|
|
163
|
+
Raises:
|
|
164
|
+
RuntimeError: If cancellation fails
|
|
165
|
+
"""
|
|
166
|
+
try:
|
|
167
|
+
self.service.cancel(query_id)
|
|
168
|
+
# Get updated status after cancellation
|
|
169
|
+
status = self.service.get_status(query_id)
|
|
170
|
+
return self._format_query_status(status)
|
|
171
|
+
except Exception as e:
|
|
172
|
+
raise RuntimeError(f"Failed to cancel query: {e}")
|
|
173
|
+
|
|
174
|
+
def wait_for_completion(
|
|
175
|
+
self, query_id: str, timeout: int = 300, poll_interval: int = 2
|
|
176
|
+
) -> Dict[str, Any]:
|
|
177
|
+
"""
|
|
178
|
+
Wait for a query to complete.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
query_id: Query identifier
|
|
182
|
+
timeout: Maximum time to wait (seconds)
|
|
183
|
+
poll_interval: Time between status checks (seconds)
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Dictionary containing final query status
|
|
187
|
+
|
|
188
|
+
Raises:
|
|
189
|
+
RuntimeError: If query fails or times out
|
|
190
|
+
"""
|
|
191
|
+
start_time = time.time()
|
|
192
|
+
|
|
193
|
+
while time.time() - start_time < timeout:
|
|
194
|
+
try:
|
|
195
|
+
status = self.service.get_status(query_id)
|
|
196
|
+
|
|
197
|
+
if isinstance(status, SucceededQueryStatus):
|
|
198
|
+
return self._format_query_status(status)
|
|
199
|
+
elif isinstance(status, FailedQueryStatus):
|
|
200
|
+
raise RuntimeError(f"Query failed: {status.error_message}")
|
|
201
|
+
elif isinstance(status, CanceledQueryStatus):
|
|
202
|
+
raise RuntimeError("Query was canceled")
|
|
203
|
+
elif isinstance(status, RunningQueryStatus):
|
|
204
|
+
# Still running, continue waiting
|
|
205
|
+
time.sleep(poll_interval)
|
|
206
|
+
continue
|
|
207
|
+
else:
|
|
208
|
+
raise RuntimeError(f"Unknown status type: {type(status)}")
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
if isinstance(e, RuntimeError):
|
|
212
|
+
raise
|
|
213
|
+
raise RuntimeError(f"Error checking query status: {e}")
|
|
214
|
+
|
|
215
|
+
# Timeout reached
|
|
216
|
+
raise RuntimeError(f"Query timed out after {timeout} seconds")
|
|
217
|
+
|
|
218
|
+
def _wait_for_query_completion(
|
|
219
|
+
self, query_id: str, timeout: int, format: str
|
|
220
|
+
) -> Dict[str, Any]:
|
|
221
|
+
"""
|
|
222
|
+
Wait for query completion and return formatted results.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
query_id: Query identifier
|
|
226
|
+
timeout: Maximum wait time
|
|
227
|
+
format: Result format
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Dictionary with query results
|
|
231
|
+
"""
|
|
232
|
+
# Wait for completion
|
|
233
|
+
self.wait_for_completion(query_id, timeout)
|
|
234
|
+
|
|
235
|
+
# Get results
|
|
236
|
+
return self._format_completed_query(query_id, format)
|
|
237
|
+
|
|
238
|
+
def _format_completed_query(self, query_id: str, format: str) -> Dict[str, Any]:
|
|
239
|
+
"""
|
|
240
|
+
Format a completed query's results.
|
|
241
|
+
|
|
242
|
+
Args:
|
|
243
|
+
query_id: Query identifier
|
|
244
|
+
format: Result format
|
|
245
|
+
|
|
246
|
+
Returns:
|
|
247
|
+
Formatted query results
|
|
248
|
+
"""
|
|
249
|
+
results_bytes = self.service.get_results(query_id)
|
|
250
|
+
results = self._format_query_results(results_bytes, format)
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
"query_id": query_id,
|
|
254
|
+
"status": "succeeded",
|
|
255
|
+
"results": results,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
def _format_query_status(
|
|
259
|
+
self,
|
|
260
|
+
status: Union[
|
|
261
|
+
RunningQueryStatus,
|
|
262
|
+
SucceededQueryStatus,
|
|
263
|
+
FailedQueryStatus,
|
|
264
|
+
CanceledQueryStatus,
|
|
265
|
+
],
|
|
266
|
+
) -> Dict[str, Any]:
|
|
267
|
+
"""
|
|
268
|
+
Format query status for consistent output.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
status: Query status object
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
Formatted status dictionary
|
|
275
|
+
"""
|
|
276
|
+
base_info: Dict[str, Any] = {"status": status.type}
|
|
277
|
+
|
|
278
|
+
if isinstance(status, (RunningQueryStatus, SucceededQueryStatus)):
|
|
279
|
+
base_info["query_id"] = status.query_id
|
|
280
|
+
elif isinstance(status, FailedQueryStatus):
|
|
281
|
+
base_info["error_message"] = status.error_message
|
|
282
|
+
|
|
283
|
+
return base_info
|
|
284
|
+
|
|
285
|
+
def _format_query_results(self, results_bytes: bytes, format: str) -> Any:
|
|
286
|
+
"""
|
|
287
|
+
Format query results based on the requested format.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
results_bytes: Raw results from the API
|
|
291
|
+
format: Desired output format
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
Formatted results
|
|
295
|
+
"""
|
|
296
|
+
if format == "raw":
|
|
297
|
+
return results_bytes
|
|
298
|
+
|
|
299
|
+
# Try to decode as text first
|
|
300
|
+
try:
|
|
301
|
+
results_text = results_bytes.decode("utf-8")
|
|
302
|
+
except UnicodeDecodeError:
|
|
303
|
+
# If it's binary data, return as base64 or hex
|
|
304
|
+
return {
|
|
305
|
+
"type": "binary",
|
|
306
|
+
"size_bytes": len(results_bytes),
|
|
307
|
+
"data": results_bytes.hex()[:200] + "..."
|
|
308
|
+
if len(results_bytes) > 100
|
|
309
|
+
else results_bytes.hex(),
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if format == "json":
|
|
313
|
+
try:
|
|
314
|
+
# Try to parse as JSON
|
|
315
|
+
return json.loads(results_text)
|
|
316
|
+
except json.JSONDecodeError:
|
|
317
|
+
# Return as text if not valid JSON
|
|
318
|
+
return {"text": results_text}
|
|
319
|
+
|
|
320
|
+
elif format == "table":
|
|
321
|
+
# For table format, we'll return structured data
|
|
322
|
+
# that the formatter can convert to a table
|
|
323
|
+
try:
|
|
324
|
+
# Try parsing as JSON first for structured data
|
|
325
|
+
data = json.loads(results_text)
|
|
326
|
+
if isinstance(data, list) and data and isinstance(data[0], dict):
|
|
327
|
+
# List of dictionaries - perfect for table format
|
|
328
|
+
return data
|
|
329
|
+
else:
|
|
330
|
+
return {"result": data}
|
|
331
|
+
except json.JSONDecodeError:
|
|
332
|
+
# Return as text data
|
|
333
|
+
lines = results_text.strip().split("\n")
|
|
334
|
+
if len(lines) == 1:
|
|
335
|
+
return {"result": lines[0]}
|
|
336
|
+
else:
|
|
337
|
+
return {"results": lines}
|
|
338
|
+
|
|
339
|
+
# Default: return as text
|
|
340
|
+
return {"text": results_text}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
"""Alias resolution utilities for CLI commands."""
|
|
2
|
+
|
|
3
|
+
import shlex
|
|
4
|
+
import sys
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
|
|
7
|
+
from pltr.config.aliases import AliasManager
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def resolve_command_aliases(args: Optional[List[str]] = None) -> List[str]:
|
|
11
|
+
"""Resolve command aliases in the argument list.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
args: Command arguments (defaults to sys.argv[1:])
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
Resolved command arguments
|
|
18
|
+
"""
|
|
19
|
+
if args is None:
|
|
20
|
+
args = sys.argv[1:]
|
|
21
|
+
|
|
22
|
+
if not args:
|
|
23
|
+
return args
|
|
24
|
+
|
|
25
|
+
# Don't resolve aliases for certain commands
|
|
26
|
+
if args[0] in ["alias", "--help", "-h", "--version", "completion"]:
|
|
27
|
+
return args
|
|
28
|
+
|
|
29
|
+
# Check if the first argument is an alias
|
|
30
|
+
manager = AliasManager()
|
|
31
|
+
first_arg = args[0]
|
|
32
|
+
|
|
33
|
+
# Try to resolve as an alias
|
|
34
|
+
resolved = manager.resolve_alias(first_arg)
|
|
35
|
+
|
|
36
|
+
if resolved != first_arg:
|
|
37
|
+
# It's an alias - parse the resolved command
|
|
38
|
+
try:
|
|
39
|
+
resolved_parts = shlex.split(resolved)
|
|
40
|
+
# Replace the first argument with the resolved command parts
|
|
41
|
+
# and append any additional arguments
|
|
42
|
+
return resolved_parts + args[1:]
|
|
43
|
+
except ValueError:
|
|
44
|
+
# If parsing fails, return original args
|
|
45
|
+
return args
|
|
46
|
+
|
|
47
|
+
return args
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def inject_alias_resolution() -> None:
|
|
51
|
+
"""Inject alias resolution into sys.argv before CLI parsing."""
|
|
52
|
+
# Get resolved arguments
|
|
53
|
+
resolved_args = resolve_command_aliases()
|
|
54
|
+
|
|
55
|
+
# Replace sys.argv with resolved arguments
|
|
56
|
+
sys.argv = [sys.argv[0]] + resolved_args
|
pltr/utils/completion.py
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"""Shell completion utilities for pltr CLI."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from typing import List
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from pltr.config.profiles import ProfileManager
|
|
9
|
+
from pltr.config.aliases import AliasManager
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_cached_rids() -> List[str]:
|
|
13
|
+
"""Get recently used RIDs from cache."""
|
|
14
|
+
cache_dir = Path.home() / ".cache" / "pltr"
|
|
15
|
+
rid_cache_file = cache_dir / "recent_rids.json"
|
|
16
|
+
|
|
17
|
+
if rid_cache_file.exists():
|
|
18
|
+
try:
|
|
19
|
+
with open(rid_cache_file) as f:
|
|
20
|
+
data = json.load(f)
|
|
21
|
+
return data.get("rids", [])
|
|
22
|
+
except Exception:
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
# Return some example RIDs if no cache
|
|
26
|
+
return [
|
|
27
|
+
"ri.foundry.main.dataset.",
|
|
28
|
+
"ri.foundry.main.folder.",
|
|
29
|
+
"ri.foundry.main.ontology.",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def cache_rid(rid: str):
|
|
34
|
+
"""Cache a RID for future completions."""
|
|
35
|
+
cache_dir = Path.home() / ".cache" / "pltr"
|
|
36
|
+
cache_dir.mkdir(parents=True, exist_ok=True)
|
|
37
|
+
rid_cache_file = cache_dir / "recent_rids.json"
|
|
38
|
+
|
|
39
|
+
# Load existing cache
|
|
40
|
+
rids = []
|
|
41
|
+
if rid_cache_file.exists():
|
|
42
|
+
try:
|
|
43
|
+
with open(rid_cache_file) as f:
|
|
44
|
+
data = json.load(f)
|
|
45
|
+
rids = data.get("rids", [])
|
|
46
|
+
except Exception:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
# Add new RID (keep last 50)
|
|
50
|
+
if rid not in rids:
|
|
51
|
+
rids.insert(0, rid)
|
|
52
|
+
rids = rids[:50]
|
|
53
|
+
|
|
54
|
+
# Save cache
|
|
55
|
+
try:
|
|
56
|
+
with open(rid_cache_file, "w") as f:
|
|
57
|
+
json.dump({"rids": rids}, f)
|
|
58
|
+
except Exception:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def complete_rid(incomplete: str):
|
|
63
|
+
"""Complete RID arguments."""
|
|
64
|
+
rids = get_cached_rids()
|
|
65
|
+
return [rid for rid in rids if rid.startswith(incomplete)]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def complete_profile(incomplete: str):
|
|
69
|
+
"""Complete profile names."""
|
|
70
|
+
try:
|
|
71
|
+
manager = ProfileManager()
|
|
72
|
+
profiles = manager.list_profiles()
|
|
73
|
+
return [profile for profile in profiles if profile.startswith(incomplete)]
|
|
74
|
+
except Exception:
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def complete_output_format(incomplete: str):
|
|
79
|
+
"""Complete output format options."""
|
|
80
|
+
formats = ["table", "json", "csv"]
|
|
81
|
+
return [fmt for fmt in formats if fmt.startswith(incomplete)]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def complete_sql_query(incomplete: str):
|
|
85
|
+
"""Complete SQL query templates."""
|
|
86
|
+
templates = [
|
|
87
|
+
"SELECT * FROM ",
|
|
88
|
+
"SELECT COUNT(*) FROM ",
|
|
89
|
+
"SELECT DISTINCT ",
|
|
90
|
+
"WHERE ",
|
|
91
|
+
"GROUP BY ",
|
|
92
|
+
"ORDER BY ",
|
|
93
|
+
"LIMIT 10",
|
|
94
|
+
"JOIN ",
|
|
95
|
+
"LEFT JOIN ",
|
|
96
|
+
"INNER JOIN ",
|
|
97
|
+
]
|
|
98
|
+
return [tmpl for tmpl in templates if tmpl.lower().startswith(incomplete.lower())]
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def complete_ontology_action(incomplete: str):
|
|
102
|
+
"""Complete ontology action names."""
|
|
103
|
+
# This would ideally fetch from the API but for now return common patterns
|
|
104
|
+
actions = [
|
|
105
|
+
"create",
|
|
106
|
+
"update",
|
|
107
|
+
"delete",
|
|
108
|
+
"createOrUpdate",
|
|
109
|
+
"link",
|
|
110
|
+
"unlink",
|
|
111
|
+
]
|
|
112
|
+
return [action for action in actions if action.startswith(incomplete)]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def complete_alias_names(incomplete: str):
|
|
116
|
+
"""Complete alias names."""
|
|
117
|
+
manager = AliasManager()
|
|
118
|
+
aliases = manager.get_completion_items()
|
|
119
|
+
return [alias for alias in aliases if alias.startswith(incomplete)]
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def complete_file_path(incomplete: str):
|
|
123
|
+
"""Complete file paths."""
|
|
124
|
+
# This is handled by shell natively, but we can provide hints
|
|
125
|
+
path = Path(incomplete) if incomplete else Path.cwd()
|
|
126
|
+
|
|
127
|
+
if incomplete and not path.exists():
|
|
128
|
+
parent = path.parent
|
|
129
|
+
prefix = path.name
|
|
130
|
+
else:
|
|
131
|
+
parent = path if path.is_dir() else path.parent
|
|
132
|
+
prefix = ""
|
|
133
|
+
|
|
134
|
+
try:
|
|
135
|
+
items = []
|
|
136
|
+
for item in parent.iterdir():
|
|
137
|
+
if item.name.startswith(prefix):
|
|
138
|
+
# Return path strings - shell will handle directory indicators
|
|
139
|
+
items.append(str(item))
|
|
140
|
+
return items
|
|
141
|
+
except Exception:
|
|
142
|
+
return []
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def setup_completion_environment():
|
|
146
|
+
"""Set up environment for shell completion support."""
|
|
147
|
+
# This is called when the CLI starts to register completion handlers
|
|
148
|
+
|
|
149
|
+
# Check if we're in completion mode
|
|
150
|
+
if os.environ.get("_PLTR_COMPLETE"):
|
|
151
|
+
# We're generating completions
|
|
152
|
+
# Set up any necessary context
|
|
153
|
+
pass
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def handle_completion():
|
|
157
|
+
"""Handle shell completion requests."""
|
|
158
|
+
# This is the main entry point for completion handling
|
|
159
|
+
# It's called when _PLTR_COMPLETE environment variable is set
|
|
160
|
+
|
|
161
|
+
complete_var = os.environ.get("_PLTR_COMPLETE")
|
|
162
|
+
if not complete_var:
|
|
163
|
+
return False
|
|
164
|
+
|
|
165
|
+
# Click handles the completion automatically through Typer
|
|
166
|
+
# Our custom completion functions are registered via autocompletion parameter
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Register custom completion functions for specific parameter types
|
|
171
|
+
COMPLETION_FUNCTIONS = {
|
|
172
|
+
"rid": complete_rid,
|
|
173
|
+
"profile": complete_profile,
|
|
174
|
+
"output_format": complete_output_format,
|
|
175
|
+
"sql_query": complete_sql_query,
|
|
176
|
+
"ontology_action": complete_ontology_action,
|
|
177
|
+
"file_path": complete_file_path,
|
|
178
|
+
}
|