flock-core 0.2.4__py3-none-any.whl → 0.2.6__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.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

@@ -0,0 +1,675 @@
1
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
2
+ # Licensed under the Apache License, Version 2.0 (the “License”);
3
+ # you may not use this file except in compliance with the License.
4
+ # You may obtain a copy of the License at
5
+ #
6
+ # http://www.apache.org/licenses/LICENSE-2.0
7
+ #
8
+ # Unless required by applicable law or agreed to in writing, software
9
+ # distributed under the License is distributed on an “AS IS” BASIS,
10
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11
+ # See the License for the specific language governing permissions and
12
+ # limitations under the License.
13
+ # =========== Copyright 2023 @ CAMEL-AI.org. All Rights Reserved. ===========
14
+ import ast
15
+ import builtins
16
+ import difflib
17
+ import importlib
18
+ import re
19
+ import typing
20
+ from collections.abc import Mapping
21
+ from typing import (
22
+ Any,
23
+ )
24
+
25
+
26
+ class InterpreterError(ValueError):
27
+ r"""An error raised when the interpreter cannot evaluate a Python
28
+ expression, due to syntax error or unsupported operations.
29
+ """
30
+
31
+ pass
32
+
33
+
34
+ class PythonInterpreter:
35
+ r"""A customized python interpreter to control the execution of
36
+ LLM-generated codes. The interpreter makes sure the code can only execute
37
+ functions given in action space and import white list. It also supports
38
+ fuzzy variable matching to receive uncertain input variable name.
39
+
40
+ [Documentation omitted for brevity]
41
+
42
+ Args:
43
+ action_space (Dict[str, Any]): A dictionary mapping action names to
44
+ their corresponding functions or objects.
45
+ import_white_list (Optional[List[str]], optional): A list of allowed modules.
46
+ verbose (bool, optional): If True, the interpreter prints log messages
47
+ as it executes the code. (default: False)
48
+ """
49
+
50
+ def __init__(
51
+ self,
52
+ action_space: dict[str, Any],
53
+ import_white_list: list[str] | None = None,
54
+ verbose: bool = False,
55
+ ) -> None:
56
+ self.action_space = action_space
57
+ self.state = self.action_space.copy()
58
+ self.fuzz_state: dict[str, Any] = {}
59
+ self.import_white_list = import_white_list or [
60
+ "math",
61
+ "random",
62
+ "datetime",
63
+ "time",
64
+ "string",
65
+ "collections",
66
+ "itertools",
67
+ "functools",
68
+ "typing",
69
+ "enum",
70
+ "json",
71
+ "ast",
72
+ ] # default imports
73
+ self.verbose = verbose
74
+
75
+ def log(self, message: str) -> None:
76
+ """Print a log message immediately."""
77
+ print(message, flush=True)
78
+
79
+ def execute(
80
+ self,
81
+ code: str,
82
+ state: dict[str, Any] | None = None,
83
+ fuzz_state: dict[str, Any] | None = None,
84
+ keep_state: bool = True,
85
+ ) -> Any:
86
+ r"""Execute the input python codes in a secure environment.
87
+
88
+ [Documentation omitted for brevity]
89
+ """
90
+ if state is not None:
91
+ self.state.update(state)
92
+ if fuzz_state is not None:
93
+ self.fuzz_state.update(fuzz_state)
94
+
95
+ try:
96
+ expression = ast.parse(code)
97
+ except SyntaxError as e:
98
+ error_line = code.splitlines()[e.lineno - 1]
99
+ raise InterpreterError(
100
+ f"Syntax error in code at line {e.lineno}: {error_line}\nError: {e}"
101
+ )
102
+
103
+ result = None
104
+ if self.verbose:
105
+ self.log("[Interpreter] Starting code execution...")
106
+
107
+ for idx, node in enumerate(expression.body):
108
+ # Log the AST node being executed (using unparse if available)
109
+ if self.verbose:
110
+ try:
111
+ node_repr = ast.unparse(node)
112
+ except Exception:
113
+ node_repr = ast.dump(node)
114
+ self.log(f"[Interpreter] Executing node {idx}: {node_repr}")
115
+
116
+ try:
117
+ line_result = self._execute_ast(node)
118
+ except InterpreterError as e:
119
+ if not keep_state:
120
+ self.clear_state()
121
+ msg = f"Evaluation of the code stopped at node {idx}. See:\n{e}"
122
+ raise InterpreterError(msg)
123
+ if line_result is not None:
124
+ result = line_result
125
+ if self.verbose:
126
+ self.log(f"[Interpreter] Node {idx} result: {result}")
127
+
128
+ if self.verbose:
129
+ self.log("[Interpreter] Finished code execution.")
130
+ if not keep_state:
131
+ self.clear_state()
132
+
133
+ return result
134
+
135
+ def clear_state(self) -> None:
136
+ r"""Initialize :obj:`state` and :obj:`fuzz_state`"""
137
+ self.state = self.action_space.copy()
138
+ self.fuzz_state = {}
139
+
140
+ # ast.Index is deprecated after python 3.9, which cannot pass type check,
141
+ # but is still necessary for older versions.
142
+ @typing.no_type_check
143
+ def _execute_ast(self, expression: ast.AST) -> Any:
144
+ if isinstance(expression, ast.Assign):
145
+ return self._execute_assign(expression)
146
+ elif isinstance(expression, ast.Attribute):
147
+ value = self._execute_ast(expression.value)
148
+ return getattr(value, expression.attr)
149
+ elif isinstance(expression, ast.AugAssign):
150
+ return self._execute_augassign(expression)
151
+ elif isinstance(expression, ast.BinOp):
152
+ return self._execute_binop(expression)
153
+ elif isinstance(expression, ast.BoolOp):
154
+ return self._execute_condition(expression)
155
+ elif isinstance(expression, ast.Call):
156
+ return self._execute_call(expression)
157
+ elif isinstance(expression, ast.Compare):
158
+ return self._execute_condition(expression)
159
+ elif isinstance(expression, ast.Constant):
160
+ return expression.value
161
+ elif isinstance(expression, ast.Dict):
162
+ result: dict = {}
163
+ for k, v in zip(expression.keys, expression.values):
164
+ if k is not None:
165
+ result[self._execute_ast(k)] = self._execute_ast(v)
166
+ else:
167
+ result.update(self._execute_ast(v))
168
+ return result
169
+ elif isinstance(expression, ast.Expr):
170
+ return self._execute_ast(expression.value)
171
+ elif isinstance(expression, ast.For):
172
+ return self._execute_for(expression)
173
+ elif isinstance(expression, ast.FormattedValue):
174
+ return self._execute_ast(expression.value)
175
+ elif isinstance(expression, ast.FunctionDef):
176
+ self.state[expression.name] = expression
177
+ return None
178
+ elif isinstance(expression, ast.GeneratorExp):
179
+ return self._execute_generatorexp(expression)
180
+ elif isinstance(expression, ast.If):
181
+ return self._execute_if(expression)
182
+ elif isinstance(expression, ast.IfExp):
183
+ return self._execute_ifexp(expression)
184
+ elif isinstance(expression, ast.Import):
185
+ self._execute_import(expression)
186
+ return None
187
+ elif isinstance(expression, ast.ImportFrom):
188
+ self._execute_import_from(expression)
189
+ return None
190
+ elif hasattr(ast, "Index") and isinstance(expression, ast.Index):
191
+ return self._execute_ast(expression.value)
192
+ elif isinstance(expression, ast.JoinedStr):
193
+ return "".join(
194
+ [str(self._execute_ast(v)) for v in expression.values]
195
+ )
196
+ elif isinstance(expression, ast.Lambda):
197
+ return self._execute_lambda(expression)
198
+ elif isinstance(expression, ast.List):
199
+ return [self._execute_ast(elt) for elt in expression.elts]
200
+ elif isinstance(expression, ast.Name):
201
+ return self._execute_name(expression)
202
+ elif isinstance(expression, ast.Return):
203
+ return self._execute_ast(expression.value)
204
+ elif isinstance(expression, ast.Subscript):
205
+ return self._execute_subscript(expression)
206
+ elif isinstance(expression, ast.Tuple):
207
+ return tuple([self._execute_ast(elt) for elt in expression.elts])
208
+ elif isinstance(expression, ast.UnaryOp):
209
+ return self._execute_unaryop(expression)
210
+ elif isinstance(expression, ast.While):
211
+ return self._execute_while(expression)
212
+ elif isinstance(expression, ast.ListComp):
213
+ return self._execute_listcomp(expression)
214
+ elif isinstance(expression, ast.DictComp):
215
+ return self._execute_dictcomp(expression)
216
+ elif isinstance(expression, ast.SetComp):
217
+ return self._execute_setcomp(expression)
218
+ elif isinstance(expression, ast.Break):
219
+ raise BreakException()
220
+ elif isinstance(expression, ast.Continue):
221
+ raise ContinueException()
222
+ elif isinstance(expression, ast.Try):
223
+ return self._execute_try(expression)
224
+ elif isinstance(expression, ast.Raise):
225
+ return self._execute_raise(expression)
226
+ elif isinstance(expression, ast.Pass):
227
+ return None
228
+ elif isinstance(expression, ast.Assert):
229
+ return self._execute_assert(expression)
230
+ else:
231
+ raise InterpreterError(
232
+ f"{expression.__class__.__name__} is not supported."
233
+ )
234
+
235
+ def _execute_assign(self, assign: ast.Assign) -> Any:
236
+ targets = assign.targets
237
+ result = self._execute_ast(assign.value)
238
+
239
+ for target in targets:
240
+ self._assign(target, result)
241
+ return result
242
+
243
+ def _assign(self, target: ast.expr, value: Any):
244
+ if isinstance(target, ast.Name):
245
+ self.state[target.id] = value
246
+ elif isinstance(target, ast.Tuple):
247
+ if not isinstance(value, tuple):
248
+ raise InterpreterError(
249
+ f"Expected type tuple, but got {value.__class__.__name__} instead."
250
+ )
251
+ if len(target.elts) != len(value):
252
+ raise InterpreterError(
253
+ f"Expected {len(target.elts)} values but got {len(value)}."
254
+ )
255
+ for t, v in zip(target.elts, value):
256
+ self.state[self._execute_ast(t)] = v
257
+ else:
258
+ raise InterpreterError(
259
+ f"Unsupported variable type. Expected ast.Name or ast.Tuple, got {target.__class__.__name__} instead."
260
+ )
261
+
262
+ def _execute_call(self, call: ast.Call) -> Any:
263
+ callable_func = self._execute_ast(call.func)
264
+
265
+ args = [self._execute_ast(arg) for arg in call.args]
266
+ kwargs = {
267
+ keyword.arg: self._execute_ast(keyword.value)
268
+ for keyword in call.keywords
269
+ }
270
+ if isinstance(callable_func, ast.FunctionDef):
271
+ old_state = self.state.copy()
272
+ for param_name, arg_value in zip(
273
+ [param.arg for param in callable_func.args.args], args
274
+ ):
275
+ self.state[param_name] = arg_value
276
+ result = None
277
+ for stmt in callable_func.body:
278
+ result = self._execute_ast(stmt)
279
+ if isinstance(stmt, ast.Return):
280
+ break
281
+ self.state = old_state
282
+ return result
283
+ return callable_func(*args, **kwargs)
284
+
285
+ def _execute_augassign(self, augassign: ast.AugAssign):
286
+ current_value = self.state[augassign.target.id]
287
+ increment_value = self._execute_ast(augassign.value)
288
+ if not (
289
+ isinstance(current_value, (int, float))
290
+ and isinstance(increment_value, (int, float))
291
+ ):
292
+ raise InterpreterError(
293
+ f"Invalid types for augmented assignment: {type(current_value)}, {type(increment_value)}"
294
+ )
295
+ if isinstance(augassign.op, ast.Add):
296
+ new_value = current_value + increment_value
297
+ elif isinstance(augassign.op, ast.Sub):
298
+ new_value = current_value - increment_value
299
+ elif isinstance(augassign.op, ast.Mult):
300
+ new_value = current_value * increment_value
301
+ elif isinstance(augassign.op, ast.Div):
302
+ new_value = current_value / increment_value
303
+ else:
304
+ raise InterpreterError(
305
+ f"Augmented assignment operator {augassign.op} is not supported"
306
+ )
307
+ self._assign(augassign.target, new_value)
308
+ return new_value
309
+
310
+ def _execute_subscript(self, subscript: ast.Subscript):
311
+ index = self._execute_ast(subscript.slice)
312
+ value = self._execute_ast(subscript.value)
313
+ if not isinstance(subscript.ctx, ast.Load):
314
+ raise InterpreterError(
315
+ f"{subscript.ctx.__class__.__name__} is not supported for subscript."
316
+ )
317
+ if isinstance(value, (list, tuple)):
318
+ return value[int(index)]
319
+ if index in value:
320
+ return value[index]
321
+ if isinstance(index, str) and isinstance(value, Mapping):
322
+ close_matches = difflib.get_close_matches(index, list(value.keys()))
323
+ if len(close_matches) > 0:
324
+ return value[close_matches[0]]
325
+ raise InterpreterError(f"Could not index {value} with '{index}'.")
326
+
327
+ def _execute_name(self, name: ast.Name):
328
+ if name.id in dir(builtins):
329
+ return getattr(builtins, name.id)
330
+ if isinstance(name.ctx, ast.Store):
331
+ return name.id
332
+ elif isinstance(name.ctx, ast.Load):
333
+ return self._get_value_from_state(name.id)
334
+ else:
335
+ raise InterpreterError(f"{name.ctx} is not supported.")
336
+
337
+ def _execute_condition(self, condition):
338
+ if isinstance(condition, ast.BoolOp):
339
+ if isinstance(condition.op, ast.And):
340
+ results = [
341
+ self._execute_ast(value) for value in condition.values
342
+ ]
343
+ return all(results)
344
+ elif isinstance(condition.op, ast.Or):
345
+ results = [
346
+ self._execute_ast(value) for value in condition.values
347
+ ]
348
+ return any(results)
349
+ else:
350
+ raise InterpreterError(
351
+ f"Boolean operator {condition.op} is not supported"
352
+ )
353
+ elif isinstance(condition, ast.Compare):
354
+ if len(condition.ops) > 1:
355
+ raise InterpreterError(
356
+ "Cannot evaluate conditions with multiple operators"
357
+ )
358
+ left = self._execute_ast(condition.left)
359
+ comparator = condition.ops[0]
360
+ right = self._execute_ast(condition.comparators[0])
361
+ if isinstance(comparator, ast.Eq):
362
+ return left == right
363
+ elif isinstance(comparator, ast.NotEq):
364
+ return left != right
365
+ elif isinstance(comparator, ast.Lt):
366
+ return left < right
367
+ elif isinstance(comparator, ast.LtE):
368
+ return left <= right
369
+ elif isinstance(comparator, ast.Gt):
370
+ return left > right
371
+ elif isinstance(comparator, ast.GtE):
372
+ return left >= right
373
+ elif isinstance(comparator, ast.Is):
374
+ return left is right
375
+ elif isinstance(comparator, ast.IsNot):
376
+ return left is not right
377
+ elif isinstance(comparator, ast.In):
378
+ return left in right
379
+ elif isinstance(comparator, ast.NotIn):
380
+ return left not in right
381
+ else:
382
+ raise InterpreterError("Unsupported comparison operator")
383
+ elif isinstance(condition, ast.UnaryOp):
384
+ return self._execute_unaryop(condition)
385
+ elif isinstance(condition, ast.Name) or isinstance(condition, ast.Call):
386
+ return bool(self._execute_ast(condition))
387
+ elif isinstance(condition, ast.Constant):
388
+ return bool(condition.value)
389
+ else:
390
+ raise InterpreterError(
391
+ f"Unsupported condition type: {type(condition).__name__}"
392
+ )
393
+
394
+ def _execute_if(self, if_statement: ast.If):
395
+ result = None
396
+ if self._execute_condition(if_statement.test):
397
+ for line in if_statement.body:
398
+ line_result = self._execute_ast(line)
399
+ if line_result is not None:
400
+ result = line_result
401
+ else:
402
+ for line in if_statement.orelse:
403
+ line_result = self._execute_ast(line)
404
+ if line_result is not None:
405
+ result = line_result
406
+ return result
407
+
408
+ def _execute_ifexp(self, ifexp: ast.IfExp) -> Any:
409
+ test_result = self._execute_condition(ifexp.test)
410
+ if test_result:
411
+ return self._execute_ast(ifexp.body)
412
+ else:
413
+ return self._execute_ast(ifexp.orelse)
414
+
415
+ def _execute_import(self, import_module: ast.Import) -> None:
416
+ for module in import_module.names:
417
+ self._validate_import(module.name)
418
+ alias = module.asname or module.name
419
+ self.state[alias] = importlib.import_module(module.name)
420
+
421
+ def _execute_import_from(self, import_from: ast.ImportFrom):
422
+ if import_from.module is None:
423
+ raise InterpreterError('"from . import" is not supported.')
424
+ for import_name in import_from.names:
425
+ full_name = import_from.module + f".{import_name.name}"
426
+ self._validate_import(full_name)
427
+ imported_module = importlib.import_module(import_from.module)
428
+ alias = import_name.asname or import_name.name
429
+ self.state[alias] = getattr(imported_module, import_name.name)
430
+
431
+ # Note: Two versions of _execute_for and _execute_while appear in this file.
432
+ # We keep both as provided, but you may wish to consolidate these in your code.
433
+
434
+ def _execute_for(self, for_statement: ast.For):
435
+ class BreakException(Exception):
436
+ pass
437
+
438
+ class ContinueException(Exception):
439
+ pass
440
+
441
+ result = None
442
+ try:
443
+ for value in self._execute_ast(for_statement.iter):
444
+ self._assign(for_statement.target, value)
445
+ try:
446
+ for line in for_statement.body:
447
+ line_result = self._execute_ast(line)
448
+ if line_result is not None:
449
+ result = line_result
450
+ except ContinueException:
451
+ continue
452
+ except BreakException:
453
+ pass
454
+ return result
455
+
456
+ def _execute_while(self, while_statement: ast.While):
457
+ class BreakException(Exception):
458
+ pass
459
+
460
+ class ContinueException(Exception):
461
+ pass
462
+
463
+ result = None
464
+ try:
465
+ while self._execute_condition(while_statement.test):
466
+ try:
467
+ for line in while_statement.body:
468
+ line_result = self._execute_ast(line)
469
+ if line_result is not None:
470
+ result = line_result
471
+ except ContinueException:
472
+ continue
473
+ except BreakException:
474
+ pass
475
+ return result
476
+
477
+ def _execute_try(self, try_statement: ast.Try):
478
+ try:
479
+ for line in try_statement.body:
480
+ self._execute_ast(line)
481
+ except Exception as e:
482
+ handled = False
483
+ for handler in try_statement.handlers:
484
+ if handler.type is None or isinstance(
485
+ e, self._execute_ast(handler.type)
486
+ ):
487
+ if handler.name:
488
+ self.state[handler.name.id] = e
489
+ for line in handler.body:
490
+ self._execute_ast(line)
491
+ handled = True
492
+ break
493
+ if not handled:
494
+ raise
495
+ finally:
496
+ for line in try_statement.finalbody:
497
+ self._execute_ast(line)
498
+
499
+ def _execute_raise(self, raise_statement: ast.Raise):
500
+ if raise_statement.exc:
501
+ exception = self._execute_ast(raise_statement.exc)
502
+ raise exception
503
+ else:
504
+ raise
505
+
506
+ def _execute_assert(self, assert_statement: ast.Assert):
507
+ test_result = self._execute_condition(assert_statement.test)
508
+ if not test_result:
509
+ if assert_statement.msg:
510
+ msg = self._execute_ast(assert_statement.msg)
511
+ raise AssertionError(msg)
512
+ else:
513
+ raise AssertionError
514
+
515
+ def _execute_lambda(self, lambda_node: ast.Lambda) -> Any:
516
+ def lambda_function(*args):
517
+ old_state = self.state.copy()
518
+ for param, arg in zip(lambda_node.args.args, args):
519
+ self.state[param.arg] = arg
520
+ result = self._execute_ast(lambda_node.body)
521
+ self.state = old_state # Restore the state
522
+ return result
523
+
524
+ return lambda_function
525
+
526
+ def _validate_import(self, full_name: str):
527
+ tmp_name = ""
528
+ found_name = False
529
+ for name in full_name.split("."):
530
+ tmp_name += name if tmp_name == "" else f".{name}"
531
+ if tmp_name in self.import_white_list:
532
+ found_name = True
533
+ return
534
+ if not found_name:
535
+ raise InterpreterError(
536
+ f"It is not permitted to import modules "
537
+ f"than module white list (try to import {full_name})."
538
+ )
539
+
540
+ def _execute_binop(self, binop: ast.BinOp):
541
+ left = self._execute_ast(binop.left)
542
+ operator = binop.op
543
+ right = self._execute_ast(binop.right)
544
+
545
+ if isinstance(operator, ast.Add):
546
+ return left + right
547
+ elif isinstance(operator, ast.Sub):
548
+ return left - right
549
+ elif isinstance(operator, ast.Mult):
550
+ return left * right
551
+ elif isinstance(operator, ast.Div):
552
+ return left / right
553
+ elif isinstance(operator, ast.FloorDiv):
554
+ return left // right
555
+ elif isinstance(operator, ast.Mod):
556
+ return left % right
557
+ elif isinstance(operator, ast.Pow):
558
+ return left**right
559
+ elif isinstance(operator, ast.LShift):
560
+ return left << right
561
+ elif isinstance(operator, ast.RShift):
562
+ return left >> right
563
+ elif isinstance(operator, ast.BitAnd):
564
+ return left & right
565
+ elif isinstance(operator, ast.BitOr):
566
+ return left | right
567
+ elif isinstance(operator, ast.BitXor):
568
+ return left ^ right
569
+ elif isinstance(operator, ast.MatMult):
570
+ return left @ right
571
+ else:
572
+ raise InterpreterError(f"Operator not supported: {operator}")
573
+
574
+ def _execute_unaryop(self, unaryop: ast.UnaryOp):
575
+ operand = self._execute_ast(unaryop.operand)
576
+ operator = unaryop.op
577
+
578
+ if isinstance(operator, ast.UAdd):
579
+ return +operand
580
+ elif isinstance(operator, ast.USub):
581
+ return -operand
582
+ elif isinstance(operator, ast.Not):
583
+ return not operand
584
+ elif isinstance(operator, ast.Invert):
585
+ return ~operand
586
+ else:
587
+ raise InterpreterError(f"Operator not supported: {operator}")
588
+
589
+ def _execute_listcomp(self, comp: ast.ListComp):
590
+ return [self._execute_comp(comp.elt, comp.generators)]
591
+
592
+ def _execute_dictcomp(self, comp: ast.DictComp):
593
+ return {
594
+ self._execute_comp(comp.key, comp.generators): self._execute_comp(
595
+ comp.value, comp.generators
596
+ )
597
+ }
598
+
599
+ def _execute_setcomp(self, comp: ast.SetComp):
600
+ return {self._execute_comp(comp.elt, comp.generators)}
601
+
602
+ def _execute_comp(self, elt, generators):
603
+ if not generators:
604
+ return self._execute_ast(elt)
605
+ gen = generators[0]
606
+ result = []
607
+ for value in self._execute_ast(gen.iter):
608
+ self._assign(gen.target, value)
609
+ if all(self._execute_condition(if_cond) for if_cond in gen.ifs):
610
+ result.extend(self._execute_comp(elt, generators[1:]))
611
+ return result
612
+
613
+ def _execute_generatorexp(self, genexp: ast.GeneratorExp):
614
+ def generator():
615
+ for value in self._execute_comp(genexp.elt, genexp.generators):
616
+ yield value
617
+
618
+ return generator()
619
+
620
+ def _get_value_from_state(self, key: str) -> Any:
621
+ if key in self.state:
622
+ return self.state[key]
623
+ elif key in self.fuzz_state:
624
+ return self.fuzz_state[key]
625
+ else:
626
+ raise InterpreterError(f"The variable `{key}` is not defined.")
627
+
628
+
629
+ class TextPrompt(str):
630
+ r"""A class that represents a text prompt. The :obj:`TextPrompt` class
631
+ extends the built-in :obj:`str` class to provide a property for retrieving
632
+ the set of keywords in the prompt.
633
+ """
634
+
635
+ @property
636
+ def key_words(self) -> set[str]:
637
+ pattern = re.compile(r"\{([^{}]+)\}")
638
+ found = pattern.findall(self)
639
+ return set(found)
640
+
641
+ def format(self, *args: Any, **kwargs: Any) -> "TextPrompt":
642
+ default_kwargs = {key: "{" + f"{key}" + "}" for key in self.key_words}
643
+ default_kwargs.update(kwargs)
644
+ return TextPrompt(super().format(*args, **default_kwargs))
645
+
646
+
647
+ class CodePrompt(TextPrompt):
648
+ r"""A class that represents a code prompt. It extends the :obj:`TextPrompt`
649
+ class with a :obj:`code_type` property.
650
+ """
651
+
652
+ def __new__(cls, *args: Any, **kwargs: Any) -> "CodePrompt":
653
+ code_type = kwargs.pop("code_type", None)
654
+ instance = super().__new__(cls, *args, **kwargs)
655
+ instance._code_type = code_type
656
+ return instance
657
+
658
+ @property
659
+ def code_type(self) -> str | None:
660
+ return self._code_type
661
+
662
+ def set_code_type(self, code_type: str) -> None:
663
+ self._code_type = code_type
664
+
665
+ def execute(
666
+ self,
667
+ interpreter: PythonInterpreter | None = None,
668
+ user_variable: dict[str, Any] | None = None,
669
+ ) -> tuple[Any, PythonInterpreter]:
670
+ if not interpreter:
671
+ interpreter = PythonInterpreter(action_space=globals())
672
+ execution_res = interpreter.execute(
673
+ self, fuzz_state=user_variable, keep_state=True
674
+ )
675
+ return execution_res, interpreter