ai-pack-cli 0.1.1__tar.gz → 0.1.2__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.1 → ai_pack_cli-0.1.2}/PKG-INFO +1 -1
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack.py +21 -12
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/PKG-INFO +1 -1
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/SOURCES.txt +5 -1
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/setup.py +1 -1
- ai_pack_cli-0.1.2/tests/test_binary.py +33 -0
- ai_pack_cli-0.1.2/tests/test_gitignore.py +38 -0
- ai_pack_cli-0.1.2/tests/test_skeleton_braces.py +139 -0
- ai_pack_cli-0.1.2/tests/test_skeleton_python.py +63 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/README.md +0 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/dependency_links.txt +0 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/entry_points.txt +0 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/requires.txt +0 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/ai_pack_cli.egg-info/top_level.txt +0 -0
- {ai_pack_cli-0.1.1 → ai_pack_cli-0.1.2}/setup.cfg +0 -0
|
@@ -231,10 +231,16 @@ class SkeletonExtractor:
|
|
|
231
231
|
indent = len(line) - len(line.lstrip())
|
|
232
232
|
sig_lines = []
|
|
233
233
|
|
|
234
|
-
# Consume multi-line signatures
|
|
234
|
+
# Consume multi-line signatures by tracking open parentheses/brackets
|
|
235
|
+
open_braces = 0
|
|
235
236
|
while i < n:
|
|
236
|
-
|
|
237
|
-
|
|
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:
|
|
238
244
|
break
|
|
239
245
|
i += 1
|
|
240
246
|
|
|
@@ -360,15 +366,18 @@ class SkeletonExtractor:
|
|
|
360
366
|
is_func = True
|
|
361
367
|
elif any(x in prefix_str for x in ('fn ', 'func ')):
|
|
362
368
|
is_func = True
|
|
363
|
-
elif
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
|
372
381
|
|
|
373
382
|
if is_func:
|
|
374
383
|
skip_until_brace_depth = brace_depth
|
|
@@ -6,4 +6,8 @@ ai_pack_cli.egg-info/SOURCES.txt
|
|
|
6
6
|
ai_pack_cli.egg-info/dependency_links.txt
|
|
7
7
|
ai_pack_cli.egg-info/entry_points.txt
|
|
8
8
|
ai_pack_cli.egg-info/requires.txt
|
|
9
|
-
ai_pack_cli.egg-info/top_level.txt
|
|
9
|
+
ai_pack_cli.egg-info/top_level.txt
|
|
10
|
+
tests/test_binary.py
|
|
11
|
+
tests/test_gitignore.py
|
|
12
|
+
tests/test_skeleton_braces.py
|
|
13
|
+
tests/test_skeleton_python.py
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
from ai_pack import is_binary
|
|
6
|
+
|
|
7
|
+
class TestBinaryDetection(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.test_dir = tempfile.mkdtemp()
|
|
10
|
+
|
|
11
|
+
def tearDown(self):
|
|
12
|
+
shutil.rmtree(self.test_dir)
|
|
13
|
+
|
|
14
|
+
def test_text_file_is_not_binary(self):
|
|
15
|
+
file_path = os.path.join(self.test_dir, "text.txt")
|
|
16
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
17
|
+
f.write("Hello, this is a plain text file with normal characters.")
|
|
18
|
+
self.assertFalse(is_binary(file_path))
|
|
19
|
+
|
|
20
|
+
def test_empty_file_is_not_binary(self):
|
|
21
|
+
file_path = os.path.join(self.test_dir, "empty.txt")
|
|
22
|
+
with open(file_path, "w") as f:
|
|
23
|
+
f.write("")
|
|
24
|
+
self.assertFalse(is_binary(file_path))
|
|
25
|
+
|
|
26
|
+
def test_binary_file_with_null_byte_is_binary(self):
|
|
27
|
+
file_path = os.path.join(self.test_dir, "binary.bin")
|
|
28
|
+
with open(file_path, "wb") as f:
|
|
29
|
+
f.write(b"PNG\x00\x00\x00\r\nIHDR")
|
|
30
|
+
self.assertTrue(is_binary(file_path))
|
|
31
|
+
|
|
32
|
+
def test_missing_file_is_treated_as_binary(self):
|
|
33
|
+
self.assertTrue(is_binary(os.path.join(self.test_dir, "does_not_exist.bin")))
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
from ai_pack import GitignoreMatcher
|
|
6
|
+
|
|
7
|
+
class TestGitignoreMatcher(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.test_dir = tempfile.mkdtemp()
|
|
10
|
+
|
|
11
|
+
def tearDown(self):
|
|
12
|
+
shutil.rmtree(self.test_dir)
|
|
13
|
+
|
|
14
|
+
def test_standard_ignores(self):
|
|
15
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
16
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "node_modules", "lodash", "index.js")))
|
|
17
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "src", "__pycache__", "utils.cpython-38.pyc")))
|
|
18
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, ".git", "config")))
|
|
19
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "venv", "lib", "python3.8", "site-packages")))
|
|
20
|
+
|
|
21
|
+
def test_matching_wildcard_patterns(self):
|
|
22
|
+
with open(os.path.join(self.test_dir, ".gitignore"), "w") as f:
|
|
23
|
+
f.write("*.log\ntemp*\n")
|
|
24
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
25
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "error.log")))
|
|
26
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "sub", "output.log")))
|
|
27
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "temp_data.csv")))
|
|
28
|
+
self.assertFalse(matcher.is_ignored(os.path.join(self.test_dir, "item_temp.txt")))
|
|
29
|
+
|
|
30
|
+
def test_absolute_vs_relative_gitignore_paths(self):
|
|
31
|
+
with open(os.path.join(self.test_dir, ".gitignore"), "w") as f:
|
|
32
|
+
f.write("/root_only.txt\nsub/ignored.txt\n")
|
|
33
|
+
matcher = GitignoreMatcher(self.test_dir)
|
|
34
|
+
|
|
35
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "root_only.txt")))
|
|
36
|
+
self.assertFalse(matcher.is_ignored(os.path.join(self.test_dir, "nested", "root_only.txt")))
|
|
37
|
+
|
|
38
|
+
self.assertTrue(matcher.is_ignored(os.path.join(self.test_dir, "sub", "ignored.txt")))
|
|
@@ -0,0 +1,139 @@
|
|
|
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())
|
|
@@ -0,0 +1,63 @@
|
|
|
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
|