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.
@@ -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.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 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,17 @@ 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
206
-
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
-
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
  [![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.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,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