ai-pack-cli 0.1.2__tar.gz → 0.1.7__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: ai-pack-cli
3
- Version: 0.1.2
3
+ Version: 0.1.7
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 and all brace-delimited languages (JS, TS, Go, Rust, C++, Java, Swift, etc.)*
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
  [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](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 and all brace-delimited languages (JS, TS, Go, Rust, C++, Java, Swift, etc.)*
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,31 @@ class SkeletonExtractor:
195
195
 
196
196
  @staticmethod
197
197
  def get_skeleton(file_path: str, content: str) -> str:
198
- ext = os.path.splitext(file_path)[1].lower()
199
- if ext == ".py":
200
- return SkeletonExtractor.extract_python_skeleton(content)
201
- elif ext in (".js", ".ts", ".jsx", ".tsx", ".go", ".rs", ".cpp", ".c", ".h", ".hpp", ".java", ".cs", ".php", ".swift"):
202
- return SkeletonExtractor.extract_brace_skeleton(content)
203
- else:
204
- # Fallback: keep full content for configuration, text, markdown, or unrecognized files
205
- return content
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
206
209
 
207
- @staticmethod
208
- def extract_python_skeleton(content: str) -> str:
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
210
 
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)
211
+ def copy_via_osc52(payload: str) -> bool:
212
+ """Attempts to copy text to the client system clipboard using OSC 52 escape sequence."""
213
+ try:
214
+ import base64
215
+ # Base64 encode the payload
216
+ b64_data = base64.b64encode(payload.encode("utf-8")).decode("ascii")
217
+ # Write to stdout using the OSC 52 escape sequence: \x1b]52;c;[base64]\x07
218
+ sys.stdout.write(f"\x1b]52;c;{b64_data}\x07")
219
+ sys.stdout.flush()
220
+ return True
221
+ except Exception:
222
+ return False
403
223
 
404
224
 
405
225
  def walk_dir(directory: str, git_files: Set[str] = None, gitignore: GitignoreMatcher = None) -> List[str]:
@@ -656,12 +476,20 @@ def main():
656
476
  print(f"❌ Error saving to file {args.output}: {e}", file=sys.stderr)
657
477
  sys.exit(1)
658
478
  else:
479
+ copied = False
659
480
  try:
660
481
  import pyperclip
661
482
  pyperclip.copy(payload)
483
+ copied = True
662
484
  print("🚀 Successfully copied to clipboard!")
663
- except Exception as e:
664
- print(f"❌ Error copying to clipboard: {e}", file=sys.stderr)
485
+ except Exception:
486
+ # Fallback to OSC 52 (works seamlessly over SSH / remote terminal sessions)
487
+ if copy_via_osc52(payload):
488
+ copied = True
489
+ print("🚀 Successfully copied to clipboard (via OSC 52 remote sequence)!")
490
+
491
+ if not copied:
492
+ print("❌ Error copying to clipboard: Pyperclip could not find a copy/paste mechanism.", file=sys.stderr)
665
493
  print("💡 In headless Linux systems, you must install 'xclip' or 'xsel', or direct output to a file using the '-o' flag.", file=sys.stderr)
666
494
  sys.exit(1)
667
495
 
@@ -1,3 +1,16 @@
1
+ Metadata-Version: 2.1
2
+ Name: ai-pack-cli
3
+ Version: 0.1.7
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
  [![Python Version](https://img.shields.io/badge/python-3.8%2B-blue.svg)](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 and all brace-delimited languages (JS, TS, Go, Rust, C++, Java, Swift, etc.)*
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
 
@@ -9,5 +9,4 @@ ai_pack_cli.egg-info/requires.txt
9
9
  ai_pack_cli.egg-info/top_level.txt
10
10
  tests/test_binary.py
11
11
  tests/test_gitignore.py
12
- tests/test_skeleton_braces.py
13
- tests/test_skeleton_python.py
12
+ tests/test_skeleton.py
@@ -0,0 +1,8 @@
1
+ pyperclip>=1.8.2
2
+ questionary>=1.10.0
3
+
4
+ [:python_version == "3.9"]
5
+ ast-grep-py<=0.39.7
6
+
7
+ [:python_version >= "3.9"]
8
+ codesigs>=0.0.1
@@ -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.2",
9
+ version="0.1.7",
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
+ # Clear sys.modules of any real codesigs import to trigger ImportError
22
+ original_modules = sys.modules.copy()
23
+ if 'codesigs' in sys.modules:
24
+ del sys.modules['codesigs']
25
+ try:
26
+ with patch.dict(sys.modules, {'codesigs': None}):
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,2 +0,0 @@
1
- pyperclip>=1.8.2
2
- questionary>=1.10.0
@@ -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