glaip-sdk 0.7.26__py3-none-any.whl → 0.7.28__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.
- glaip_sdk/cli/slash/tui/__init__.py +2 -2
- glaip_sdk/cli/slash/tui/accounts_app.py +109 -47
- glaip_sdk/cli/slash/tui/remote_runs_app.py +854 -580
- glaip_sdk/cli/slash/tui/toast.py +29 -3
- glaip_sdk/utils/import_resolver.py +384 -38
- {glaip_sdk-0.7.26.dist-info → glaip_sdk-0.7.28.dist-info}/METADATA +1 -1
- {glaip_sdk-0.7.26.dist-info → glaip_sdk-0.7.28.dist-info}/RECORD +10 -10
- {glaip_sdk-0.7.26.dist-info → glaip_sdk-0.7.28.dist-info}/WHEEL +0 -0
- {glaip_sdk-0.7.26.dist-info → glaip_sdk-0.7.28.dist-info}/entry_points.txt +0 -0
- {glaip_sdk-0.7.26.dist-info → glaip_sdk-0.7.28.dist-info}/top_level.txt +0 -0
glaip_sdk/cli/slash/tui/toast.py
CHANGED
|
@@ -9,6 +9,7 @@ from enum import Enum
|
|
|
9
9
|
from typing import Any, cast
|
|
10
10
|
|
|
11
11
|
from rich.text import Text
|
|
12
|
+
from textual.app import ComposeResult
|
|
12
13
|
from textual.message import Message
|
|
13
14
|
from textual.widget import Widget
|
|
14
15
|
from textual.widgets import Static
|
|
@@ -55,13 +56,25 @@ class ToastBus:
|
|
|
55
56
|
"""Initialize the toast bus with optional change callback."""
|
|
56
57
|
self._state: ToastState | None = None
|
|
57
58
|
self._dismiss_task: asyncio.Task[None] | None = None
|
|
58
|
-
self.
|
|
59
|
+
self._subscribers: list[Callable[[ToastBus.Changed], None]] = []
|
|
60
|
+
if on_change is not None:
|
|
61
|
+
self._subscribers.append(on_change)
|
|
59
62
|
|
|
60
63
|
@property
|
|
61
64
|
def state(self) -> ToastState | None:
|
|
62
65
|
"""Return the current toast state, or None if no toast is shown."""
|
|
63
66
|
return self._state
|
|
64
67
|
|
|
68
|
+
def subscribe(self, callback: Callable[[ToastBus.Changed], None]) -> None:
|
|
69
|
+
"""Register a callback to be notified of toast state changes."""
|
|
70
|
+
if callback not in self._subscribers:
|
|
71
|
+
self._subscribers.append(callback)
|
|
72
|
+
|
|
73
|
+
def unsubscribe(self, callback: Callable[[ToastBus.Changed], None]) -> None:
|
|
74
|
+
"""Unregister a toast callback."""
|
|
75
|
+
if callback in self._subscribers:
|
|
76
|
+
self._subscribers.remove(callback)
|
|
77
|
+
|
|
65
78
|
def show(
|
|
66
79
|
self,
|
|
67
80
|
message: str,
|
|
@@ -146,8 +159,12 @@ class ToastBus:
|
|
|
146
159
|
self._notify_changed()
|
|
147
160
|
|
|
148
161
|
def _notify_changed(self) -> None:
|
|
149
|
-
|
|
150
|
-
|
|
162
|
+
message = ToastBus.Changed(self._state)
|
|
163
|
+
for callback in tuple(self._subscribers):
|
|
164
|
+
try:
|
|
165
|
+
callback(message)
|
|
166
|
+
except Exception:
|
|
167
|
+
pass
|
|
151
168
|
|
|
152
169
|
|
|
153
170
|
class ToastHandlerMixin:
|
|
@@ -303,6 +320,15 @@ class ToastContainer(Widget):
|
|
|
303
320
|
yield ToastContainer(Toast(), id="toast-container")
|
|
304
321
|
"""
|
|
305
322
|
|
|
323
|
+
def __init__(self, toast: Toast, id: str | None = None) -> None:
|
|
324
|
+
"""Initialize the toast container with a toast widget."""
|
|
325
|
+
super().__init__(id=id)
|
|
326
|
+
self._toast = toast
|
|
327
|
+
|
|
328
|
+
def compose(self) -> ComposeResult:
|
|
329
|
+
"""Compose the container by yielding the toast widget."""
|
|
330
|
+
yield self._toast
|
|
331
|
+
|
|
306
332
|
|
|
307
333
|
class Toast(Static):
|
|
308
334
|
"""A Textual widget that displays toast notifications at the top-right of the screen.
|
|
@@ -15,6 +15,8 @@ from pathlib import Path
|
|
|
15
15
|
|
|
16
16
|
from glaip_sdk.utils.tool_detection import is_tool_plugin_decorator
|
|
17
17
|
|
|
18
|
+
INIT_FILE = "__init__.py"
|
|
19
|
+
|
|
18
20
|
|
|
19
21
|
class ImportResolver:
|
|
20
22
|
"""Resolves and categorizes Python imports for tool bundling.
|
|
@@ -46,6 +48,36 @@ class ImportResolver:
|
|
|
46
48
|
"""
|
|
47
49
|
self.tool_dir = tool_dir
|
|
48
50
|
self._processed_modules: set[str] = set()
|
|
51
|
+
self._package_root: Path = self._find_package_root(tool_dir)
|
|
52
|
+
|
|
53
|
+
def _find_package_root(self, start_path: Path) -> Path:
|
|
54
|
+
"""Find package root using standard project markers.
|
|
55
|
+
|
|
56
|
+
Searches upward from start_path for common Python project markers:
|
|
57
|
+
- pyproject.toml (modern Python standard)
|
|
58
|
+
- setup.py (legacy setuptools)
|
|
59
|
+
- .git (version control root)
|
|
60
|
+
|
|
61
|
+
Falls back to start_path.parent if no markers found.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
start_path: Starting directory to search from.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
Path to the identified package root.
|
|
68
|
+
"""
|
|
69
|
+
# Check current directory and all parents
|
|
70
|
+
for parent in [start_path, *start_path.parents]:
|
|
71
|
+
if (parent / "pyproject.toml").exists():
|
|
72
|
+
return parent
|
|
73
|
+
if (parent / "setup.py").exists():
|
|
74
|
+
return parent
|
|
75
|
+
if (parent / ".git").exists():
|
|
76
|
+
return parent
|
|
77
|
+
|
|
78
|
+
# Fallback: return parent of start_path
|
|
79
|
+
# This maintains backward compatibility with original behavior
|
|
80
|
+
return start_path.parent
|
|
49
81
|
|
|
50
82
|
def categorize_imports(self, tree: ast.AST) -> tuple[list, list]:
|
|
51
83
|
"""Categorize imports into local and external.
|
|
@@ -63,7 +95,7 @@ class ImportResolver:
|
|
|
63
95
|
for node in ast.walk(tree):
|
|
64
96
|
if isinstance(node, ast.ImportFrom):
|
|
65
97
|
if self.is_local_import(node):
|
|
66
|
-
module_file = self.
|
|
98
|
+
module_file = self._resolve_import_path(node)
|
|
67
99
|
local_imports.append((node.module, module_file, node))
|
|
68
100
|
else:
|
|
69
101
|
external_imports.append(node)
|
|
@@ -72,6 +104,25 @@ class ImportResolver:
|
|
|
72
104
|
|
|
73
105
|
return local_imports, external_imports
|
|
74
106
|
|
|
107
|
+
def _resolve_import_path(self, node: ast.ImportFrom) -> Path:
|
|
108
|
+
"""Resolve import node to file path.
|
|
109
|
+
|
|
110
|
+
Handles both absolute and relative imports.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
node: ImportFrom AST node.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Path to the module file.
|
|
117
|
+
"""
|
|
118
|
+
if node.level > 0:
|
|
119
|
+
return self.resolve_relative_import_path(node)
|
|
120
|
+
|
|
121
|
+
if not node.module:
|
|
122
|
+
return self.tool_dir / INIT_FILE
|
|
123
|
+
|
|
124
|
+
return self.resolve_module_path(node.module)
|
|
125
|
+
|
|
75
126
|
def is_local_import(self, node: ast.ImportFrom) -> bool:
|
|
76
127
|
"""Check if import is local to the tool directory.
|
|
77
128
|
|
|
@@ -81,16 +132,83 @@ class ImportResolver:
|
|
|
81
132
|
Returns:
|
|
82
133
|
True if import is local.
|
|
83
134
|
"""
|
|
135
|
+
# Handle relative imports (level > 0)
|
|
136
|
+
if node.level > 0:
|
|
137
|
+
return self._is_local_relative_import(node)
|
|
138
|
+
|
|
139
|
+
# Handle absolute imports with no module name
|
|
84
140
|
if not node.module:
|
|
85
141
|
return False
|
|
86
142
|
|
|
87
|
-
# Handle package imports
|
|
143
|
+
# Handle dotted package imports
|
|
88
144
|
if "." in node.module:
|
|
89
145
|
return self._is_local_package_import(node.module)
|
|
90
146
|
|
|
147
|
+
# Handle simple module imports
|
|
91
148
|
potential_file = self.tool_dir / f"{node.module}.py"
|
|
92
149
|
return potential_file.exists()
|
|
93
150
|
|
|
151
|
+
def _is_local_relative_import(self, node: ast.ImportFrom) -> bool:
|
|
152
|
+
"""Check if a relative import is local.
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
node: ImportFrom node with node.level > 0.
|
|
156
|
+
|
|
157
|
+
Returns:
|
|
158
|
+
True if the relative import resolves to a local file.
|
|
159
|
+
"""
|
|
160
|
+
if node.level == 0:
|
|
161
|
+
return False
|
|
162
|
+
|
|
163
|
+
base_dir = self._calculate_relative_base_dir(node.level)
|
|
164
|
+
if not base_dir:
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
if node.module is None:
|
|
168
|
+
return self._is_init_file_present(base_dir)
|
|
169
|
+
|
|
170
|
+
return self._check_module_exists(base_dir, node.module)
|
|
171
|
+
|
|
172
|
+
def _calculate_relative_base_dir(self, level: int) -> Path | None:
|
|
173
|
+
"""Calculate base directory for relative import.
|
|
174
|
+
|
|
175
|
+
Args:
|
|
176
|
+
level: Relative import level (1=current, 2=parent, etc.).
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
Base directory path or None if exceeds filesystem root.
|
|
180
|
+
"""
|
|
181
|
+
base_dir = self.tool_dir
|
|
182
|
+
for _ in range(level - 1):
|
|
183
|
+
if base_dir == base_dir.parent:
|
|
184
|
+
return None
|
|
185
|
+
base_dir = base_dir.parent
|
|
186
|
+
return base_dir
|
|
187
|
+
|
|
188
|
+
def _is_init_file_present(self, base_dir: Path) -> bool:
|
|
189
|
+
"""Check if __init__.py exists in directory.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
base_dir: Directory to check.
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
True if __init__.py exists.
|
|
196
|
+
"""
|
|
197
|
+
return (base_dir / INIT_FILE).exists()
|
|
198
|
+
|
|
199
|
+
def _check_module_exists(self, base_dir: Path, module: str) -> bool:
|
|
200
|
+
"""Check if module exists in base directory.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
base_dir: Base directory to search from.
|
|
204
|
+
module: Module name (dotted or simple).
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
True if module file or package exists.
|
|
208
|
+
"""
|
|
209
|
+
parts = module.split(".")
|
|
210
|
+
return self._module_parts_exist(base_dir, parts)
|
|
211
|
+
|
|
94
212
|
def _is_local_package_import(self, module: str) -> bool:
|
|
95
213
|
"""Check if a dotted module path is local.
|
|
96
214
|
|
|
@@ -102,29 +220,141 @@ class ImportResolver:
|
|
|
102
220
|
"""
|
|
103
221
|
parts = module.split(".")
|
|
104
222
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
223
|
+
if self._check_module_in_tool_dir(parts):
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
if self._check_module_in_subdirectory(parts):
|
|
227
|
+
return True
|
|
228
|
+
|
|
229
|
+
if self._check_module_in_ancestor_packages(parts):
|
|
230
|
+
return True
|
|
231
|
+
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
def _check_module_in_tool_dir(self, parts: list[str]) -> bool:
|
|
235
|
+
"""Check if module is in the current tool directory (Case 1).
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
parts: Module path split into components.
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
True if module is found in tool_dir.
|
|
242
|
+
"""
|
|
243
|
+
if parts[0] != self.tool_dir.name:
|
|
244
|
+
return False
|
|
245
|
+
|
|
246
|
+
remaining_parts = parts[1:]
|
|
247
|
+
if not remaining_parts:
|
|
248
|
+
return False
|
|
249
|
+
|
|
250
|
+
return self._module_parts_exist(self.tool_dir, remaining_parts)
|
|
251
|
+
|
|
252
|
+
def _check_module_in_subdirectory(self, parts: list[str]) -> bool:
|
|
253
|
+
"""Check if module is in a subdirectory of tool_dir (Case 2).
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
parts: Module path split into components.
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
True if module is found in a subdirectory.
|
|
260
|
+
"""
|
|
118
261
|
package_dir = self.tool_dir / parts[0]
|
|
119
|
-
if package_dir.is_dir():
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
262
|
+
if not package_dir.is_dir():
|
|
263
|
+
return False
|
|
264
|
+
|
|
265
|
+
return self._module_parts_exist(self.tool_dir, parts)
|
|
266
|
+
|
|
267
|
+
def _check_module_in_ancestor_packages(self, parts: list[str]) -> bool:
|
|
268
|
+
"""Check if module is in the package root.
|
|
269
|
+
|
|
270
|
+
Uses the discovered package root (via project markers) to resolve
|
|
271
|
+
imports from the package root. Also falls back to directory name
|
|
272
|
+
matching for backward compatibility.
|
|
273
|
+
|
|
274
|
+
Args:
|
|
275
|
+
parts: Module path split into components.
|
|
276
|
+
|
|
277
|
+
Returns:
|
|
278
|
+
True if module is found in the package root.
|
|
279
|
+
"""
|
|
280
|
+
# Strategy 1: Check if package root contains the module
|
|
281
|
+
if self._check_module_in_package_root(parts):
|
|
282
|
+
return True
|
|
283
|
+
|
|
284
|
+
# Strategy 2: Fallback to directory name matching (backward compatibility)
|
|
285
|
+
return self._check_module_by_directory_name(parts)
|
|
286
|
+
|
|
287
|
+
def _check_module_in_package_root(self, parts: list[str]) -> bool:
|
|
288
|
+
"""Check if module exists in the discovered package root.
|
|
289
|
+
|
|
290
|
+
Args:
|
|
291
|
+
parts: Module path split into components.
|
|
292
|
+
|
|
293
|
+
Returns:
|
|
294
|
+
True if module exists in package root.
|
|
295
|
+
"""
|
|
296
|
+
# Resolve module from package root
|
|
297
|
+
module_path = self._resolve_from_package_root_path(self._package_root, parts)
|
|
298
|
+
return module_path.exists()
|
|
299
|
+
|
|
300
|
+
def _check_module_by_directory_name(self, parts: list[str]) -> bool:
|
|
301
|
+
"""Check module by matching directory names (legacy fallback).
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
parts: Module path split into components.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
True if module found by directory name matching.
|
|
308
|
+
"""
|
|
309
|
+
current = self.tool_dir
|
|
310
|
+
|
|
311
|
+
while current != current.parent:
|
|
312
|
+
if parts[0] == current.name:
|
|
313
|
+
return self._resolve_from_package_root(current, parts)
|
|
314
|
+
current = current.parent
|
|
125
315
|
|
|
126
316
|
return False
|
|
127
317
|
|
|
318
|
+
def _resolve_from_package_root(self, package_root: Path, parts: list[str]) -> bool:
|
|
319
|
+
"""Resolve module from a found package root directory.
|
|
320
|
+
|
|
321
|
+
Args:
|
|
322
|
+
package_root: The identified package root directory.
|
|
323
|
+
parts: Module path split into components.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
True if module exists in the package root.
|
|
327
|
+
"""
|
|
328
|
+
remaining_parts = parts[1:]
|
|
329
|
+
|
|
330
|
+
if not remaining_parts:
|
|
331
|
+
return (package_root / INIT_FILE).exists()
|
|
332
|
+
|
|
333
|
+
return self._module_parts_exist(package_root, remaining_parts)
|
|
334
|
+
|
|
335
|
+
def _module_parts_exist(self, base_dir: Path, parts: list[str]) -> bool:
|
|
336
|
+
"""Check if module parts exist as a file or package.
|
|
337
|
+
|
|
338
|
+
Args:
|
|
339
|
+
base_dir: Base directory to search from.
|
|
340
|
+
parts: Remaining module path components.
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
True if module file or package exists.
|
|
344
|
+
"""
|
|
345
|
+
if len(parts) == 1:
|
|
346
|
+
module_file = base_dir / f"{parts[0]}.py"
|
|
347
|
+
if module_file.exists():
|
|
348
|
+
return True
|
|
349
|
+
|
|
350
|
+
if len(parts) > 1:
|
|
351
|
+
module_file = base_dir / "/".join(parts[:-1]) / f"{parts[-1]}.py"
|
|
352
|
+
if module_file.exists():
|
|
353
|
+
return True
|
|
354
|
+
|
|
355
|
+
init_file = base_dir / "/".join(parts) / INIT_FILE
|
|
356
|
+
return init_file.exists()
|
|
357
|
+
|
|
128
358
|
def resolve_module_path(self, module_name: str) -> Path:
|
|
129
359
|
"""Resolve module name to file path.
|
|
130
360
|
|
|
@@ -138,6 +368,40 @@ class ImportResolver:
|
|
|
138
368
|
return self._resolve_dotted_module_path(module_name)
|
|
139
369
|
return self.tool_dir / f"{module_name}.py"
|
|
140
370
|
|
|
371
|
+
def resolve_relative_import_path(self, node: ast.ImportFrom) -> Path:
|
|
372
|
+
"""Resolve relative import to file path.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
node: ImportFrom node with node.level > 0.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Path to the module file.
|
|
379
|
+
|
|
380
|
+
Raises:
|
|
381
|
+
ValueError: If relative import level exceeds directory depth.
|
|
382
|
+
"""
|
|
383
|
+
base_dir = self._calculate_relative_base_dir(node.level)
|
|
384
|
+
if base_dir is None:
|
|
385
|
+
raise ValueError(f"Invalid relative import: level {node.level} exceeds directory depth")
|
|
386
|
+
|
|
387
|
+
if node.module is None:
|
|
388
|
+
return base_dir / INIT_FILE
|
|
389
|
+
|
|
390
|
+
return self._resolve_dotted_module_path_from_base(base_dir, node.module)
|
|
391
|
+
|
|
392
|
+
def _resolve_dotted_module_path_from_base(self, base_dir: Path, module_name: str) -> Path:
|
|
393
|
+
"""Resolve dotted module path from a specific base directory.
|
|
394
|
+
|
|
395
|
+
Args:
|
|
396
|
+
base_dir: Base directory to resolve from.
|
|
397
|
+
module_name: Dotted module path (e.g., 'package.module').
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Path to the module file.
|
|
401
|
+
"""
|
|
402
|
+
parts = module_name.split(".")
|
|
403
|
+
return self._build_module_path(base_dir, parts)
|
|
404
|
+
|
|
141
405
|
def _resolve_dotted_module_path(self, module_name: str) -> Path:
|
|
142
406
|
"""Resolve a dotted module path to a file path.
|
|
143
407
|
|
|
@@ -149,25 +413,107 @@ class ImportResolver:
|
|
|
149
413
|
"""
|
|
150
414
|
parts = module_name.split(".")
|
|
151
415
|
|
|
152
|
-
|
|
153
|
-
if
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
if module_path.exists():
|
|
158
|
-
return module_path
|
|
159
|
-
elif len(remaining_parts) > 1:
|
|
160
|
-
module_path = self.tool_dir / "/".join(remaining_parts[:-1]) / f"{remaining_parts[-1]}.py"
|
|
161
|
-
if module_path.exists():
|
|
162
|
-
return module_path
|
|
163
|
-
|
|
164
|
-
# Case 2: Standard package/module.py
|
|
165
|
-
module_path = self.tool_dir / "/".join(parts[:-1]) / f"{parts[-1]}.py"
|
|
416
|
+
module_path = self._resolve_from_tool_dir(parts)
|
|
417
|
+
if module_path and module_path.exists():
|
|
418
|
+
return module_path
|
|
419
|
+
|
|
420
|
+
module_path = self._resolve_from_subdir(parts)
|
|
166
421
|
if module_path.exists():
|
|
167
422
|
return module_path
|
|
168
423
|
|
|
169
|
-
|
|
170
|
-
|
|
424
|
+
module_path = self._resolve_from_ancestor_packages(parts)
|
|
425
|
+
if module_path and module_path.exists():
|
|
426
|
+
return module_path
|
|
427
|
+
|
|
428
|
+
return self.tool_dir / "/".join(parts) / INIT_FILE
|
|
429
|
+
|
|
430
|
+
def _resolve_from_tool_dir(self, parts: list[str]) -> Path | None:
|
|
431
|
+
"""Resolve module from current tool directory.
|
|
432
|
+
|
|
433
|
+
Args:
|
|
434
|
+
parts: Module path split into components.
|
|
435
|
+
|
|
436
|
+
Returns:
|
|
437
|
+
Path to potential module file, or None if not applicable.
|
|
438
|
+
"""
|
|
439
|
+
if parts[0] != self.tool_dir.name:
|
|
440
|
+
return None
|
|
441
|
+
|
|
442
|
+
remaining_parts = parts[1:]
|
|
443
|
+
if not remaining_parts:
|
|
444
|
+
return self.tool_dir / INIT_FILE
|
|
445
|
+
|
|
446
|
+
return self._build_module_path(self.tool_dir, remaining_parts)
|
|
447
|
+
|
|
448
|
+
def _resolve_from_subdir(self, parts: list[str]) -> Path:
|
|
449
|
+
"""Resolve module from a subdirectory.
|
|
450
|
+
|
|
451
|
+
Args:
|
|
452
|
+
parts: Module path split into components.
|
|
453
|
+
|
|
454
|
+
Returns:
|
|
455
|
+
Path to potential module file.
|
|
456
|
+
"""
|
|
457
|
+
return self._build_module_path(self.tool_dir, parts)
|
|
458
|
+
|
|
459
|
+
def _resolve_from_ancestor_packages(self, parts: list[str]) -> Path | None:
|
|
460
|
+
"""Resolve module from an ancestor directory (Package Root Traversal).
|
|
461
|
+
|
|
462
|
+
Traverses up the directory tree looking for a matching package root.
|
|
463
|
+
|
|
464
|
+
Args:
|
|
465
|
+
parts: Module path split into components.
|
|
466
|
+
|
|
467
|
+
Returns:
|
|
468
|
+
Path to module if found in ancestor, None otherwise.
|
|
469
|
+
"""
|
|
470
|
+
current = self.tool_dir
|
|
471
|
+
|
|
472
|
+
while current != current.parent:
|
|
473
|
+
if parts[0] == current.name:
|
|
474
|
+
return self._resolve_from_package_root_path(current, parts)
|
|
475
|
+
current = current.parent
|
|
476
|
+
|
|
477
|
+
return None
|
|
478
|
+
|
|
479
|
+
def _resolve_from_package_root_path(self, package_root: Path, parts: list[str]) -> Path:
|
|
480
|
+
"""Build module path from a found package root.
|
|
481
|
+
|
|
482
|
+
Args:
|
|
483
|
+
package_root: The identified package root directory.
|
|
484
|
+
parts: Module path split into components.
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
Path to potential module file.
|
|
488
|
+
"""
|
|
489
|
+
remaining_parts = parts[1:]
|
|
490
|
+
|
|
491
|
+
if not remaining_parts:
|
|
492
|
+
return package_root / INIT_FILE
|
|
493
|
+
|
|
494
|
+
return self._build_module_path(package_root, remaining_parts)
|
|
495
|
+
|
|
496
|
+
def _build_module_path(self, base_dir: Path, parts: list[str]) -> Path:
|
|
497
|
+
"""Build potential module file path from parts.
|
|
498
|
+
|
|
499
|
+
Args:
|
|
500
|
+
base_dir: Base directory to search from.
|
|
501
|
+
parts: Module path components.
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
Path to potential module file.
|
|
505
|
+
"""
|
|
506
|
+
if len(parts) == 1:
|
|
507
|
+
module_path = base_dir / f"{parts[0]}.py"
|
|
508
|
+
if module_path.exists():
|
|
509
|
+
return module_path
|
|
510
|
+
|
|
511
|
+
if len(parts) > 1:
|
|
512
|
+
module_path = base_dir / "/".join(parts[:-1]) / f"{parts[-1]}.py"
|
|
513
|
+
if module_path.exists():
|
|
514
|
+
return module_path
|
|
515
|
+
|
|
516
|
+
return base_dir / "/".join(parts) / INIT_FILE
|
|
171
517
|
|
|
172
518
|
def format_external_imports(self, external_imports: list) -> list[str]:
|
|
173
519
|
"""Format external imports as code strings.
|
|
@@ -421,7 +767,7 @@ class ImportResolver:
|
|
|
421
767
|
The node if external, None if local.
|
|
422
768
|
"""
|
|
423
769
|
if isinstance(node, ast.ImportFrom):
|
|
424
|
-
if node.
|
|
770
|
+
if node.level > 0:
|
|
425
771
|
return None
|
|
426
772
|
temp_resolver = ImportResolver(tool_dir)
|
|
427
773
|
if temp_resolver.is_local_import(node):
|
|
@@ -81,18 +81,18 @@ glaip_sdk/cli/slash/agent_session.py,sha256=tuVOme-NbEyr6rwJvsBEKZYWQmsaRf4piJeR
|
|
|
81
81
|
glaip_sdk/cli/slash/prompt.py,sha256=q4f1c2zr7ZMUeO6AgOBF2Nz4qgMOXrVPt6WzPRQMbAM,8501
|
|
82
82
|
glaip_sdk/cli/slash/remote_runs_controller.py,sha256=iLl4a-mu9QU7dcedgEILewPtDIVtFUJkbKGtcx1F66U,21445
|
|
83
83
|
glaip_sdk/cli/slash/session.py,sha256=lWK4iCb4HGnHZf251kP_1iyH8J0BaTlwSs3XJWfYOuI,76146
|
|
84
|
-
glaip_sdk/cli/slash/tui/__init__.py,sha256=
|
|
84
|
+
glaip_sdk/cli/slash/tui/__init__.py,sha256=mhX89VfkWFLCvSnu7ffIssbBgtqeTAJ3s90uf11HoFs,1107
|
|
85
85
|
glaip_sdk/cli/slash/tui/accounts.tcss,sha256=5iVZZfS10CTJhnoZ9AFJejtj8nyQXH9xV7u9k8jSkGE,2411
|
|
86
|
-
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=
|
|
86
|
+
glaip_sdk/cli/slash/tui/accounts_app.py,sha256=TXAs2VmXvBeK0r09InL1H5liean789r4iTii9kRLm7Q,75578
|
|
87
87
|
glaip_sdk/cli/slash/tui/background_tasks.py,sha256=SAe1mV2vXB3mJcSGhelU950vf8Lifjhws9iomyIVFKw,2422
|
|
88
88
|
glaip_sdk/cli/slash/tui/clipboard.py,sha256=Rb1n6nYsjTgMfSMTVo4HisW8ZM3na2REtd3OHEy-Lz0,11255
|
|
89
89
|
glaip_sdk/cli/slash/tui/context.py,sha256=mzI4TDXnfZd42osACp5uo10d10y1_A0z6IxRK1KVoVk,3320
|
|
90
90
|
glaip_sdk/cli/slash/tui/indicators.py,sha256=jV3fFvEVWQ0inWJJ-B1fMsdkF0Uq2zwX3xcl0YWPHSE,11768
|
|
91
91
|
glaip_sdk/cli/slash/tui/keybind_registry.py,sha256=_rK05BxTxNudYc4iJ9gDxpgeUkjDAq8rarIT-9A-jyM,6739
|
|
92
92
|
glaip_sdk/cli/slash/tui/loading.py,sha256=Ku7HyQ_h-r2dJQ5aIEaCOi5PUu5gSsYle8oiKHIxfKI,2336
|
|
93
|
-
glaip_sdk/cli/slash/tui/remote_runs_app.py,sha256=
|
|
93
|
+
glaip_sdk/cli/slash/tui/remote_runs_app.py,sha256=Rm_Fo8WNyTHWNj2aGwTnYsV-QVUCCSbT380D8Cio3uM,40618
|
|
94
94
|
glaip_sdk/cli/slash/tui/terminal.py,sha256=ZAC3sB17TGpl-GFeRVm_nI8DQTN3pyti3ynlZ41wT_A,12323
|
|
95
|
-
glaip_sdk/cli/slash/tui/toast.py,sha256=
|
|
95
|
+
glaip_sdk/cli/slash/tui/toast.py,sha256=UmP0_UAQcp0mOdX1iIS8W3gGDb14Z3kYGmbN_NvYDls,13494
|
|
96
96
|
glaip_sdk/cli/slash/tui/layouts/__init__.py,sha256=KT77pZHa7Wz84QlHYT2mfhQ_AXUA-T0eHv_HtAvc1ac,473
|
|
97
97
|
glaip_sdk/cli/slash/tui/layouts/harlequin.py,sha256=JOsaK18jTojzZ-Py-87foxfijuRDWwi8LIWmqM6qS0k,5644
|
|
98
98
|
glaip_sdk/cli/slash/tui/theme/__init__.py,sha256=rtM2ik83YNCRcI1qh_Sf3rnxco2OvCNNT3NbHY6cLvw,432
|
|
@@ -179,7 +179,7 @@ glaip_sdk/utils/display.py,sha256=zu3SYqxj9hPyEN8G1vIXv_yXBkV8jLLCXEg2rs8NlzM,44
|
|
|
179
179
|
glaip_sdk/utils/export.py,sha256=1NxxE3wGsA1auzecG5oJw5ELB4VmPljoeIkGhrGOh1I,5006
|
|
180
180
|
glaip_sdk/utils/general.py,sha256=3HSVIopUsIymPaim-kP2lqLX75TkkdIVLe6g3UKabZ0,1507
|
|
181
181
|
glaip_sdk/utils/import_export.py,sha256=RCvoydm_6_L7_J1igcE6IYDunqgS5mQUbWT4VGrytMw,5510
|
|
182
|
-
glaip_sdk/utils/import_resolver.py,sha256=
|
|
182
|
+
glaip_sdk/utils/import_resolver.py,sha256=82kSV9eAlzBiwPI8rkeXVrNjlpXa5q9fvRoJxE6b-6U,27706
|
|
183
183
|
glaip_sdk/utils/instructions.py,sha256=MTk93lsq3I8aRnvnRMSXXNMzcpnaIM_Pm3Aiiiq3GBc,2997
|
|
184
184
|
glaip_sdk/utils/resource_refs.py,sha256=vF34kyAtFBLnaKnQVrsr2st1JiSxVbIZ4yq0DelJvCI,5966
|
|
185
185
|
glaip_sdk/utils/run_renderer.py,sha256=d_VMI6LbvHPUUeRmGqh5wK_lHqDEIAcym2iqpbtDad0,1365
|
|
@@ -220,8 +220,8 @@ glaip_sdk/utils/rendering/steps/format.py,sha256=Chnq7OBaj8XMeBntSBxrX5zSmrYeGcO
|
|
|
220
220
|
glaip_sdk/utils/rendering/steps/manager.py,sha256=BiBmTeQMQhjRMykgICXsXNYh1hGsss-fH9BIGVMWFi0,13194
|
|
221
221
|
glaip_sdk/utils/rendering/viewer/__init__.py,sha256=XrxmE2cMAozqrzo1jtDFm8HqNtvDcYi2mAhXLXn5CjI,457
|
|
222
222
|
glaip_sdk/utils/rendering/viewer/presenter.py,sha256=mlLMTjnyeyPVtsyrAbz1BJu9lFGQSlS-voZ-_Cuugv0,5725
|
|
223
|
-
glaip_sdk-0.7.
|
|
224
|
-
glaip_sdk-0.7.
|
|
225
|
-
glaip_sdk-0.7.
|
|
226
|
-
glaip_sdk-0.7.
|
|
227
|
-
glaip_sdk-0.7.
|
|
223
|
+
glaip_sdk-0.7.28.dist-info/METADATA,sha256=NLsPQhywv5Gba0deG2zIQX3FRZc0p_LZoDAp82Dawzg,8686
|
|
224
|
+
glaip_sdk-0.7.28.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
225
|
+
glaip_sdk-0.7.28.dist-info/entry_points.txt,sha256=NkhO6FfgX9Zrjn63GuKphf-dLw7KNJvucAcXc7P3aMk,54
|
|
226
|
+
glaip_sdk-0.7.28.dist-info/top_level.txt,sha256=td7yXttiYX2s94-4wFhv-5KdT0rSZ-pnJRSire341hw,10
|
|
227
|
+
glaip_sdk-0.7.28.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|