quantalogic 0.61.2__py3-none-any.whl → 0.80__py3-none-any.whl
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.
- quantalogic/agent.py +0 -1
- quantalogic/codeact/TODO.md +14 -0
- quantalogic/codeact/agent.py +400 -421
- quantalogic/codeact/cli.py +42 -224
- quantalogic/codeact/cli_commands/__init__.py +0 -0
- quantalogic/codeact/cli_commands/create_toolbox.py +45 -0
- quantalogic/codeact/cli_commands/install_toolbox.py +20 -0
- quantalogic/codeact/cli_commands/list_executor.py +15 -0
- quantalogic/codeact/cli_commands/list_reasoners.py +15 -0
- quantalogic/codeact/cli_commands/list_toolboxes.py +47 -0
- quantalogic/codeact/cli_commands/task.py +215 -0
- quantalogic/codeact/cli_commands/tool_info.py +24 -0
- quantalogic/codeact/cli_commands/uninstall_toolbox.py +43 -0
- quantalogic/codeact/config.yaml +21 -0
- quantalogic/codeact/constants.py +1 -1
- quantalogic/codeact/events.py +12 -5
- quantalogic/codeact/examples/README.md +342 -0
- quantalogic/codeact/examples/agent_sample.yaml +29 -0
- quantalogic/codeact/executor.py +186 -0
- quantalogic/codeact/history_manager.py +94 -0
- quantalogic/codeact/llm_util.py +3 -22
- quantalogic/codeact/plugin_manager.py +92 -0
- quantalogic/codeact/prompts/generate_action.j2 +65 -14
- quantalogic/codeact/prompts/generate_program.j2 +32 -19
- quantalogic/codeact/react_agent.py +318 -0
- quantalogic/codeact/reasoner.py +185 -0
- quantalogic/codeact/templates/toolbox/README.md.j2 +10 -0
- quantalogic/codeact/templates/toolbox/pyproject.toml.j2 +16 -0
- quantalogic/codeact/templates/toolbox/tools.py.j2 +6 -0
- quantalogic/codeact/templates.py +7 -0
- quantalogic/codeact/tools_manager.py +242 -119
- quantalogic/codeact/utils.py +16 -89
- quantalogic/codeact/xml_utils.py +126 -0
- quantalogic/flow/flow.py +151 -41
- quantalogic/flow/flow_extractor.py +61 -1
- quantalogic/flow/flow_generator.py +34 -6
- quantalogic/flow/flow_manager.py +64 -25
- quantalogic/flow/flow_manager_schema.py +32 -0
- quantalogic/tools/action_gen.py +1 -1
- quantalogic/tools/action_gen_safe.py +340 -0
- quantalogic/tools/tool.py +531 -109
- quantalogic/tools/write_file_tool.py +7 -8
- {quantalogic-0.61.2.dist-info → quantalogic-0.80.dist-info}/METADATA +3 -2
- {quantalogic-0.61.2.dist-info → quantalogic-0.80.dist-info}/RECORD +47 -42
- {quantalogic-0.61.2.dist-info → quantalogic-0.80.dist-info}/WHEEL +1 -1
- quantalogic-0.80.dist-info/entry_points.txt +3 -0
- quantalogic/python_interpreter/__init__.py +0 -23
- quantalogic/python_interpreter/assignment_visitors.py +0 -63
- quantalogic/python_interpreter/base_visitors.py +0 -20
- quantalogic/python_interpreter/class_visitors.py +0 -22
- quantalogic/python_interpreter/comprehension_visitors.py +0 -172
- quantalogic/python_interpreter/context_visitors.py +0 -59
- quantalogic/python_interpreter/control_flow_visitors.py +0 -88
- quantalogic/python_interpreter/exception_visitors.py +0 -109
- quantalogic/python_interpreter/exceptions.py +0 -39
- quantalogic/python_interpreter/execution.py +0 -202
- quantalogic/python_interpreter/function_utils.py +0 -386
- quantalogic/python_interpreter/function_visitors.py +0 -209
- quantalogic/python_interpreter/import_visitors.py +0 -28
- quantalogic/python_interpreter/interpreter_core.py +0 -358
- quantalogic/python_interpreter/literal_visitors.py +0 -74
- quantalogic/python_interpreter/misc_visitors.py +0 -148
- quantalogic/python_interpreter/operator_visitors.py +0 -108
- quantalogic/python_interpreter/scope.py +0 -10
- quantalogic/python_interpreter/visit_handlers.py +0 -110
- quantalogic-0.61.2.dist-info/entry_points.txt +0 -6
- {quantalogic-0.61.2.dist-info → quantalogic-0.80.dist-info}/LICENSE +0 -0
@@ -1,386 +0,0 @@
|
|
1
|
-
import ast
|
2
|
-
import asyncio
|
3
|
-
from typing import Any, Dict, List, Optional
|
4
|
-
|
5
|
-
from .interpreter_core import ASTInterpreter
|
6
|
-
from .exceptions import ReturnException, BreakException, ContinueException
|
7
|
-
|
8
|
-
class GeneratorWrapper:
|
9
|
-
def __init__(self, gen):
|
10
|
-
self.gen = gen
|
11
|
-
self.closed = False
|
12
|
-
|
13
|
-
def __iter__(self):
|
14
|
-
return self
|
15
|
-
|
16
|
-
def __next__(self):
|
17
|
-
if self.closed:
|
18
|
-
raise StopIteration
|
19
|
-
try:
|
20
|
-
return next(self.gen)
|
21
|
-
except StopIteration:
|
22
|
-
self.closed = True
|
23
|
-
raise
|
24
|
-
|
25
|
-
def send(self, value):
|
26
|
-
if self.closed:
|
27
|
-
raise StopIteration
|
28
|
-
try:
|
29
|
-
return self.gen.send(value)
|
30
|
-
except StopIteration:
|
31
|
-
self.closed = True
|
32
|
-
raise
|
33
|
-
|
34
|
-
def throw(self, exc):
|
35
|
-
if self.closed:
|
36
|
-
raise StopIteration
|
37
|
-
try:
|
38
|
-
return self.gen.throw(exc)
|
39
|
-
except StopIteration:
|
40
|
-
self.closed = True
|
41
|
-
raise
|
42
|
-
|
43
|
-
def close(self):
|
44
|
-
self.closed = True
|
45
|
-
self.gen.close()
|
46
|
-
|
47
|
-
class Function:
|
48
|
-
def __init__(self, node: ast.FunctionDef, closure: List[Dict[str, Any]], interpreter: ASTInterpreter,
|
49
|
-
pos_kw_params: List[str], vararg_name: Optional[str], kwonly_params: List[str],
|
50
|
-
kwarg_name: Optional[str], pos_defaults: Dict[str, Any], kw_defaults: Dict[str, Any]) -> None:
|
51
|
-
self.node: ast.FunctionDef = node
|
52
|
-
self.closure: List[Dict[str, Any]] = closure[:]
|
53
|
-
self.interpreter: ASTInterpreter = interpreter
|
54
|
-
self.pos_kw_params = pos_kw_params
|
55
|
-
self.vararg_name = vararg_name
|
56
|
-
self.kwonly_params = kwonly_params
|
57
|
-
self.kwarg_name = kwarg_name
|
58
|
-
self.pos_defaults = pos_defaults
|
59
|
-
self.kw_defaults = kw_defaults
|
60
|
-
self.defining_class = None
|
61
|
-
self.is_generator = any(isinstance(n, (ast.Yield, ast.YieldFrom)) for n in ast.walk(node))
|
62
|
-
self.generator_state = None # Added for generator protocol
|
63
|
-
|
64
|
-
async def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
65
|
-
new_env_stack: List[Dict[str, Any]] = self.closure[:]
|
66
|
-
local_frame: Dict[str, Any] = {}
|
67
|
-
local_frame[self.node.name] = self
|
68
|
-
|
69
|
-
num_pos = len(self.pos_kw_params)
|
70
|
-
for i, arg in enumerate(args):
|
71
|
-
if i < num_pos:
|
72
|
-
local_frame[self.pos_kw_params[i]] = arg
|
73
|
-
elif self.vararg_name:
|
74
|
-
if self.vararg_name not in local_frame:
|
75
|
-
local_frame[self.vararg_name] = []
|
76
|
-
local_frame[self.vararg_name].append(arg)
|
77
|
-
else:
|
78
|
-
raise TypeError(f"Function '{self.node.name}' takes {num_pos} positional arguments but {len(args)} were given")
|
79
|
-
if self.vararg_name and self.vararg_name not in local_frame:
|
80
|
-
local_frame[self.vararg_name] = tuple()
|
81
|
-
|
82
|
-
for kwarg_name, kwarg_value in kwargs.items():
|
83
|
-
if kwarg_name in self.pos_kw_params or kwarg_name in self.kwonly_params:
|
84
|
-
if kwarg_name in local_frame:
|
85
|
-
raise TypeError(f"Function '{self.node.name}' got multiple values for argument '{kwarg_name}'")
|
86
|
-
local_frame[kwarg_name] = kwarg_value
|
87
|
-
elif self.kwarg_name:
|
88
|
-
if self.kwarg_name not in local_frame:
|
89
|
-
local_frame[self.kwarg_name] = {}
|
90
|
-
local_frame[self.kwarg_name][kwarg_name] = kwarg_value
|
91
|
-
else:
|
92
|
-
raise TypeError(f"Function '{self.node.name}' got an unexpected keyword argument '{kwarg_name}'")
|
93
|
-
|
94
|
-
for param in self.pos_kw_params:
|
95
|
-
if param not in local_frame and param in self.pos_defaults:
|
96
|
-
local_frame[param] = self.pos_defaults[param]
|
97
|
-
for param in self.kwonly_params:
|
98
|
-
if param not in local_frame and param in self.kw_defaults:
|
99
|
-
local_frame[param] = self.kw_defaults[param]
|
100
|
-
|
101
|
-
if self.kwarg_name and self.kwarg_name in local_frame:
|
102
|
-
local_frame[self.kwarg_name] = dict(local_frame[self.kwarg_name])
|
103
|
-
|
104
|
-
missing_args = [param for param in self.pos_kw_params if param not in local_frame and param not in self.pos_defaults]
|
105
|
-
missing_args += [param for param in self.kwonly_params if param not in local_frame and param not in self.kw_defaults]
|
106
|
-
if missing_args:
|
107
|
-
raise TypeError(f"Function '{self.node.name}' missing required arguments: {', '.join(missing_args)}")
|
108
|
-
|
109
|
-
if self.pos_kw_params and self.pos_kw_params[0] == 'self' and args:
|
110
|
-
local_frame['self'] = args[0]
|
111
|
-
local_frame['__current_method__'] = self
|
112
|
-
|
113
|
-
new_env_stack.append(local_frame)
|
114
|
-
new_interp: ASTInterpreter = self.interpreter.spawn_from_env(new_env_stack)
|
115
|
-
|
116
|
-
if self.defining_class and args:
|
117
|
-
new_interp.current_class = self.defining_class
|
118
|
-
new_interp.current_instance = args[0]
|
119
|
-
|
120
|
-
if self.is_generator:
|
121
|
-
async def generator():
|
122
|
-
for body_stmt in self.node.body:
|
123
|
-
if isinstance(body_stmt, ast.Expr) and isinstance(body_stmt.value, ast.Yield):
|
124
|
-
value = await new_interp.visit(body_stmt.value, wrap_exceptions=True)
|
125
|
-
yield value
|
126
|
-
elif isinstance(body_stmt, ast.Expr) and isinstance(body_stmt.value, ast.YieldFrom):
|
127
|
-
sub_iterable = await new_interp.visit(body_stmt.value, wrap_exceptions=True)
|
128
|
-
for v in sub_iterable:
|
129
|
-
yield v
|
130
|
-
else:
|
131
|
-
await new_interp.visit(body_stmt, wrap_exceptions=True)
|
132
|
-
gen = generator()
|
133
|
-
return GeneratorWrapper(gen)
|
134
|
-
else:
|
135
|
-
last_value = None
|
136
|
-
try:
|
137
|
-
for stmt in self.node.body:
|
138
|
-
last_value = await new_interp.visit(stmt, wrap_exceptions=True)
|
139
|
-
return last_value
|
140
|
-
except ReturnException as ret:
|
141
|
-
return ret.value
|
142
|
-
return last_value
|
143
|
-
|
144
|
-
def _send_sync(self, gen, value):
|
145
|
-
try:
|
146
|
-
return next(gen) if value is None else gen.send(value)
|
147
|
-
except StopIteration:
|
148
|
-
raise
|
149
|
-
|
150
|
-
def _throw_sync(self, gen, exc):
|
151
|
-
try:
|
152
|
-
return gen.throw(exc)
|
153
|
-
except StopIteration:
|
154
|
-
raise
|
155
|
-
|
156
|
-
def __get__(self, instance: Any, owner: Any):
|
157
|
-
if instance is None:
|
158
|
-
return self
|
159
|
-
async def method(*args: Any, **kwargs: Any) -> Any:
|
160
|
-
return await self(instance, *args, **kwargs)
|
161
|
-
method.__self__ = instance
|
162
|
-
return method
|
163
|
-
|
164
|
-
class AsyncFunction:
|
165
|
-
def __init__(self, node: ast.AsyncFunctionDef, closure: List[Dict[str, Any]], interpreter: ASTInterpreter,
|
166
|
-
pos_kw_params: List[str], vararg_name: Optional[str], kwonly_params: List[str],
|
167
|
-
kwarg_name: Optional[str], pos_defaults: Dict[str, Any], kw_defaults: Dict[str, Any]) -> None:
|
168
|
-
self.node: ast.AsyncFunctionDef = node
|
169
|
-
self.closure: List[Dict[str, Any]] = closure[:]
|
170
|
-
self.interpreter: ASTInterpreter = interpreter
|
171
|
-
self.pos_kw_params = pos_kw_params
|
172
|
-
self.vararg_name = vararg_name
|
173
|
-
self.kwonly_params = kwonly_params
|
174
|
-
self.kwarg_name = kwarg_name
|
175
|
-
self.pos_defaults = pos_defaults
|
176
|
-
self.kw_defaults = kw_defaults
|
177
|
-
|
178
|
-
async def __call__(self, *args: Any, **kwargs: Any) -> tuple[Any, Dict[str, Any]]:
|
179
|
-
new_env_stack: List[Dict[str, Any]] = self.closure[:]
|
180
|
-
local_frame: Dict[str, Any] = {}
|
181
|
-
local_frame[self.node.name] = self
|
182
|
-
|
183
|
-
num_pos = len(self.pos_kw_params)
|
184
|
-
for i, arg in enumerate(args):
|
185
|
-
if i < num_pos:
|
186
|
-
local_frame[self.pos_kw_params[i]] = arg
|
187
|
-
elif self.vararg_name:
|
188
|
-
if self.vararg_name not in local_frame:
|
189
|
-
local_frame[self.vararg_name] = []
|
190
|
-
local_frame[self.vararg_name].append(arg)
|
191
|
-
else:
|
192
|
-
raise TypeError(f"Async function '{self.node.name}' takes {num_pos} positional arguments but {len(args)} were given")
|
193
|
-
if self.vararg_name and self.vararg_name not in local_frame:
|
194
|
-
local_frame[self.vararg_name] = tuple()
|
195
|
-
|
196
|
-
for kwarg_name, kwarg_value in kwargs.items():
|
197
|
-
if kwarg_name in self.pos_kw_params or kwarg_name in self.kwonly_params:
|
198
|
-
if kwarg_name in local_frame:
|
199
|
-
raise TypeError(f"Async function '{self.node.name}' got multiple values for argument '{kwarg_name}'")
|
200
|
-
local_frame[kwarg_name] = kwarg_value
|
201
|
-
elif self.kwarg_name:
|
202
|
-
if self.kwarg_name not in local_frame:
|
203
|
-
local_frame[self.kwarg_name] = {}
|
204
|
-
local_frame[self.kwarg_name][kwarg_name] = kwarg_value
|
205
|
-
else:
|
206
|
-
raise TypeError(f"Async function '{self.node.name}' got an unexpected keyword argument '{kwarg_name}'")
|
207
|
-
|
208
|
-
for param in self.pos_kw_params:
|
209
|
-
if param not in local_frame and param in self.pos_defaults:
|
210
|
-
local_frame[param] = self.pos_defaults[param]
|
211
|
-
for param in self.kwonly_params:
|
212
|
-
if param not in local_frame and param in self.kw_defaults:
|
213
|
-
local_frame[param] = self.kw_defaults[param]
|
214
|
-
|
215
|
-
missing_args = [param for param in self.pos_kw_params if param not in local_frame and param not in self.pos_defaults]
|
216
|
-
missing_args += [param for param in self.kwonly_params if param not in local_frame and param not in self.kw_defaults]
|
217
|
-
if missing_args:
|
218
|
-
raise TypeError(f"Async function '{self.node.name}' missing required arguments: {', '.join(missing_args)}")
|
219
|
-
|
220
|
-
new_env_stack.append(local_frame)
|
221
|
-
new_interp: ASTInterpreter = self.interpreter.spawn_from_env(new_env_stack)
|
222
|
-
last_value = None
|
223
|
-
try:
|
224
|
-
for stmt in self.node.body:
|
225
|
-
last_value = await new_interp.visit(stmt, wrap_exceptions=True)
|
226
|
-
return last_value, {k: v for k, v in local_frame.items() if not k.startswith('__')}
|
227
|
-
except ReturnException as ret:
|
228
|
-
return ret.value, {k: v for k, v in local_frame.items() if not k.startswith('__')}
|
229
|
-
finally:
|
230
|
-
new_env_stack.pop()
|
231
|
-
|
232
|
-
class AsyncGeneratorFunction:
|
233
|
-
def __init__(self, node: ast.AsyncFunctionDef, closure: List[Dict[str, Any]], interpreter: ASTInterpreter,
|
234
|
-
pos_kw_params: List[str], vararg_name: Optional[str], kwonly_params: List[str],
|
235
|
-
kwarg_name: Optional[str], pos_defaults: Dict[str, Any], kw_defaults: Dict[str, Any]) -> None:
|
236
|
-
self.node: ast.AsyncFunctionDef = node
|
237
|
-
self.closure: List[Dict[str, Any]] = closure[:]
|
238
|
-
self.interpreter: ASTInterpreter = interpreter
|
239
|
-
self.pos_kw_params = pos_kw_params
|
240
|
-
self.vararg_name = vararg_name
|
241
|
-
self.kwonly_params = kwonly_params
|
242
|
-
self.kwarg_name = kwarg_name
|
243
|
-
self.pos_defaults = pos_defaults
|
244
|
-
self.kw_defaults = kw_defaults
|
245
|
-
self.generator_state = None
|
246
|
-
|
247
|
-
async def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
248
|
-
new_env_stack: List[Dict[str, Any]] = self.closure[:]
|
249
|
-
local_frame: Dict[str, Any] = {}
|
250
|
-
local_frame[self.node.name] = self
|
251
|
-
|
252
|
-
num_pos = len(self.pos_kw_params)
|
253
|
-
for i, arg in enumerate(args):
|
254
|
-
if i < num_pos:
|
255
|
-
local_frame[self.pos_kw_params[i]] = arg
|
256
|
-
elif self.vararg_name:
|
257
|
-
if self.vararg_name not in local_frame:
|
258
|
-
local_frame[self.vararg_name] = []
|
259
|
-
local_frame[self.vararg_name].append(arg)
|
260
|
-
else:
|
261
|
-
raise TypeError(f"Async generator '{self.node.name}' takes {num_pos} positional arguments but {len(args)} were given")
|
262
|
-
if self.vararg_name and self.vararg_name not in local_frame:
|
263
|
-
local_frame[self.vararg_name] = tuple()
|
264
|
-
|
265
|
-
for kwarg_name, kwarg_value in kwargs.items():
|
266
|
-
if kwarg_name in self.pos_kw_params or kwarg_name in self.kwonly_params:
|
267
|
-
if kwarg_name in local_frame:
|
268
|
-
raise TypeError(f"Async generator '{self.node.name}' got multiple values for argument '{kwarg_name}'")
|
269
|
-
local_frame[kwarg_name] = kwarg_value
|
270
|
-
elif self.kwarg_name:
|
271
|
-
if self.kwarg_name not in local_frame:
|
272
|
-
local_frame[self.kwarg_name] = {}
|
273
|
-
local_frame[self.kwarg_name][kwarg_name] = kwarg_value
|
274
|
-
else:
|
275
|
-
raise TypeError(f"Async generator '{self.node.name}' got an unexpected keyword argument '{kwarg_name}'")
|
276
|
-
|
277
|
-
for param in self.pos_kw_params:
|
278
|
-
if param not in local_frame and param in self.pos_defaults:
|
279
|
-
local_frame[param] = self.pos_defaults[param]
|
280
|
-
for param in self.kwonly_params:
|
281
|
-
if param not in local_frame and param in self.kw_defaults:
|
282
|
-
local_frame[param] = self.kw_defaults[param]
|
283
|
-
|
284
|
-
missing_args = [param for param in self.pos_kw_params if param not in local_frame and param not in self.pos_defaults]
|
285
|
-
missing_args += [param for param in self.kwonly_params if param not in local_frame and param not in self.kw_defaults]
|
286
|
-
if missing_args:
|
287
|
-
raise TypeError(f"Async generator '{self.node.name}' missing required arguments: {', '.join(missing_args)}")
|
288
|
-
|
289
|
-
new_env_stack.append(local_frame)
|
290
|
-
new_interp: ASTInterpreter = self.interpreter.spawn_from_env(new_env_stack)
|
291
|
-
|
292
|
-
async def generator():
|
293
|
-
nonlocal self
|
294
|
-
if self.generator_state is None:
|
295
|
-
self.generator_state = {"closed": False, "pending_value": None}
|
296
|
-
|
297
|
-
for stmt in self.node.body:
|
298
|
-
if self.generator_state["closed"]:
|
299
|
-
raise StopAsyncIteration
|
300
|
-
|
301
|
-
if isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.Yield):
|
302
|
-
value = await new_interp.visit(stmt.value, wrap_exceptions=True)
|
303
|
-
yield value
|
304
|
-
elif isinstance(stmt, ast.Expr) and isinstance(stmt.value, ast.YieldFrom):
|
305
|
-
sub_iterable = await new_interp.visit(stmt.value, wrap_exceptions=True)
|
306
|
-
async for v in sub_iterable:
|
307
|
-
yield v
|
308
|
-
else:
|
309
|
-
await new_interp.visit(stmt, wrap_exceptions=True)
|
310
|
-
|
311
|
-
gen = generator()
|
312
|
-
gen.send = lambda value: asyncio.run_coroutine_threadsafe(self._send(gen, value), self.interpreter.loop).result()
|
313
|
-
gen.throw = lambda exc: asyncio.run_coroutine_threadsafe(self._throw(gen, exc), self.interpreter.loop).result()
|
314
|
-
gen.close = lambda: setattr(self.generator_state, "closed", True)
|
315
|
-
return gen
|
316
|
-
|
317
|
-
async def _send(self, gen, value):
|
318
|
-
try:
|
319
|
-
return await gen.asend(value)
|
320
|
-
except StopAsyncIteration:
|
321
|
-
raise
|
322
|
-
|
323
|
-
async def _throw(self, gen, exc):
|
324
|
-
try:
|
325
|
-
return await gen.athrow(exc)
|
326
|
-
except StopAsyncIteration:
|
327
|
-
raise
|
328
|
-
|
329
|
-
class LambdaFunction:
|
330
|
-
def __init__(self, node: ast.Lambda, closure: List[Dict[str, Any]], interpreter: ASTInterpreter,
|
331
|
-
pos_kw_params: List[str], vararg_name: Optional[str], kwonly_params: List[str],
|
332
|
-
kwarg_name: Optional[str], pos_defaults: Dict[str, Any], kw_defaults: Dict[str, Any]) -> None:
|
333
|
-
self.node: ast.Lambda = node
|
334
|
-
self.closure: List[Dict[str, Any]] = closure[:]
|
335
|
-
self.interpreter: ASTInterpreter = interpreter
|
336
|
-
self.pos_kw_params = pos_kw_params
|
337
|
-
self.vararg_name = vararg_name
|
338
|
-
self.kwonly_params = kwonly_params
|
339
|
-
self.kwarg_name = kwarg_name
|
340
|
-
self.pos_defaults = pos_defaults
|
341
|
-
self.kw_defaults = kw_defaults
|
342
|
-
|
343
|
-
async def __call__(self, *args: Any, **kwargs: Any) -> Any:
|
344
|
-
new_env_stack: List[Dict[str, Any]] = self.closure[:]
|
345
|
-
local_frame: Dict[str, Any] = {}
|
346
|
-
|
347
|
-
num_pos = len(self.pos_kw_params)
|
348
|
-
for i, arg in enumerate(args):
|
349
|
-
if i < num_pos:
|
350
|
-
local_frame[self.pos_kw_params[i]] = arg
|
351
|
-
elif self.vararg_name:
|
352
|
-
if self.vararg_name not in local_frame:
|
353
|
-
local_frame[self.vararg_name] = []
|
354
|
-
local_frame[self.vararg_name].append(arg)
|
355
|
-
else:
|
356
|
-
raise TypeError(f"Lambda takes {num_pos} positional arguments but {len(args)} were given")
|
357
|
-
if self.vararg_name and self.vararg_name not in local_frame:
|
358
|
-
local_frame[self.vararg_name] = tuple()
|
359
|
-
|
360
|
-
for kwarg_name, kwarg_value in kwargs.items():
|
361
|
-
if kwarg_name in self.pos_kw_params or kwarg_name in self.kwonly_params:
|
362
|
-
if kwarg_name in local_frame:
|
363
|
-
raise TypeError(f"Lambda got multiple values for argument '{kwarg_name}'")
|
364
|
-
local_frame[kwarg_name] = kwarg_value
|
365
|
-
elif self.kwarg_name:
|
366
|
-
if self.kwarg_name not in local_frame:
|
367
|
-
local_frame[self.kwarg_name] = {}
|
368
|
-
local_frame[self.kwarg_name][kwarg_name] = kwarg_value
|
369
|
-
else:
|
370
|
-
raise TypeError(f"Lambda got an unexpected keyword argument '{kwarg_name}'")
|
371
|
-
|
372
|
-
for param in self.pos_kw_params:
|
373
|
-
if param not in local_frame and param in self.pos_defaults:
|
374
|
-
local_frame[param] = self.pos_defaults[param]
|
375
|
-
for param in self.kwonly_params:
|
376
|
-
if param not in local_frame and param in self.kw_defaults:
|
377
|
-
local_frame[param] = self.kw_defaults[param]
|
378
|
-
|
379
|
-
missing_args = [param for param in self.pos_kw_params if param not in local_frame and param not in self.pos_defaults]
|
380
|
-
missing_args += [param for param in self.kwonly_params if param not in local_frame and param not in self.kw_defaults]
|
381
|
-
if missing_args:
|
382
|
-
raise TypeError(f"Lambda missing required arguments: {', '.join(missing_args)}")
|
383
|
-
|
384
|
-
new_env_stack.append(local_frame)
|
385
|
-
new_interp: ASTInterpreter = self.interpreter.spawn_from_env(new_env_stack)
|
386
|
-
return await new_interp.visit(self.node.body, wrap_exceptions=True)
|
@@ -1,209 +0,0 @@
|
|
1
|
-
import ast
|
2
|
-
import asyncio
|
3
|
-
import inspect
|
4
|
-
from typing import Any, Dict, List
|
5
|
-
|
6
|
-
from .exceptions import WrappedException
|
7
|
-
from .function_utils import AsyncFunction, Function, LambdaFunction, AsyncGeneratorFunction
|
8
|
-
from .interpreter_core import ASTInterpreter
|
9
|
-
|
10
|
-
async def visit_FunctionDef(self: ASTInterpreter, node: ast.FunctionDef, wrap_exceptions: bool = True) -> None:
|
11
|
-
closure: List[Dict[str, Any]] = self.env_stack[:]
|
12
|
-
pos_kw_params = [arg.arg for arg in node.args.args]
|
13
|
-
vararg_name = node.args.vararg.arg if node.args.vararg else None
|
14
|
-
kwonly_params = [arg.arg for arg in node.args.kwonlyargs]
|
15
|
-
kwarg_name = node.args.kwarg.arg if node.args.kwarg else None
|
16
|
-
pos_defaults_values = [await self.visit(default, wrap_exceptions=True) for default in node.args.defaults]
|
17
|
-
num_pos_defaults = len(pos_defaults_values)
|
18
|
-
pos_defaults = dict(zip(pos_kw_params[-num_pos_defaults:], pos_defaults_values)) if num_pos_defaults else {}
|
19
|
-
kw_defaults_values = [await self.visit(default, wrap_exceptions=True) if default else None for default in node.args.kw_defaults]
|
20
|
-
kw_defaults = dict(zip(kwonly_params, kw_defaults_values))
|
21
|
-
kw_defaults = {k: v for k, v in kw_defaults.items() if v is not None}
|
22
|
-
|
23
|
-
func = Function(node, closure, self, pos_kw_params, vararg_name, kwonly_params, kwarg_name, pos_defaults, kw_defaults)
|
24
|
-
decorated_func = func
|
25
|
-
for decorator in reversed(node.decorator_list):
|
26
|
-
dec = await self.visit(decorator, wrap_exceptions=True)
|
27
|
-
if asyncio.iscoroutine(dec):
|
28
|
-
dec = await dec
|
29
|
-
if dec in (staticmethod, classmethod, property):
|
30
|
-
decorated_func = dec(func)
|
31
|
-
else:
|
32
|
-
decorated_func = await self._execute_function(dec, [decorated_func], {})
|
33
|
-
self.set_variable(node.name, decorated_func)
|
34
|
-
|
35
|
-
async def visit_AsyncFunctionDef(self: ASTInterpreter, node: ast.AsyncFunctionDef, wrap_exceptions: bool = True) -> None:
|
36
|
-
closure: List[Dict[str, Any]] = self.env_stack[:]
|
37
|
-
pos_kw_params = [arg.arg for arg in node.args.args]
|
38
|
-
vararg_name = node.args.vararg.arg if node.args.vararg else None
|
39
|
-
kwonly_params = [arg.arg for arg in node.args.kwonlyargs]
|
40
|
-
kwarg_name = node.args.kwarg.arg if node.args.kwarg else None
|
41
|
-
pos_defaults_values = [await self.visit(default, wrap_exceptions=True) for default in node.args.defaults]
|
42
|
-
num_pos_defaults = len(pos_defaults_values)
|
43
|
-
pos_defaults = dict(zip(pos_kw_params[-num_pos_defaults:], pos_defaults_values)) if num_pos_defaults else {}
|
44
|
-
kw_defaults_values = [await self.visit(default, wrap_exceptions=True) if default else None for default in node.args.kw_defaults]
|
45
|
-
kw_defaults = dict(zip(kwonly_params, kw_defaults_values))
|
46
|
-
kw_defaults = {k: v for k, v in kw_defaults.items() if v is not None}
|
47
|
-
|
48
|
-
func = AsyncFunction(node, closure, self, pos_kw_params, vararg_name, kwonly_params, kwarg_name, pos_defaults, kw_defaults)
|
49
|
-
for decorator in reversed(node.decorator_list):
|
50
|
-
dec = await self.visit(decorator, wrap_exceptions=True)
|
51
|
-
if asyncio.iscoroutine(dec):
|
52
|
-
dec = await dec
|
53
|
-
func = await self._execute_function(dec, [func], {})
|
54
|
-
self.set_variable(node.name, func)
|
55
|
-
|
56
|
-
async def visit_AsyncGeneratorDef(self: ASTInterpreter, node: ast.AsyncFunctionDef, wrap_exceptions: bool = True) -> None:
|
57
|
-
closure: List[Dict[str, Any]] = self.env_stack[:]
|
58
|
-
pos_kw_params = [arg.arg for arg in node.args.args]
|
59
|
-
vararg_name = node.args.vararg.arg if node.args.vararg else None
|
60
|
-
kwonly_params = [arg.arg for arg in node.args.kwonlyargs]
|
61
|
-
kwarg_name = node.args.kwarg.arg if node.args.kwarg else None
|
62
|
-
pos_defaults_values = [await self.visit(default, wrap_exceptions=True) for default in node.args.defaults]
|
63
|
-
num_pos_defaults = len(pos_defaults_values)
|
64
|
-
pos_defaults = dict(zip(pos_kw_params[-num_pos_defaults:], pos_defaults_values)) if num_pos_defaults else {}
|
65
|
-
kw_defaults_values = [await self.visit(default, wrap_exceptions=True) if default else None for default in node.args.kw_defaults]
|
66
|
-
kw_defaults = dict(zip(kwonly_params, kw_defaults_values))
|
67
|
-
kw_defaults = {k: v for k, v in kw_defaults.items() if v is not None}
|
68
|
-
|
69
|
-
func = AsyncGeneratorFunction(node, closure, self, pos_kw_params, vararg_name, kwonly_params, kwarg_name, pos_defaults, kw_defaults)
|
70
|
-
for decorator in reversed(node.decorator_list):
|
71
|
-
dec = await self.visit(decorator, wrap_exceptions=True)
|
72
|
-
if asyncio.iscoroutine(dec):
|
73
|
-
dec = await dec
|
74
|
-
func = await self._execute_function(dec, [func], {})
|
75
|
-
self.set_variable(node.name, func)
|
76
|
-
|
77
|
-
async def visit_Call(self: ASTInterpreter, node: ast.Call, is_await_context: bool = False, wrap_exceptions: bool = True) -> Any:
|
78
|
-
func = await self.visit(node.func, wrap_exceptions=wrap_exceptions)
|
79
|
-
|
80
|
-
evaluated_args: List[Any] = []
|
81
|
-
for arg in node.args:
|
82
|
-
arg_value = await self.visit(arg, wrap_exceptions=wrap_exceptions)
|
83
|
-
if isinstance(arg, ast.Starred):
|
84
|
-
evaluated_args.extend(arg_value)
|
85
|
-
else:
|
86
|
-
evaluated_args.append(arg_value)
|
87
|
-
|
88
|
-
kwargs: Dict[str, Any] = {}
|
89
|
-
for kw in node.keywords:
|
90
|
-
if kw.arg is None:
|
91
|
-
unpacked_kwargs = await self.visit(kw.value, wrap_exceptions=wrap_exceptions)
|
92
|
-
if not isinstance(unpacked_kwargs, dict):
|
93
|
-
raise TypeError(f"** argument must be a mapping, not {type(unpacked_kwargs).__name__}")
|
94
|
-
kwargs.update(unpacked_kwargs)
|
95
|
-
else:
|
96
|
-
kwargs[kw.arg] = await self.visit(kw.value, wrap_exceptions=wrap_exceptions)
|
97
|
-
|
98
|
-
# Handle str() explicitly to avoid __new__ restriction
|
99
|
-
if func is str:
|
100
|
-
if len(evaluated_args) != 1:
|
101
|
-
raise TypeError(f"str() takes exactly one argument ({len(evaluated_args)} given)")
|
102
|
-
arg = evaluated_args[0]
|
103
|
-
return f"{arg}" # Use f-string to safely convert to string
|
104
|
-
|
105
|
-
# Handle exceptions passed to str() (original behavior preserved)
|
106
|
-
if func is str and len(evaluated_args) == 1 and isinstance(evaluated_args[0], BaseException):
|
107
|
-
exc = evaluated_args[0]
|
108
|
-
if isinstance(exc, WrappedException) and hasattr(exc, 'original_exception'):
|
109
|
-
inner_exc = exc.original_exception
|
110
|
-
return inner_exc.args[0] if inner_exc.args else str(inner_exc)
|
111
|
-
return exc.args[0] if exc.args else str(exc)
|
112
|
-
|
113
|
-
if func is super:
|
114
|
-
if len(evaluated_args) == 0:
|
115
|
-
if not (self.current_class and self.current_instance):
|
116
|
-
# Infer from call stack if possible
|
117
|
-
for frame in reversed(self.env_stack):
|
118
|
-
if 'self' in frame and '__current_method__' in frame:
|
119
|
-
self.current_instance = frame['self']
|
120
|
-
self.current_class = frame['self'].__class__
|
121
|
-
break
|
122
|
-
if not (self.current_class and self.current_instance):
|
123
|
-
raise TypeError("super() without arguments requires a class instantiation context")
|
124
|
-
result = super(self.current_class, self.current_instance)
|
125
|
-
elif len(evaluated_args) >= 2:
|
126
|
-
cls, obj = evaluated_args[0], evaluated_args[1]
|
127
|
-
result = super(cls, obj)
|
128
|
-
else:
|
129
|
-
raise TypeError("super() requires class and instance arguments")
|
130
|
-
return result
|
131
|
-
|
132
|
-
if isinstance(node.func, ast.Attribute) and isinstance(node.func.value, ast.Call) and isinstance(node.func.value.func, ast.Name) and node.func.value.func.id == 'super':
|
133
|
-
super_call = await self.visit(node.func.value, wrap_exceptions=wrap_exceptions)
|
134
|
-
method = getattr(super_call, node.func.attr)
|
135
|
-
return await self._execute_function(method, evaluated_args, kwargs)
|
136
|
-
|
137
|
-
if func is list and len(evaluated_args) == 1 and hasattr(evaluated_args[0], '__aiter__'):
|
138
|
-
return [val async for val in evaluated_args[0]]
|
139
|
-
|
140
|
-
if func in (range, list, dict, set, tuple, frozenset):
|
141
|
-
return func(*evaluated_args, **kwargs)
|
142
|
-
|
143
|
-
if inspect.isclass(func):
|
144
|
-
instance = await self._create_class_instance(func, *evaluated_args, **kwargs)
|
145
|
-
return instance
|
146
|
-
|
147
|
-
if isinstance(func, (staticmethod, classmethod, property)):
|
148
|
-
if isinstance(func, property):
|
149
|
-
result = func.fget(*evaluated_args, **kwargs)
|
150
|
-
else:
|
151
|
-
result = func(*evaluated_args, **kwargs)
|
152
|
-
elif asyncio.iscoroutinefunction(func) or isinstance(func, AsyncFunction):
|
153
|
-
result = func(*evaluated_args, **kwargs)
|
154
|
-
if not is_await_context:
|
155
|
-
result = await result
|
156
|
-
elif isinstance(func, Function):
|
157
|
-
if func.node.name == "__init__":
|
158
|
-
await func(*evaluated_args, **kwargs)
|
159
|
-
return None
|
160
|
-
result = await func(*evaluated_args, **kwargs)
|
161
|
-
elif isinstance(func, AsyncGeneratorFunction):
|
162
|
-
return func(*evaluated_args, **kwargs)
|
163
|
-
else:
|
164
|
-
result = func(*evaluated_args, **kwargs)
|
165
|
-
if asyncio.iscoroutine(result) and not is_await_context:
|
166
|
-
result = await result
|
167
|
-
return result
|
168
|
-
|
169
|
-
async def visit_Await(self: ASTInterpreter, node: ast.Await, wrap_exceptions: bool = True) -> Any:
|
170
|
-
coro = await self.visit(node.value, is_await_context=True, wrap_exceptions=wrap_exceptions)
|
171
|
-
if not asyncio.iscoroutine(coro):
|
172
|
-
raise TypeError(f"Cannot await non-coroutine object: {type(coro)}")
|
173
|
-
|
174
|
-
try:
|
175
|
-
return await asyncio.wait_for(coro, timeout=60)
|
176
|
-
except asyncio.TimeoutError as e:
|
177
|
-
line_info = f"line {node.lineno}" if hasattr(node, "lineno") else "unknown line"
|
178
|
-
context_line = self.source_lines[node.lineno - 1] if self.source_lines and hasattr(node, "lineno") else "<unknown>"
|
179
|
-
error_msg = f"Operation timed out after 60 seconds at {line_info}: {context_line.strip()}"
|
180
|
-
logger_msg = f"Coroutine execution timed out: {error_msg}"
|
181
|
-
self.env_stack[0]["logger"].error(logger_msg)
|
182
|
-
|
183
|
-
if wrap_exceptions:
|
184
|
-
col = getattr(node, "col_offset", 0)
|
185
|
-
raise WrappedException(error_msg, e, node.lineno if hasattr(node, "lineno") else 0, col, context_line)
|
186
|
-
else:
|
187
|
-
raise asyncio.TimeoutError(error_msg) from e
|
188
|
-
|
189
|
-
async def visit_Lambda(self: ASTInterpreter, node: ast.Lambda, wrap_exceptions: bool = True) -> Any:
|
190
|
-
closure: List[Dict[str, Any]] = self.env_stack[:]
|
191
|
-
pos_kw_params = [arg.arg for arg in node.args.args]
|
192
|
-
vararg_name = node.args.vararg.arg if node.args.vararg else None
|
193
|
-
kwonly_params = [arg.arg for arg in node.args.kwonlyargs]
|
194
|
-
kwarg_name = node.args.kwarg.arg if node.args.kwarg else None
|
195
|
-
pos_defaults_values = [await self.visit(default, wrap_exceptions=True) for default in node.args.defaults]
|
196
|
-
num_pos_defaults = len(pos_defaults_values)
|
197
|
-
pos_defaults = dict(zip(pos_kw_params[-num_pos_defaults:], pos_defaults_values)) if num_pos_defaults else {}
|
198
|
-
kw_defaults_values = [await self.visit(default, wrap_exceptions=True) if default else None for default in node.args.kw_defaults]
|
199
|
-
kw_defaults = dict(zip(kwonly_params, kw_defaults_values))
|
200
|
-
kw_defaults = {k: v for k, v in kw_defaults.items() if v is not None}
|
201
|
-
|
202
|
-
lambda_func = LambdaFunction(
|
203
|
-
node, closure, self, pos_kw_params, vararg_name, kwonly_params, kwarg_name, pos_defaults, kw_defaults
|
204
|
-
)
|
205
|
-
|
206
|
-
async def lambda_wrapper(*args, **kwargs):
|
207
|
-
return await lambda_func(*args, **kwargs)
|
208
|
-
|
209
|
-
return lambda_wrapper
|
@@ -1,28 +0,0 @@
|
|
1
|
-
import ast
|
2
|
-
from typing import Any
|
3
|
-
|
4
|
-
from .interpreter_core import ASTInterpreter
|
5
|
-
|
6
|
-
async def visit_Import(self: ASTInterpreter, node: ast.Import, wrap_exceptions: bool = True) -> None:
|
7
|
-
for alias in node.names:
|
8
|
-
module_name: str = alias.name
|
9
|
-
asname: str = alias.asname if alias.asname is not None else module_name
|
10
|
-
if module_name not in self.allowed_modules:
|
11
|
-
raise Exception(
|
12
|
-
f"Import Error: Module '{module_name}' is not allowed. Only {self.allowed_modules} are permitted."
|
13
|
-
)
|
14
|
-
self.set_variable(asname, self.modules[module_name])
|
15
|
-
|
16
|
-
async def visit_ImportFrom(self: ASTInterpreter, node: ast.ImportFrom, wrap_exceptions: bool = True) -> None:
|
17
|
-
if not node.module:
|
18
|
-
raise Exception("Import Error: Missing module name in 'from ... import ...' statement")
|
19
|
-
if node.module not in self.allowed_modules:
|
20
|
-
raise Exception(
|
21
|
-
f"Import Error: Module '{node.module}' is not allowed. Only {self.allowed_modules} are permitted."
|
22
|
-
)
|
23
|
-
for alias in node.names:
|
24
|
-
if alias.name == "*":
|
25
|
-
raise Exception("Import Error: 'from ... import *' is not supported.")
|
26
|
-
asname = alias.asname if alias.asname else alias.name
|
27
|
-
attr = getattr(self.modules[node.module], alias.name)
|
28
|
-
self.set_variable(asname, attr)
|