py-adtools 0.1.0__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.
Potentially problematic release.
This version of py-adtools might be problematic. Click here for more details.
- py_adtools-0.1.0/LICENSE +21 -0
- py_adtools-0.1.0/PKG-INFO +89 -0
- py_adtools-0.1.0/README.md +65 -0
- py_adtools-0.1.0/adtools/__init__.py +1 -0
- py_adtools-0.1.0/adtools/py_code.py +335 -0
- py_adtools-0.1.0/py_adtools.egg-info/PKG-INFO +89 -0
- py_adtools-0.1.0/py_adtools.egg-info/SOURCES.txt +9 -0
- py_adtools-0.1.0/py_adtools.egg-info/dependency_links.txt +1 -0
- py_adtools-0.1.0/py_adtools.egg-info/top_level.txt +1 -0
- py_adtools-0.1.0/setup.cfg +4 -0
- py_adtools-0.1.0/setup.py +24 -0
py_adtools-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Rui Zhang
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-adtools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Useful tools for parsing Python programs for algorithm design.
|
|
5
|
+
Home-page: https://github.com/RayZhhh/py-adtools
|
|
6
|
+
Author: Rui Zhang
|
|
7
|
+
Author-email: rzhang.cs@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: author-email
|
|
17
|
+
Dynamic: classifier
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: description-content-type
|
|
20
|
+
Dynamic: home-page
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# Useful tools for parsing Python programs for algorithm design
|
|
26
|
+
|
|
27
|
+
------
|
|
28
|
+
|
|
29
|
+
> This repo aims to help develop more powerful [Large Language Models for Algorithm Design (LLM4AD)](https://github.com/Optima-CityU/llm4ad) applications.
|
|
30
|
+
>
|
|
31
|
+
> More tools will be provided soon.
|
|
32
|
+
|
|
33
|
+
------
|
|
34
|
+
|
|
35
|
+
The figure demonstrates how a Python program is parsed into `PyScript`, `PyFunction`, `PyClass,` and `PyProgram` via `adtools`.
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
------
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
> [!TIP]
|
|
44
|
+
>
|
|
45
|
+
> It is recommended to use Python >= 3.10.
|
|
46
|
+
|
|
47
|
+
Run the following instructions to install adtools.
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
pip install git+https://github.com/RayZhhh/adtool.git
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
Parse your code (in string) into Python code instances.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from adtools import PyProgram
|
|
59
|
+
|
|
60
|
+
code = r'''
|
|
61
|
+
import ast
|
|
62
|
+
import numpy as np
|
|
63
|
+
|
|
64
|
+
def func():
|
|
65
|
+
a = 5
|
|
66
|
+
return a + a
|
|
67
|
+
|
|
68
|
+
class A(B):
|
|
69
|
+
a=1
|
|
70
|
+
|
|
71
|
+
@yes()
|
|
72
|
+
@deco()
|
|
73
|
+
def __init__(self):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
def method(self):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
b=2
|
|
80
|
+
'''
|
|
81
|
+
|
|
82
|
+
p = PyProgram.from_text(code)
|
|
83
|
+
print(p)
|
|
84
|
+
print(f'-------------------------------------')
|
|
85
|
+
print(p.classes[0].functions[0].decorator)
|
|
86
|
+
print(f'-------------------------------------')
|
|
87
|
+
print(p.functions[0].name)
|
|
88
|
+
```
|
|
89
|
+
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# Useful tools for parsing Python programs for algorithm design
|
|
2
|
+
|
|
3
|
+
------
|
|
4
|
+
|
|
5
|
+
> This repo aims to help develop more powerful [Large Language Models for Algorithm Design (LLM4AD)](https://github.com/Optima-CityU/llm4ad) applications.
|
|
6
|
+
>
|
|
7
|
+
> More tools will be provided soon.
|
|
8
|
+
|
|
9
|
+
------
|
|
10
|
+
|
|
11
|
+
The figure demonstrates how a Python program is parsed into `PyScript`, `PyFunction`, `PyClass,` and `PyProgram` via `adtools`.
|
|
12
|
+
|
|
13
|
+

|
|
14
|
+
|
|
15
|
+
------
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
> [!TIP]
|
|
20
|
+
>
|
|
21
|
+
> It is recommended to use Python >= 3.10.
|
|
22
|
+
|
|
23
|
+
Run the following instructions to install adtools.
|
|
24
|
+
|
|
25
|
+
```shell
|
|
26
|
+
pip install git+https://github.com/RayZhhh/adtool.git
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
Parse your code (in string) into Python code instances.
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
from adtools import PyProgram
|
|
35
|
+
|
|
36
|
+
code = r'''
|
|
37
|
+
import ast
|
|
38
|
+
import numpy as np
|
|
39
|
+
|
|
40
|
+
def func():
|
|
41
|
+
a = 5
|
|
42
|
+
return a + a
|
|
43
|
+
|
|
44
|
+
class A(B):
|
|
45
|
+
a=1
|
|
46
|
+
|
|
47
|
+
@yes()
|
|
48
|
+
@deco()
|
|
49
|
+
def __init__(self):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
def method(self):
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
b=2
|
|
56
|
+
'''
|
|
57
|
+
|
|
58
|
+
p = PyProgram.from_text(code)
|
|
59
|
+
print(p)
|
|
60
|
+
print(f'-------------------------------------')
|
|
61
|
+
print(p.classes[0].functions[0].decorator)
|
|
62
|
+
print(f'-------------------------------------')
|
|
63
|
+
print(p.functions[0].name)
|
|
64
|
+
```
|
|
65
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from .py_code import PyScript, PyFunction, PyClass, PyProgram
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Copyright (c) 2025 Rui Zhang <rzhang.cs@gmail.com>
|
|
3
|
+
|
|
4
|
+
NOTICE: This code is under MIT license. This code is intended for academic/research purposes only.
|
|
5
|
+
Commercial use of this software or its derivatives requires prior written permission.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import ast
|
|
9
|
+
import dataclasses
|
|
10
|
+
import textwrap
|
|
11
|
+
from typing import List, Optional
|
|
12
|
+
|
|
13
|
+
__all__ = ['PyScript', 'PyFunction', 'PyClass', 'PyProgram']
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclasses.dataclass
|
|
17
|
+
class PyScript:
|
|
18
|
+
"""A parsed Python script block (top-level code that's not in classes/functions).
|
|
19
|
+
"""
|
|
20
|
+
code: str
|
|
21
|
+
|
|
22
|
+
def __str__(self) -> str:
|
|
23
|
+
return self.code + '\n'
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclasses.dataclass
|
|
27
|
+
class PyFunction:
|
|
28
|
+
"""A parsed Python function.
|
|
29
|
+
> Part of this class is referenced from:
|
|
30
|
+
> https://github.com/google-deepmind/funsearch/blob/main/implementation/code_manipulation.py
|
|
31
|
+
"""
|
|
32
|
+
decorator: str
|
|
33
|
+
name: str
|
|
34
|
+
args: str
|
|
35
|
+
body: str
|
|
36
|
+
return_type: str | None = None
|
|
37
|
+
docstring: str | None = None
|
|
38
|
+
|
|
39
|
+
def __str__(self) -> str:
|
|
40
|
+
return_type = f' -> {self.return_type}' if self.return_type else ''
|
|
41
|
+
function = f'{self.decorator}\ndef {self.name}({self.args}){return_type}:\n'
|
|
42
|
+
if self.docstring:
|
|
43
|
+
# The self.docstring is already indented on every line except the first one.
|
|
44
|
+
# Here, we assume the indentation is always four spaces.
|
|
45
|
+
new_line = '\n' if self.body else ''
|
|
46
|
+
function += f' """{self.docstring}"""{new_line}'
|
|
47
|
+
# self.body is already indented.
|
|
48
|
+
function += self.body + '\n\n'
|
|
49
|
+
return function
|
|
50
|
+
|
|
51
|
+
def __setattr__(self, name: str, value: str) -> None:
|
|
52
|
+
# Ensure there aren't leading & trailing new lines in `body`
|
|
53
|
+
if name == 'body':
|
|
54
|
+
value = value.strip('\n')
|
|
55
|
+
# ensure there aren't leading & trailing quotes in `docstring`
|
|
56
|
+
if name == 'docstring' and value is not None:
|
|
57
|
+
if '"""' in value:
|
|
58
|
+
value = value.strip()
|
|
59
|
+
value = value.replace('"""', '')
|
|
60
|
+
super().__setattr__(name, value)
|
|
61
|
+
|
|
62
|
+
@classmethod
|
|
63
|
+
def extract_first_function_from_text(cls, text: str) -> 'PyFunction':
|
|
64
|
+
tree = ast.parse(text)
|
|
65
|
+
visitor = _ProgramVisitor(text)
|
|
66
|
+
visitor.visit(tree)
|
|
67
|
+
program = visitor.return_program()
|
|
68
|
+
return program.functions[0]
|
|
69
|
+
|
|
70
|
+
@classmethod
|
|
71
|
+
def extract_all_functions_from_text(cls, text: str) -> List['PyFunction']:
|
|
72
|
+
tree = ast.parse(text)
|
|
73
|
+
visitor = _ProgramVisitor(text)
|
|
74
|
+
visitor.visit(tree)
|
|
75
|
+
program = visitor.return_program()
|
|
76
|
+
return program.functions
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclasses.dataclass
|
|
80
|
+
class PyClass:
|
|
81
|
+
"""A parsed Python class."""
|
|
82
|
+
decorator: str
|
|
83
|
+
name: str
|
|
84
|
+
bases: str
|
|
85
|
+
class_vars_and_code: List[PyScript] = None
|
|
86
|
+
docstring: str | None = None
|
|
87
|
+
functions: list[PyFunction] = dataclasses.field(default_factory=list)
|
|
88
|
+
functions_class_vars_and_code: List[PyScript | PyFunction] | None = None
|
|
89
|
+
|
|
90
|
+
def __str__(self) -> str:
|
|
91
|
+
class_def = f'{self.decorator}\nclass {self.name}'
|
|
92
|
+
if self.bases:
|
|
93
|
+
class_def += f'({self.bases})'
|
|
94
|
+
class_def += ':\n'
|
|
95
|
+
|
|
96
|
+
if self.docstring:
|
|
97
|
+
class_def += f' """{self.docstring}"""\n'
|
|
98
|
+
|
|
99
|
+
for item in self.functions_class_vars_and_code:
|
|
100
|
+
if isinstance(item, PyScript):
|
|
101
|
+
class_def += f'{str(item)}\n'
|
|
102
|
+
else:
|
|
103
|
+
# Add functions with an extra level of indentation
|
|
104
|
+
class_def += textwrap.indent(str(item).strip(), ' ')
|
|
105
|
+
class_def += '\n\n'
|
|
106
|
+
return class_def
|
|
107
|
+
|
|
108
|
+
def __setattr__(self, name: str, value: str) -> None:
|
|
109
|
+
# Ensure there aren't leading & trailing new lines in `body`
|
|
110
|
+
if name == 'body':
|
|
111
|
+
value = value.strip('\n')
|
|
112
|
+
# ensure there aren't leading & trailing quotes in `docstring`
|
|
113
|
+
if name == 'docstring' and value is not None:
|
|
114
|
+
if '"""' in value:
|
|
115
|
+
value = value.strip()
|
|
116
|
+
value = value.replace('"""', '')
|
|
117
|
+
super().__setattr__(name, value)
|
|
118
|
+
|
|
119
|
+
@classmethod
|
|
120
|
+
def extract_first_class_from_text(cls, text: str) -> 'PyClass':
|
|
121
|
+
tree = ast.parse(text)
|
|
122
|
+
visitor = _ProgramVisitor(text)
|
|
123
|
+
visitor.visit(tree)
|
|
124
|
+
program = visitor.return_program()
|
|
125
|
+
return program.classes[0]
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def extract_all_classes_from_text(cls, text: str) -> List['PyClass']:
|
|
129
|
+
tree = ast.parse(text)
|
|
130
|
+
visitor = _ProgramVisitor(text)
|
|
131
|
+
visitor.visit(tree)
|
|
132
|
+
program = visitor.return_program()
|
|
133
|
+
return program.classes
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
@dataclasses.dataclass
|
|
137
|
+
class PyProgram:
|
|
138
|
+
"""A parsed Python program."""
|
|
139
|
+
|
|
140
|
+
scripts: list[PyScript] # Top-level code that's not in classes/functions
|
|
141
|
+
functions: list[PyFunction] # Top-level functions in the code
|
|
142
|
+
classes: list[PyClass] # Top-level classes in the code
|
|
143
|
+
classes_functions_scripts: list[PyFunction | PyClass | PyScript]
|
|
144
|
+
|
|
145
|
+
def __str__(self) -> str:
|
|
146
|
+
program = ''
|
|
147
|
+
for class_or_func_or_script in self.classes_functions_scripts:
|
|
148
|
+
program += str(class_or_func_or_script) + '\n'
|
|
149
|
+
return program
|
|
150
|
+
|
|
151
|
+
@classmethod
|
|
152
|
+
def from_text(cls, text: str) -> Optional['PyProgram']:
|
|
153
|
+
tree = ast.parse(text)
|
|
154
|
+
visitor = _ProgramVisitor(text)
|
|
155
|
+
visitor.visit(tree)
|
|
156
|
+
return visitor.return_program()
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class _ProgramVisitor(ast.NodeVisitor):
|
|
160
|
+
"""Parses code to collect all required information to produce a `Program`.
|
|
161
|
+
Now handles scripts, functions, and classes.
|
|
162
|
+
"""
|
|
163
|
+
|
|
164
|
+
def __init__(self, sourcecode: str):
|
|
165
|
+
self._codelines: list[str] = sourcecode.splitlines()
|
|
166
|
+
self._scripts: list[PyScript] = []
|
|
167
|
+
self._functions: list[PyFunction] = []
|
|
168
|
+
self._classes: list[PyClass] = []
|
|
169
|
+
self._classes_functions_scripts: list[PyFunction | PyClass | PyScript] = []
|
|
170
|
+
self._last_script_end = 0
|
|
171
|
+
|
|
172
|
+
def _add_script(self, start_line: int, end_line: int):
|
|
173
|
+
"""Add a script segment from the code."""
|
|
174
|
+
if start_line >= end_line:
|
|
175
|
+
return
|
|
176
|
+
script_code = '\n'.join(self._codelines[start_line:end_line]).strip()
|
|
177
|
+
if script_code:
|
|
178
|
+
script = PyScript(code=script_code)
|
|
179
|
+
self._scripts.append(script)
|
|
180
|
+
self._classes_functions_scripts.append(script)
|
|
181
|
+
|
|
182
|
+
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
|
183
|
+
"""Collects all information about the function being parsed.
|
|
184
|
+
"""
|
|
185
|
+
# We only consider top-level function
|
|
186
|
+
if node.col_offset == 0:
|
|
187
|
+
# Extract decorator first
|
|
188
|
+
has_decorators = bool(node.decorator_list)
|
|
189
|
+
if has_decorators:
|
|
190
|
+
# Find the minimum line number and retain the code above
|
|
191
|
+
decorator_start_line = min(decorator.lineno for decorator in node.decorator_list)
|
|
192
|
+
decorator = '\n'.join(self._codelines[decorator_start_line - 1: node.lineno - 1])
|
|
193
|
+
# Update script end line
|
|
194
|
+
script_end_line = decorator_start_line - 1
|
|
195
|
+
else:
|
|
196
|
+
decorator = ''
|
|
197
|
+
script_end_line = node.lineno - 1
|
|
198
|
+
|
|
199
|
+
# Add any script code before this function
|
|
200
|
+
self._add_script(self._last_script_end, script_end_line)
|
|
201
|
+
self._last_script_end = node.end_lineno
|
|
202
|
+
|
|
203
|
+
function_end_line = node.end_lineno
|
|
204
|
+
body_start_line = node.body[0].lineno - 1
|
|
205
|
+
docstring = None
|
|
206
|
+
|
|
207
|
+
if isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Constant):
|
|
208
|
+
docstring = ast.literal_eval(ast.unparse(node.body[0])).strip()
|
|
209
|
+
if len(node.body) > 1:
|
|
210
|
+
body_start_line = node.body[0].end_lineno
|
|
211
|
+
else:
|
|
212
|
+
body_start_line = function_end_line
|
|
213
|
+
|
|
214
|
+
func = PyFunction(
|
|
215
|
+
decorator=decorator,
|
|
216
|
+
name=node.name,
|
|
217
|
+
args=ast.unparse(node.args),
|
|
218
|
+
return_type=ast.unparse(node.returns) if node.returns else None,
|
|
219
|
+
docstring=docstring,
|
|
220
|
+
body='\n'.join(self._codelines[body_start_line: function_end_line]),
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
self._functions.append(func)
|
|
224
|
+
self._classes_functions_scripts.append(func)
|
|
225
|
+
self.generic_visit(node)
|
|
226
|
+
|
|
227
|
+
def visit_ClassDef(self, node: ast.ClassDef) -> None:
|
|
228
|
+
"""Collects all information about the class being parsed.
|
|
229
|
+
"""
|
|
230
|
+
# We only care about top-level classes
|
|
231
|
+
if node.col_offset == 0:
|
|
232
|
+
# Extract decorator first
|
|
233
|
+
has_decorators = bool(node.decorator_list)
|
|
234
|
+
if has_decorators:
|
|
235
|
+
# Find the minimum line number and retain the code above
|
|
236
|
+
decorator_start_line = min(decorator.lineno for decorator in node.decorator_list)
|
|
237
|
+
class_decorator = '\n'.join(self._codelines[decorator_start_line - 1: node.lineno - 1])
|
|
238
|
+
# Update script end line
|
|
239
|
+
script_end_line = decorator_start_line - 1
|
|
240
|
+
else:
|
|
241
|
+
class_decorator = ''
|
|
242
|
+
script_end_line = node.lineno - 1
|
|
243
|
+
|
|
244
|
+
# Add any script code before this class
|
|
245
|
+
self._add_script(self._last_script_end, script_end_line)
|
|
246
|
+
self._last_script_end = node.end_lineno
|
|
247
|
+
|
|
248
|
+
# Extract class docstring
|
|
249
|
+
docstring = None
|
|
250
|
+
if isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Constant):
|
|
251
|
+
docstring = ast.literal_eval(ast.unparse(node.body[0]))
|
|
252
|
+
|
|
253
|
+
methods = []
|
|
254
|
+
class_vars_and_code = []
|
|
255
|
+
function_class_vars_and_code = [] # record the order of function and class vars and code
|
|
256
|
+
|
|
257
|
+
# Traverse each body, if there is a docstring, skip body[0]
|
|
258
|
+
for item in node.body if docstring is None else node.body[1:]:
|
|
259
|
+
if isinstance(item, ast.FunctionDef):
|
|
260
|
+
# Extract decorators
|
|
261
|
+
has_decorators = bool(item.decorator_list)
|
|
262
|
+
if has_decorators:
|
|
263
|
+
# Find the minimum line number and retain the code above
|
|
264
|
+
decorator_start_line = min(decorator.lineno for decorator in item.decorator_list)
|
|
265
|
+
# Dedent decorator code for 4 spaces
|
|
266
|
+
decorator = []
|
|
267
|
+
for line in range(decorator_start_line - 1, item.lineno - 1):
|
|
268
|
+
dedented_decorator = self._codelines[line][4:]
|
|
269
|
+
decorator.append(dedented_decorator)
|
|
270
|
+
decorator = '\n'.join(decorator)
|
|
271
|
+
else:
|
|
272
|
+
decorator = ''
|
|
273
|
+
|
|
274
|
+
method_end_line = item.end_lineno
|
|
275
|
+
method_body_start_line = item.body[0].lineno - 1
|
|
276
|
+
method_docstring = None
|
|
277
|
+
|
|
278
|
+
# Extract doc-string if there exists
|
|
279
|
+
if isinstance(item.body[0], ast.Expr) and isinstance(item.body[0].value, ast.Constant):
|
|
280
|
+
method_docstring = ast.literal_eval(ast.unparse(item.body[0])).strip()
|
|
281
|
+
if len(item.body) > 1:
|
|
282
|
+
method_body_start_line = item.body[0].end_lineno
|
|
283
|
+
else:
|
|
284
|
+
method_body_start_line = method_end_line
|
|
285
|
+
|
|
286
|
+
# Extract function body and dedent for 4 spaces
|
|
287
|
+
body = []
|
|
288
|
+
for line in range(method_body_start_line, method_end_line):
|
|
289
|
+
dedented_body = self._codelines[line][4:]
|
|
290
|
+
body.append(dedented_body)
|
|
291
|
+
body = '\n'.join(body)
|
|
292
|
+
|
|
293
|
+
py_func = PyFunction(
|
|
294
|
+
decorator=decorator,
|
|
295
|
+
name=item.name,
|
|
296
|
+
args=ast.unparse(item.args),
|
|
297
|
+
return_type=ast.unparse(item.returns) if item.returns else None,
|
|
298
|
+
docstring=method_docstring,
|
|
299
|
+
body=body,
|
|
300
|
+
)
|
|
301
|
+
methods.append(py_func)
|
|
302
|
+
function_class_vars_and_code.append(py_func)
|
|
303
|
+
else: # If the item is not a function definition,add to class variables and code
|
|
304
|
+
code = []
|
|
305
|
+
for i in range(item.lineno - 1, item.end_lineno):
|
|
306
|
+
code.append(self._codelines[i])
|
|
307
|
+
py_script = PyScript(code='\n'.join(code))
|
|
308
|
+
class_vars_and_code.append(py_script)
|
|
309
|
+
function_class_vars_and_code.append(py_script)
|
|
310
|
+
|
|
311
|
+
# Get base classes
|
|
312
|
+
bases = ', '.join([ast.unparse(base) for base in node.bases]) if node.bases else ''
|
|
313
|
+
|
|
314
|
+
class_ = PyClass(
|
|
315
|
+
decorator=class_decorator,
|
|
316
|
+
name=node.name,
|
|
317
|
+
bases=bases,
|
|
318
|
+
docstring=docstring,
|
|
319
|
+
class_vars_and_code=class_vars_and_code if class_vars_and_code else None,
|
|
320
|
+
functions=methods,
|
|
321
|
+
functions_class_vars_and_code=function_class_vars_and_code if function_class_vars_and_code else None
|
|
322
|
+
)
|
|
323
|
+
self._classes.append(class_)
|
|
324
|
+
self._classes_functions_scripts.append(class_)
|
|
325
|
+
self.generic_visit(node)
|
|
326
|
+
|
|
327
|
+
def return_program(self) -> PyProgram:
|
|
328
|
+
# Add any remaining script code after the last class/function
|
|
329
|
+
self._add_script(self._last_script_end, len(self._codelines))
|
|
330
|
+
return PyProgram(
|
|
331
|
+
scripts=self._scripts,
|
|
332
|
+
functions=self._functions,
|
|
333
|
+
classes=self._classes,
|
|
334
|
+
classes_functions_scripts=self._classes_functions_scripts,
|
|
335
|
+
)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: py-adtools
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Useful tools for parsing Python programs for algorithm design.
|
|
5
|
+
Home-page: https://github.com/RayZhhh/py-adtools
|
|
6
|
+
Author: Rui Zhang
|
|
7
|
+
Author-email: rzhang.cs@gmail.com
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Scientific/Engineering
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: author
|
|
16
|
+
Dynamic: author-email
|
|
17
|
+
Dynamic: classifier
|
|
18
|
+
Dynamic: description
|
|
19
|
+
Dynamic: description-content-type
|
|
20
|
+
Dynamic: home-page
|
|
21
|
+
Dynamic: license-file
|
|
22
|
+
Dynamic: requires-python
|
|
23
|
+
Dynamic: summary
|
|
24
|
+
|
|
25
|
+
# Useful tools for parsing Python programs for algorithm design
|
|
26
|
+
|
|
27
|
+
------
|
|
28
|
+
|
|
29
|
+
> This repo aims to help develop more powerful [Large Language Models for Algorithm Design (LLM4AD)](https://github.com/Optima-CityU/llm4ad) applications.
|
|
30
|
+
>
|
|
31
|
+
> More tools will be provided soon.
|
|
32
|
+
|
|
33
|
+
------
|
|
34
|
+
|
|
35
|
+
The figure demonstrates how a Python program is parsed into `PyScript`, `PyFunction`, `PyClass,` and `PyProgram` via `adtools`.
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
------
|
|
40
|
+
|
|
41
|
+
## Installation
|
|
42
|
+
|
|
43
|
+
> [!TIP]
|
|
44
|
+
>
|
|
45
|
+
> It is recommended to use Python >= 3.10.
|
|
46
|
+
|
|
47
|
+
Run the following instructions to install adtools.
|
|
48
|
+
|
|
49
|
+
```shell
|
|
50
|
+
pip install git+https://github.com/RayZhhh/adtool.git
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Usage
|
|
54
|
+
|
|
55
|
+
Parse your code (in string) into Python code instances.
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from adtools import PyProgram
|
|
59
|
+
|
|
60
|
+
code = r'''
|
|
61
|
+
import ast
|
|
62
|
+
import numpy as np
|
|
63
|
+
|
|
64
|
+
def func():
|
|
65
|
+
a = 5
|
|
66
|
+
return a + a
|
|
67
|
+
|
|
68
|
+
class A(B):
|
|
69
|
+
a=1
|
|
70
|
+
|
|
71
|
+
@yes()
|
|
72
|
+
@deco()
|
|
73
|
+
def __init__(self):
|
|
74
|
+
pass
|
|
75
|
+
|
|
76
|
+
def method(self):
|
|
77
|
+
pass
|
|
78
|
+
|
|
79
|
+
b=2
|
|
80
|
+
'''
|
|
81
|
+
|
|
82
|
+
p = PyProgram.from_text(code)
|
|
83
|
+
print(p)
|
|
84
|
+
print(f'-------------------------------------')
|
|
85
|
+
print(p.classes[0].functions[0].decorator)
|
|
86
|
+
print(f'-------------------------------------')
|
|
87
|
+
print(p.functions[0].name)
|
|
88
|
+
```
|
|
89
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
adtools
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from setuptools import setup, find_packages
|
|
2
|
+
|
|
3
|
+
with open('README.md', 'r', encoding='utf-8') as fh:
|
|
4
|
+
long_description = fh.read()
|
|
5
|
+
|
|
6
|
+
setup(
|
|
7
|
+
name='py-adtools',
|
|
8
|
+
version='0.1.0',
|
|
9
|
+
author='Rui Zhang',
|
|
10
|
+
author_email='rzhang.cs@gmail.com',
|
|
11
|
+
description='Useful tools for parsing Python programs for algorithm design.',
|
|
12
|
+
long_description=long_description,
|
|
13
|
+
long_description_content_type='text/markdown',
|
|
14
|
+
url='https://github.com/RayZhhh/py-adtools',
|
|
15
|
+
packages=['adtools'],
|
|
16
|
+
classifiers=[
|
|
17
|
+
'Programming Language :: Python :: 3',
|
|
18
|
+
'Operating System :: OS Independent',
|
|
19
|
+
'Intended Audience :: Developers',
|
|
20
|
+
'Topic :: Scientific/Engineering',
|
|
21
|
+
],
|
|
22
|
+
python_requires='>=3.10',
|
|
23
|
+
install_requires=[],
|
|
24
|
+
)
|