ai-pack-cli 0.1.2__tar.gz → 0.1.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {ai_pack_cli-0.1.2/ai_pack_cli.egg-info → ai_pack_cli-0.1.6}/PKG-INFO +17 -2
- ai_pack_cli-0.1.2/PKG-INFO → ai_pack_cli-0.1.6/README.md +14 -12
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/ai_pack.py +11 -205
- ai_pack_cli-0.1.2/README.md → ai_pack_cli-0.1.6/ai_pack_cli.egg-info/PKG-INFO +27 -1
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/ai_pack_cli.egg-info/SOURCES.txt +1 -2
- ai_pack_cli-0.1.6/ai_pack_cli.egg-info/requires.txt +8 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/setup.py +3 -1
- ai_pack_cli-0.1.6/tests/test_skeleton.py +41 -0
- ai_pack_cli-0.1.2/ai_pack_cli.egg-info/requires.txt +0 -2
- ai_pack_cli-0.1.2/tests/test_skeleton_braces.py +0 -139
- ai_pack_cli-0.1.2/tests/test_skeleton_python.py +0 -63
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/ai_pack_cli.egg-info/dependency_links.txt +0 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/ai_pack_cli.egg-info/entry_points.txt +0 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/ai_pack_cli.egg-info/top_level.txt +0 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/setup.cfg +0 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/tests/test_binary.py +0 -0
- {ai_pack_cli-0.1.2 → ai_pack_cli-0.1.6}/tests/test_gitignore.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: ai-pack-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Home-page: https://github.com/iamraydoan/ai-pack
|
|
5
5
|
Classifier: Programming Language :: Python :: 3
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -8,6 +8,8 @@ Classifier: Operating System :: OS Independent
|
|
|
8
8
|
Description-Content-Type: text/markdown
|
|
9
9
|
Requires-Dist: pyperclip>=1.8.2
|
|
10
10
|
Requires-Dist: questionary>=1.10.0
|
|
11
|
+
Requires-Dist: codesigs>=0.0.1; python_version >= "3.9"
|
|
12
|
+
Requires-Dist: ast-grep-py<=0.39.7; python_version == "3.9"
|
|
11
13
|
|
|
12
14
|
# 📦 ai-pack
|
|
13
15
|
|
|
@@ -72,6 +74,14 @@ curl -o ~/.local/bin/aip https://raw.githubusercontent.com/iamraydoan/ai-pack/ma
|
|
|
72
74
|
```
|
|
73
75
|
*(Note: If you run it standalone, you will need to manually run `pip3 install pyperclip questionary` to enable all optional interactive and clipboard features).*
|
|
74
76
|
|
|
77
|
+
> [!TIP]
|
|
78
|
+
> **Optional backend for Skeleton Mode (`--skeleton`)**:
|
|
79
|
+
> If you plan to use skeleton extraction for non-Python languages (such as JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin), you must install `ast-grep` globally:
|
|
80
|
+
> ```bash
|
|
81
|
+
> npm install -g @ast-grep/cli
|
|
82
|
+
> # or: cargo install ast-grep
|
|
83
|
+
> ```
|
|
84
|
+
|
|
75
85
|
|
|
76
86
|
### Usage Examples
|
|
77
87
|
|
|
@@ -123,7 +133,12 @@ def fibonacci(n):
|
|
|
123
133
|
...
|
|
124
134
|
```
|
|
125
135
|
|
|
126
|
-
*Supports Python
|
|
136
|
+
*Supports Python, JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin via codesigs and ast-grep.*
|
|
137
|
+
|
|
138
|
+
> [!IMPORTANT]
|
|
139
|
+
> **Skeleton Mode Dependencies**:
|
|
140
|
+
> * **Python >= 3.9**: Required to run the `--skeleton` feature.
|
|
141
|
+
> * **ast-grep**: For non-Python languages, the `ast-grep` command-line tool must be installed globally (e.g. via npm: `npm install -g @ast-grep/cli` or cargo: `cargo install ast-grep`).
|
|
127
142
|
|
|
128
143
|
---
|
|
129
144
|
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: ai-pack-cli
|
|
3
|
-
Version: 0.1.2
|
|
4
|
-
Home-page: https://github.com/iamraydoan/ai-pack
|
|
5
|
-
Classifier: Programming Language :: Python :: 3
|
|
6
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
-
Classifier: Operating System :: OS Independent
|
|
8
|
-
Description-Content-Type: text/markdown
|
|
9
|
-
Requires-Dist: pyperclip>=1.8.2
|
|
10
|
-
Requires-Dist: questionary>=1.10.0
|
|
11
|
-
|
|
12
1
|
# 📦 ai-pack
|
|
13
2
|
|
|
14
3
|
[](https://www.python.org/)
|
|
@@ -72,6 +61,14 @@ curl -o ~/.local/bin/aip https://raw.githubusercontent.com/iamraydoan/ai-pack/ma
|
|
|
72
61
|
```
|
|
73
62
|
*(Note: If you run it standalone, you will need to manually run `pip3 install pyperclip questionary` to enable all optional interactive and clipboard features).*
|
|
74
63
|
|
|
64
|
+
> [!TIP]
|
|
65
|
+
> **Optional backend for Skeleton Mode (`--skeleton`)**:
|
|
66
|
+
> If you plan to use skeleton extraction for non-Python languages (such as JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin), you must install `ast-grep` globally:
|
|
67
|
+
> ```bash
|
|
68
|
+
> npm install -g @ast-grep/cli
|
|
69
|
+
> # or: cargo install ast-grep
|
|
70
|
+
> ```
|
|
71
|
+
|
|
75
72
|
|
|
76
73
|
### Usage Examples
|
|
77
74
|
|
|
@@ -123,7 +120,12 @@ def fibonacci(n):
|
|
|
123
120
|
...
|
|
124
121
|
```
|
|
125
122
|
|
|
126
|
-
*Supports Python
|
|
123
|
+
*Supports Python, JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin via codesigs and ast-grep.*
|
|
124
|
+
|
|
125
|
+
> [!IMPORTANT]
|
|
126
|
+
> **Skeleton Mode Dependencies**:
|
|
127
|
+
> * **Python >= 3.9**: Required to run the `--skeleton` feature.
|
|
128
|
+
> * **ast-grep**: For non-Python languages, the `ast-grep` command-line tool must be installed globally (e.g. via npm: `npm install -g @ast-grep/cli` or cargo: `cargo install ast-grep`).
|
|
127
129
|
|
|
128
130
|
---
|
|
129
131
|
|
|
@@ -195,211 +195,17 @@ class SkeletonExtractor:
|
|
|
195
195
|
|
|
196
196
|
@staticmethod
|
|
197
197
|
def get_skeleton(file_path: str, content: str) -> str:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
lines = content.splitlines()
|
|
210
|
-
skeleton_lines = []
|
|
211
|
-
i = 0
|
|
212
|
-
n = len(lines)
|
|
213
|
-
decorator_buffer = []
|
|
214
|
-
|
|
215
|
-
while i < n:
|
|
216
|
-
line = lines[i]
|
|
217
|
-
stripped = line.strip()
|
|
218
|
-
|
|
219
|
-
# Keep track of decorators
|
|
220
|
-
if stripped.startswith("@"):
|
|
221
|
-
decorator_buffer.append(line)
|
|
222
|
-
i += 1
|
|
223
|
-
continue
|
|
224
|
-
|
|
225
|
-
# If function signature
|
|
226
|
-
if stripped.startswith("def ") or stripped.startswith("async def "):
|
|
227
|
-
if decorator_buffer:
|
|
228
|
-
skeleton_lines.extend(decorator_buffer)
|
|
229
|
-
decorator_buffer = []
|
|
230
|
-
|
|
231
|
-
indent = len(line) - len(line.lstrip())
|
|
232
|
-
sig_lines = []
|
|
233
|
-
|
|
234
|
-
# Consume multi-line signatures by tracking open parentheses/brackets
|
|
235
|
-
open_braces = 0
|
|
236
|
-
while i < n:
|
|
237
|
-
line_to_add = lines[i]
|
|
238
|
-
sig_lines.append(line_to_add)
|
|
239
|
-
open_braces += line_to_add.count('(') - line_to_add.count(')')
|
|
240
|
-
open_braces += line_to_add.count('[') - line_to_add.count(']')
|
|
241
|
-
open_braces += line_to_add.count('{') - line_to_add.count('}')
|
|
242
|
-
|
|
243
|
-
if open_braces <= 0 and ":" in line_to_add:
|
|
244
|
-
break
|
|
245
|
-
i += 1
|
|
246
|
-
|
|
247
|
-
skeleton_lines.extend(sig_lines)
|
|
248
|
-
|
|
249
|
-
# Skip the function body (all lines indented further than indent)
|
|
250
|
-
i += 1
|
|
251
|
-
body_skipped = False
|
|
252
|
-
while i < n:
|
|
253
|
-
next_line = lines[i]
|
|
254
|
-
next_stripped = next_line.strip()
|
|
255
|
-
if not next_stripped:
|
|
256
|
-
i += 1
|
|
257
|
-
continue
|
|
258
|
-
next_indent = len(next_line) - len(next_line.lstrip())
|
|
259
|
-
if next_indent <= indent:
|
|
260
|
-
break
|
|
261
|
-
body_skipped = True
|
|
262
|
-
i += 1
|
|
263
|
-
|
|
264
|
-
if body_skipped:
|
|
265
|
-
skeleton_lines.append(" " * (indent + 4) + "...")
|
|
266
|
-
decorator_buffer = []
|
|
267
|
-
else:
|
|
268
|
-
if decorator_buffer:
|
|
269
|
-
skeleton_lines.extend(decorator_buffer)
|
|
270
|
-
decorator_buffer = []
|
|
271
|
-
skeleton_lines.append(line)
|
|
272
|
-
i += 1
|
|
273
|
-
|
|
274
|
-
return "\n".join(skeleton_lines)
|
|
275
|
-
|
|
276
|
-
@staticmethod
|
|
277
|
-
def extract_brace_skeleton(content: str) -> str:
|
|
278
|
-
"""Parses brace-delimited languages to extract function definitions and omit bodies."""
|
|
279
|
-
result = []
|
|
280
|
-
i = 0
|
|
281
|
-
n = len(content)
|
|
282
|
-
|
|
283
|
-
in_string = None
|
|
284
|
-
in_comment = None
|
|
285
|
-
brace_depth = 0
|
|
286
|
-
skip_until_brace_depth = None
|
|
287
|
-
prefix_buffer = []
|
|
288
|
-
|
|
289
|
-
while i < n:
|
|
290
|
-
c = content[i]
|
|
291
|
-
|
|
292
|
-
# --- 1. Handle Comments ---
|
|
293
|
-
if in_comment == 'single':
|
|
294
|
-
if c == '\n':
|
|
295
|
-
in_comment = None
|
|
296
|
-
if skip_until_brace_depth is None:
|
|
297
|
-
result.append(c)
|
|
298
|
-
i += 1
|
|
299
|
-
continue
|
|
300
|
-
elif in_comment == 'multi':
|
|
301
|
-
if c == '/' and i > 0 and content[i-1] == '*':
|
|
302
|
-
in_comment = None
|
|
303
|
-
i += 1
|
|
304
|
-
continue
|
|
305
|
-
|
|
306
|
-
# --- 2. Handle Strings ---
|
|
307
|
-
if in_string:
|
|
308
|
-
if c == '\\' and i + 1 < n:
|
|
309
|
-
escaped_char = content[i:i+2]
|
|
310
|
-
if skip_until_brace_depth is None:
|
|
311
|
-
result.append(escaped_char)
|
|
312
|
-
prefix_buffer.append(escaped_char)
|
|
313
|
-
i += 2
|
|
314
|
-
continue
|
|
315
|
-
if skip_until_brace_depth is None:
|
|
316
|
-
result.append(c)
|
|
317
|
-
prefix_buffer.append(c)
|
|
318
|
-
if c == in_string:
|
|
319
|
-
in_string = None
|
|
320
|
-
i += 1
|
|
321
|
-
continue
|
|
322
|
-
|
|
323
|
-
# --- 3. Detect Comment Start (when not in string/comment) ---
|
|
324
|
-
if c == '/' and i + 1 < n:
|
|
325
|
-
next_c = content[i+1]
|
|
326
|
-
if next_c == '/':
|
|
327
|
-
in_comment = 'single'
|
|
328
|
-
i += 2
|
|
329
|
-
continue
|
|
330
|
-
elif next_c == '*':
|
|
331
|
-
in_comment = 'multi'
|
|
332
|
-
i += 2
|
|
333
|
-
continue
|
|
334
|
-
|
|
335
|
-
# --- 4. Detect String Start (when not in string/comment) ---
|
|
336
|
-
if c in ('"', "'", "`"):
|
|
337
|
-
in_string = c
|
|
338
|
-
if skip_until_brace_depth is None:
|
|
339
|
-
result.append(c)
|
|
340
|
-
prefix_buffer.append(c)
|
|
341
|
-
i += 1
|
|
342
|
-
continue
|
|
343
|
-
|
|
344
|
-
# --- 5. Brace & Skipping Logic ---
|
|
345
|
-
if skip_until_brace_depth is not None:
|
|
346
|
-
if c == '{':
|
|
347
|
-
brace_depth += 1
|
|
348
|
-
elif c == '}':
|
|
349
|
-
brace_depth -= 1
|
|
350
|
-
if brace_depth == skip_until_brace_depth:
|
|
351
|
-
result.append(' { /* ... */ }')
|
|
352
|
-
skip_until_brace_depth = None
|
|
353
|
-
i += 1
|
|
354
|
-
continue
|
|
355
|
-
|
|
356
|
-
# Normal code processing
|
|
357
|
-
if c == '{':
|
|
358
|
-
prefix_str = "".join(prefix_buffer).strip()
|
|
359
|
-
is_func = False
|
|
360
|
-
|
|
361
|
-
if '=>' in prefix_str:
|
|
362
|
-
is_func = True
|
|
363
|
-
elif 'function' in prefix_str:
|
|
364
|
-
is_func = True
|
|
365
|
-
elif 'constructor' in prefix_str:
|
|
366
|
-
is_func = True
|
|
367
|
-
elif any(x in prefix_str for x in ('fn ', 'func ')):
|
|
368
|
-
is_func = True
|
|
369
|
-
elif '(' in prefix_str and ')' in prefix_str:
|
|
370
|
-
first_paren = prefix_str.find('(')
|
|
371
|
-
name_part = prefix_str[:first_paren].strip()
|
|
372
|
-
words = name_part.split()
|
|
373
|
-
if words:
|
|
374
|
-
clean_words = [w for w in words if w not in (
|
|
375
|
-
'export', 'async', 'default', 'public', 'private', 'protected', 'static', 'readonly'
|
|
376
|
-
)]
|
|
377
|
-
if clean_words:
|
|
378
|
-
first_word = clean_words[0].strip()
|
|
379
|
-
if first_word not in ('if', 'for', 'while', 'switch', 'catch'):
|
|
380
|
-
is_func = True
|
|
381
|
-
|
|
382
|
-
if is_func:
|
|
383
|
-
skip_until_brace_depth = brace_depth
|
|
384
|
-
brace_depth += 1
|
|
385
|
-
prefix_buffer = []
|
|
386
|
-
else:
|
|
387
|
-
brace_depth += 1
|
|
388
|
-
result.append(c)
|
|
389
|
-
prefix_buffer = []
|
|
390
|
-
elif c == '}':
|
|
391
|
-
brace_depth -= 1
|
|
392
|
-
result.append(c)
|
|
393
|
-
prefix_buffer = []
|
|
394
|
-
else:
|
|
395
|
-
result.append(c)
|
|
396
|
-
if c == '\n':
|
|
397
|
-
prefix_buffer = []
|
|
398
|
-
else:
|
|
399
|
-
prefix_buffer.append(c)
|
|
400
|
-
i += 1
|
|
401
|
-
|
|
402
|
-
return "".join(result)
|
|
198
|
+
try:
|
|
199
|
+
from codesigs import file_sigs
|
|
200
|
+
sigs = file_sigs(file_path)
|
|
201
|
+
if sigs is not None:
|
|
202
|
+
return "\n".join(sigs)
|
|
203
|
+
except ImportError:
|
|
204
|
+
print("⚠️ Error: 'codesigs' package is required for --skeleton mode.", file=sys.stderr)
|
|
205
|
+
print("💡 Please install it using: pip install ai-pack-cli (requires Python >= 3.9)", file=sys.stderr)
|
|
206
|
+
except Exception as e:
|
|
207
|
+
print(f"⚠️ codesigs failed for {file_path}: {e}", file=sys.stderr)
|
|
208
|
+
return content
|
|
403
209
|
|
|
404
210
|
|
|
405
211
|
def walk_dir(directory: str, git_files: Set[str] = None, gitignore: GitignoreMatcher = None) -> List[str]:
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: ai-pack-cli
|
|
3
|
+
Version: 0.1.6
|
|
4
|
+
Home-page: https://github.com/iamraydoan/ai-pack
|
|
5
|
+
Classifier: Programming Language :: Python :: 3
|
|
6
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
7
|
+
Classifier: Operating System :: OS Independent
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: pyperclip>=1.8.2
|
|
10
|
+
Requires-Dist: questionary>=1.10.0
|
|
11
|
+
Requires-Dist: codesigs>=0.0.1; python_version >= "3.9"
|
|
12
|
+
Requires-Dist: ast-grep-py<=0.39.7; python_version == "3.9"
|
|
13
|
+
|
|
1
14
|
# 📦 ai-pack
|
|
2
15
|
|
|
3
16
|
[](https://www.python.org/)
|
|
@@ -61,6 +74,14 @@ curl -o ~/.local/bin/aip https://raw.githubusercontent.com/iamraydoan/ai-pack/ma
|
|
|
61
74
|
```
|
|
62
75
|
*(Note: If you run it standalone, you will need to manually run `pip3 install pyperclip questionary` to enable all optional interactive and clipboard features).*
|
|
63
76
|
|
|
77
|
+
> [!TIP]
|
|
78
|
+
> **Optional backend for Skeleton Mode (`--skeleton`)**:
|
|
79
|
+
> If you plan to use skeleton extraction for non-Python languages (such as JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin), you must install `ast-grep` globally:
|
|
80
|
+
> ```bash
|
|
81
|
+
> npm install -g @ast-grep/cli
|
|
82
|
+
> # or: cargo install ast-grep
|
|
83
|
+
> ```
|
|
84
|
+
|
|
64
85
|
|
|
65
86
|
### Usage Examples
|
|
66
87
|
|
|
@@ -112,7 +133,12 @@ def fibonacci(n):
|
|
|
112
133
|
...
|
|
113
134
|
```
|
|
114
135
|
|
|
115
|
-
*Supports Python
|
|
136
|
+
*Supports Python, JavaScript, TypeScript, Go, Rust, Java, C#, C++, PHP, Lua, CSS, Swift, and Kotlin via codesigs and ast-grep.*
|
|
137
|
+
|
|
138
|
+
> [!IMPORTANT]
|
|
139
|
+
> **Skeleton Mode Dependencies**:
|
|
140
|
+
> * **Python >= 3.9**: Required to run the `--skeleton` feature.
|
|
141
|
+
> * **ast-grep**: For non-Python languages, the `ast-grep` command-line tool must be installed globally (e.g. via npm: `npm install -g @ast-grep/cli` or cargo: `cargo install ast-grep`).
|
|
116
142
|
|
|
117
143
|
---
|
|
118
144
|
|
|
@@ -6,11 +6,13 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
6
6
|
|
|
7
7
|
setup(
|
|
8
8
|
name="ai-pack-cli",
|
|
9
|
-
version="0.1.
|
|
9
|
+
version="0.1.6",
|
|
10
10
|
py_modules=["ai_pack"],
|
|
11
11
|
install_requires=[
|
|
12
12
|
"pyperclip>=1.8.2",
|
|
13
13
|
"questionary>=1.10.0",
|
|
14
|
+
'codesigs>=0.0.1; python_version >= "3.9"',
|
|
15
|
+
'ast-grep-py<=0.39.7; python_version == "3.9"',
|
|
14
16
|
],
|
|
15
17
|
entry_points={
|
|
16
18
|
"console_scripts": [
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
from unittest.mock import patch, MagicMock
|
|
3
|
+
import sys
|
|
4
|
+
from io import StringIO
|
|
5
|
+
from ai_pack import SkeletonExtractor
|
|
6
|
+
|
|
7
|
+
class TestSkeletonExtractor(unittest.TestCase):
|
|
8
|
+
@patch('ai_pack.sys.stderr', new_callable=StringIO)
|
|
9
|
+
def test_get_skeleton_success(self, mock_stderr):
|
|
10
|
+
# Mock codesigs module in sys.modules
|
|
11
|
+
mock_codesigs = MagicMock()
|
|
12
|
+
mock_codesigs.file_sigs.return_value = ["def foo():", " ..."]
|
|
13
|
+
|
|
14
|
+
with patch.dict(sys.modules, {'codesigs': mock_codesigs}):
|
|
15
|
+
result = SkeletonExtractor.get_skeleton("test.py", "def foo():\n pass")
|
|
16
|
+
self.assertEqual(result, "def foo():\n ...")
|
|
17
|
+
mock_codesigs.file_sigs.assert_called_once_with("test.py")
|
|
18
|
+
|
|
19
|
+
@patch('ai_pack.sys.stderr', new_callable=StringIO)
|
|
20
|
+
def test_get_skeleton_import_error(self, mock_stderr):
|
|
21
|
+
with patch.dict(sys.modules, {'codesigs': None}):
|
|
22
|
+
# Clear sys.modules of any real codesigs import to trigger ImportError
|
|
23
|
+
original_modules = sys.modules.copy()
|
|
24
|
+
if 'codesigs' in sys.modules:
|
|
25
|
+
del sys.modules['codesigs']
|
|
26
|
+
try:
|
|
27
|
+
result = SkeletonExtractor.get_skeleton("test.py", "def foo():\n pass")
|
|
28
|
+
self.assertEqual(result, "def foo():\n pass")
|
|
29
|
+
self.assertIn("package is required", mock_stderr.getvalue())
|
|
30
|
+
finally:
|
|
31
|
+
sys.modules = original_modules
|
|
32
|
+
|
|
33
|
+
@patch('ai_pack.sys.stderr', new_callable=StringIO)
|
|
34
|
+
def test_get_skeleton_runtime_error(self, mock_stderr):
|
|
35
|
+
mock_codesigs = MagicMock()
|
|
36
|
+
mock_codesigs.file_sigs.side_effect = Exception("ast-grep not found")
|
|
37
|
+
|
|
38
|
+
with patch.dict(sys.modules, {'codesigs': mock_codesigs}):
|
|
39
|
+
result = SkeletonExtractor.get_skeleton("test.py", "def foo():\n pass")
|
|
40
|
+
self.assertEqual(result, "def foo():\n pass")
|
|
41
|
+
self.assertIn("codesigs failed", mock_stderr.getvalue())
|
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from ai_pack import SkeletonExtractor
|
|
3
|
-
|
|
4
|
-
class TestBraceSkeletonExtractor(unittest.TestCase):
|
|
5
|
-
def test_javascript_typescript(self):
|
|
6
|
-
js_code = """import { component } from 'framework';
|
|
7
|
-
const PORT = 8080;
|
|
8
|
-
|
|
9
|
-
export default class Widget extends Base {
|
|
10
|
-
constructor(config) {
|
|
11
|
-
super(config);
|
|
12
|
-
this.init();
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
render() {
|
|
16
|
-
return `<div>${this.config.title}</div>`;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const add = (a, b) => {
|
|
21
|
-
return a + b;
|
|
22
|
-
};
|
|
23
|
-
"""
|
|
24
|
-
expected = """import { component } from 'framework';
|
|
25
|
-
const PORT = 8080;
|
|
26
|
-
|
|
27
|
-
export default class Widget extends Base {
|
|
28
|
-
constructor(config) { /* ... */ }
|
|
29
|
-
|
|
30
|
-
render() { /* ... */ }
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
const add = (a, b) => { /* ... */ };"""
|
|
34
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(js_code).strip(), expected.strip())
|
|
35
|
-
|
|
36
|
-
def test_js_template_literals_with_braces(self):
|
|
37
|
-
js_code = """const render = (name) => {
|
|
38
|
-
return `Hello, ${name}! Your age is ${getAge({birth: 1990})}.`;
|
|
39
|
-
};
|
|
40
|
-
"""
|
|
41
|
-
expected = """const render = (name) => { /* ... */ };"""
|
|
42
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(js_code).strip(), expected.strip())
|
|
43
|
-
|
|
44
|
-
def test_golang_parser(self):
|
|
45
|
-
go_code = """package main
|
|
46
|
-
import "fmt"
|
|
47
|
-
|
|
48
|
-
type Server struct {
|
|
49
|
-
Port int
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
func NewServer(port int) *Server {
|
|
53
|
-
return &Server{Port: port}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
func (s *Server) Start() error {
|
|
57
|
-
fmt.Println("Starting...")
|
|
58
|
-
return nil
|
|
59
|
-
}
|
|
60
|
-
"""
|
|
61
|
-
expected = """package main
|
|
62
|
-
import "fmt"
|
|
63
|
-
|
|
64
|
-
type Server struct {
|
|
65
|
-
Port int
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
func NewServer(port int) *Server { /* ... */ }
|
|
69
|
-
|
|
70
|
-
func (s *Server) Start() error { /* ... */ }"""
|
|
71
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(go_code).strip(), expected.strip())
|
|
72
|
-
|
|
73
|
-
def test_rust_parser(self):
|
|
74
|
-
rust_code = """use std::io;
|
|
75
|
-
|
|
76
|
-
pub struct Robot {
|
|
77
|
-
name: String,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
impl Robot {
|
|
81
|
-
pub fn new(name: &str) -> Self {
|
|
82
|
-
Robot {
|
|
83
|
-
name: name.to_string(),
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
fn speak(&self) {
|
|
88
|
-
println!("Hello, my name is {}", self.name);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
"""
|
|
92
|
-
expected = """use std::io;
|
|
93
|
-
|
|
94
|
-
pub struct Robot {
|
|
95
|
-
name: String,
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
impl Robot {
|
|
99
|
-
pub fn new(name: &str) -> Self { /* ... */ }
|
|
100
|
-
|
|
101
|
-
fn speak(&self) { /* ... */ }
|
|
102
|
-
}"""
|
|
103
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(rust_code).strip(), expected.strip())
|
|
104
|
-
|
|
105
|
-
def test_cpp_java_parser(self):
|
|
106
|
-
java_code = """public class App {
|
|
107
|
-
public static void main(String[] args) {
|
|
108
|
-
System.out.println("Hello World!");
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
private int calc(int a) {
|
|
112
|
-
return a * 2;
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
"""
|
|
116
|
-
expected = """public class App {
|
|
117
|
-
public static void main(String[] args) { /* ... */ }
|
|
118
|
-
|
|
119
|
-
private int calc(int a) { /* ... */ }
|
|
120
|
-
}"""
|
|
121
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(java_code).strip(), expected.strip())
|
|
122
|
-
|
|
123
|
-
def test_typescript_return_types(self):
|
|
124
|
-
ts_code = """class Manager {
|
|
125
|
-
async getItems(req: Request): Promise<Item[]> {
|
|
126
|
-
return this.client.get();
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
add(a: number, b: number): number {
|
|
130
|
-
return a + b;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
"""
|
|
134
|
-
expected = """class Manager {
|
|
135
|
-
async getItems(req: Request): Promise<Item[]> { /* ... */ }
|
|
136
|
-
|
|
137
|
-
add(a: number, b: number): number { /* ... */ }
|
|
138
|
-
}"""
|
|
139
|
-
self.assertEqual(SkeletonExtractor.extract_brace_skeleton(ts_code).strip(), expected.strip())
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import unittest
|
|
2
|
-
from ai_pack import SkeletonExtractor
|
|
3
|
-
|
|
4
|
-
class TestPythonSkeletonExtractor(unittest.TestCase):
|
|
5
|
-
def test_basic_functions_and_classes(self):
|
|
6
|
-
code = """class User:
|
|
7
|
-
def __init__(self, name):
|
|
8
|
-
self.name = name
|
|
9
|
-
def greet(self):
|
|
10
|
-
print("Hello " + self.name)
|
|
11
|
-
"""
|
|
12
|
-
expected = """class User:
|
|
13
|
-
def __init__(self, name):
|
|
14
|
-
...
|
|
15
|
-
def greet(self):
|
|
16
|
-
..."""
|
|
17
|
-
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
18
|
-
|
|
19
|
-
def test_decorators_and_async(self):
|
|
20
|
-
code = """@property
|
|
21
|
-
@cache
|
|
22
|
-
def value(self):
|
|
23
|
-
return self._val
|
|
24
|
-
|
|
25
|
-
async def fetch_data(url):
|
|
26
|
-
res = await client.get(url)
|
|
27
|
-
return res.json()
|
|
28
|
-
"""
|
|
29
|
-
expected = """@property
|
|
30
|
-
@cache
|
|
31
|
-
def value(self):
|
|
32
|
-
...
|
|
33
|
-
async def fetch_data(url):
|
|
34
|
-
..."""
|
|
35
|
-
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
36
|
-
|
|
37
|
-
def test_multiline_signatures(self):
|
|
38
|
-
code = """def complex_function(
|
|
39
|
-
a: int,
|
|
40
|
-
b: str,
|
|
41
|
-
c: float = 1.0
|
|
42
|
-
) -> bool:
|
|
43
|
-
print(a, b, c)
|
|
44
|
-
return True
|
|
45
|
-
"""
|
|
46
|
-
expected = """def complex_function(
|
|
47
|
-
a: int,
|
|
48
|
-
b: str,
|
|
49
|
-
c: float = 1.0
|
|
50
|
-
) -> bool:
|
|
51
|
-
..."""
|
|
52
|
-
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
53
|
-
|
|
54
|
-
def test_single_line_and_comments(self):
|
|
55
|
-
code = """def simple_get(self): return self.x
|
|
56
|
-
|
|
57
|
-
def comment_sig(a, b): # basic add
|
|
58
|
-
return a + b
|
|
59
|
-
"""
|
|
60
|
-
expected = """def simple_get(self): return self.x
|
|
61
|
-
def comment_sig(a, b): # basic add
|
|
62
|
-
..."""
|
|
63
|
-
self.assertEqual(SkeletonExtractor.extract_python_skeleton(code).strip(), expected.strip())
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|