langfun 0.0.2.dev20240330__py3-none-any.whl → 0.1.2.dev202501140804__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.
- langfun/__init__.py +22 -2
- langfun/core/__init__.py +17 -5
- langfun/core/agentic/__init__.py +30 -0
- langfun/core/agentic/action.py +854 -0
- langfun/core/agentic/action_eval.py +150 -0
- langfun/core/agentic/action_eval_test.py +109 -0
- langfun/core/agentic/action_test.py +136 -0
- langfun/core/coding/python/__init__.py +5 -11
- langfun/core/coding/python/correction.py +37 -28
- langfun/core/coding/python/correction_test.py +29 -3
- langfun/core/coding/python/execution.py +40 -216
- langfun/core/coding/python/execution_test.py +29 -89
- langfun/core/coding/python/generation.py +21 -11
- langfun/core/coding/python/generation_test.py +2 -2
- langfun/core/coding/python/parsing.py +108 -193
- langfun/core/coding/python/parsing_test.py +2 -105
- langfun/core/component.py +69 -2
- langfun/core/component_test.py +54 -0
- langfun/core/concurrent.py +414 -117
- langfun/core/concurrent_test.py +111 -24
- langfun/core/console.py +18 -5
- langfun/core/console_test.py +17 -0
- langfun/core/eval/__init__.py +17 -0
- langfun/core/eval/base.py +767 -140
- langfun/core/eval/base_test.py +238 -53
- langfun/core/eval/matching.py +80 -76
- langfun/core/eval/matching_test.py +19 -9
- langfun/core/eval/patching.py +130 -0
- langfun/core/eval/patching_test.py +170 -0
- langfun/core/eval/scoring.py +37 -28
- langfun/core/eval/scoring_test.py +21 -3
- langfun/core/eval/v2/__init__.py +42 -0
- langfun/core/eval/v2/checkpointing.py +380 -0
- langfun/core/eval/v2/checkpointing_test.py +228 -0
- langfun/core/eval/v2/eval_test_helper.py +136 -0
- langfun/core/eval/v2/evaluation.py +725 -0
- langfun/core/eval/v2/evaluation_test.py +180 -0
- langfun/core/eval/v2/example.py +305 -0
- langfun/core/eval/v2/example_test.py +128 -0
- langfun/core/eval/v2/experiment.py +1048 -0
- langfun/core/eval/v2/experiment_test.py +433 -0
- langfun/core/eval/v2/metric_values.py +156 -0
- langfun/core/eval/v2/metric_values_test.py +80 -0
- langfun/core/eval/v2/metrics.py +357 -0
- langfun/core/eval/v2/metrics_test.py +203 -0
- langfun/core/eval/v2/progress.py +348 -0
- langfun/core/eval/v2/progress_test.py +82 -0
- langfun/core/eval/v2/progress_tracking.py +210 -0
- langfun/core/eval/v2/progress_tracking_test.py +66 -0
- langfun/core/eval/v2/reporting.py +270 -0
- langfun/core/eval/v2/reporting_test.py +158 -0
- langfun/core/eval/v2/runners.py +488 -0
- langfun/core/eval/v2/runners_test.py +334 -0
- langfun/core/langfunc.py +3 -21
- langfun/core/langfunc_test.py +26 -8
- langfun/core/language_model.py +686 -48
- langfun/core/language_model_test.py +681 -44
- langfun/core/llms/__init__.py +100 -12
- langfun/core/llms/anthropic.py +488 -0
- langfun/core/llms/anthropic_test.py +235 -0
- langfun/core/llms/cache/base.py +21 -2
- langfun/core/llms/cache/in_memory.py +13 -0
- langfun/core/llms/cache/in_memory_test.py +88 -28
- langfun/core/llms/compositional.py +101 -0
- langfun/core/llms/compositional_test.py +73 -0
- langfun/core/llms/deepseek.py +117 -0
- langfun/core/llms/deepseek_test.py +61 -0
- langfun/core/llms/fake.py +39 -26
- langfun/core/llms/fake_test.py +136 -11
- langfun/core/llms/gemini.py +507 -0
- langfun/core/llms/gemini_test.py +195 -0
- langfun/core/llms/google_genai.py +62 -218
- langfun/core/llms/google_genai_test.py +9 -197
- langfun/core/llms/groq.py +276 -0
- langfun/core/llms/groq_test.py +64 -0
- langfun/core/llms/llama_cpp.py +15 -40
- langfun/core/llms/llama_cpp_test.py +4 -30
- langfun/core/llms/openai.py +436 -226
- langfun/core/llms/openai_compatible.py +179 -0
- langfun/core/llms/openai_compatible_test.py +495 -0
- langfun/core/llms/openai_test.py +35 -174
- langfun/core/llms/rest.py +113 -0
- langfun/core/llms/rest_test.py +111 -0
- langfun/core/llms/vertexai.py +192 -0
- langfun/core/llms/vertexai_test.py +52 -0
- langfun/core/logging.py +284 -0
- langfun/core/logging_test.py +125 -0
- langfun/core/message.py +319 -9
- langfun/core/message_test.py +190 -13
- langfun/core/modalities/__init__.py +6 -2
- langfun/core/modalities/audio.py +30 -0
- langfun/core/modalities/audio_test.py +63 -0
- langfun/core/modalities/image.py +39 -20
- langfun/core/modalities/image_test.py +52 -9
- langfun/core/modalities/mime.py +206 -29
- langfun/core/modalities/mime_test.py +90 -9
- langfun/core/modalities/ms_office.py +117 -0
- langfun/core/modalities/ms_office_test.py +389 -0
- langfun/core/modalities/pdf.py +22 -0
- langfun/core/modalities/pdf_test.py +57 -0
- langfun/core/modalities/video.py +9 -23
- langfun/core/modalities/video_test.py +3 -3
- langfun/core/modality.py +26 -3
- langfun/core/modality_test.py +2 -2
- langfun/core/sampling.py +11 -11
- langfun/core/structured/__init__.py +15 -16
- langfun/core/structured/completion.py +32 -5
- langfun/core/structured/completion_test.py +9 -8
- langfun/core/structured/description.py +2 -2
- langfun/core/structured/description_test.py +3 -3
- langfun/core/structured/function_generation.py +278 -0
- langfun/core/structured/function_generation_test.py +399 -0
- langfun/core/structured/mapping.py +150 -46
- langfun/core/structured/mapping_test.py +105 -0
- langfun/core/structured/parsing.py +33 -21
- langfun/core/structured/parsing_test.py +71 -22
- langfun/core/structured/querying.py +746 -0
- langfun/core/structured/{prompting_test.py → querying_test.py} +545 -60
- langfun/core/structured/schema.py +208 -99
- langfun/core/structured/schema_generation.py +1 -1
- langfun/core/structured/schema_generation_test.py +2 -2
- langfun/core/structured/schema_test.py +133 -34
- langfun/core/structured/scoring.py +125 -19
- langfun/core/structured/scoring_test.py +30 -0
- langfun/core/structured/tokenization.py +64 -0
- langfun/core/structured/tokenization_test.py +48 -0
- langfun/core/template.py +240 -11
- langfun/core/template_test.py +146 -1
- langfun/core/templates/conversation.py +9 -0
- langfun/core/templates/conversation_test.py +4 -3
- langfun/core/templates/selfplay_test.py +14 -2
- langfun-0.1.2.dev202501140804.dist-info/METADATA +225 -0
- langfun-0.1.2.dev202501140804.dist-info/RECORD +153 -0
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/WHEEL +1 -1
- langfun/core/coding/python/errors.py +0 -108
- langfun/core/coding/python/errors_test.py +0 -99
- langfun/core/coding/python/permissions.py +0 -90
- langfun/core/coding/python/permissions_test.py +0 -86
- langfun/core/structured/prompting.py +0 -217
- langfun/core/text_formatting.py +0 -162
- langfun/core/text_formatting_test.py +0 -47
- langfun-0.0.2.dev20240330.dist-info/METADATA +0 -99
- langfun-0.0.2.dev20240330.dist-info/RECORD +0 -102
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/LICENSE +0 -0
- {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/top_level.txt +0 -0
@@ -13,50 +13,24 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
"""Python code execution."""
|
15
15
|
|
16
|
-
import
|
17
|
-
import contextlib
|
18
|
-
import io
|
19
|
-
import multiprocessing
|
20
|
-
from typing import Any, Callable
|
16
|
+
from typing import Any
|
21
17
|
|
22
|
-
from langfun.core.coding.python import errors
|
23
18
|
from langfun.core.coding.python import parsing
|
24
|
-
from langfun.core.coding.python import permissions
|
25
19
|
import pyglove as pg
|
26
20
|
|
27
21
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
RESULT_KEY = '__result__'
|
33
|
-
_TLS_CODE_RUN_CONTEXT = '__code_run_context__'
|
34
|
-
|
35
|
-
|
36
|
-
@contextlib.contextmanager
|
37
|
-
def context(**kwargs):
|
38
|
-
"""Context manager to inject symbols for code execution."""
|
39
|
-
ctx = get_context()
|
40
|
-
ctx.update(kwargs)
|
41
|
-
pg.object_utils.thread_local_push(_TLS_CODE_RUN_CONTEXT, ctx)
|
42
|
-
|
43
|
-
try:
|
44
|
-
yield ctx
|
45
|
-
finally:
|
46
|
-
pg.object_utils.thread_local_pop(_TLS_CODE_RUN_CONTEXT)
|
47
|
-
|
48
|
-
|
49
|
-
def get_context() -> dict[str, Any]:
|
50
|
-
"""Gets the current context for code execution."""
|
51
|
-
context_stack = pg.object_utils.thread_local_get(_TLS_CODE_RUN_CONTEXT, None)
|
52
|
-
return dict(context_stack[-1]) if context_stack else {}
|
22
|
+
context = pg.coding.context
|
23
|
+
CodeError = pg.coding.CodeError
|
24
|
+
CodePermission = pg.coding.CodePermission
|
25
|
+
permission = pg.coding.permission
|
53
26
|
|
54
27
|
|
55
28
|
def evaluate(
|
56
29
|
code: str,
|
57
30
|
*,
|
58
31
|
global_vars: dict[str, Any] | None = None,
|
59
|
-
permission:
|
32
|
+
permission: CodePermission | None = None, # pylint: disable=redefined-outer-name
|
33
|
+
returns_stdout: bool = False,
|
60
34
|
outputs_intermediate: bool = False,
|
61
35
|
) -> Any | dict[str, Any]:
|
62
36
|
"""Executes Python code.
|
@@ -71,191 +45,33 @@ def evaluate(
|
|
71
45
|
global_vars: An optional dict as the globals that could be referenced by the
|
72
46
|
code.
|
73
47
|
permission: Permission for the Python code to run.
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
The value of the last line of the code. Or a dict of variable name to
|
80
|
-
their values if `outputs_intermediate` is set to True, with the final result
|
81
|
-
accessible by key '__result__'.
|
82
|
-
"""
|
83
|
-
# Set up the permission and context.
|
84
|
-
permission = permission or permissions.get_permission()
|
85
|
-
ctx = dict(get_context())
|
86
|
-
if global_vars:
|
87
|
-
ctx.update(global_vars)
|
88
|
-
|
89
|
-
# Parse the code str.
|
90
|
-
code, code_block = parsing.PythonCodeParser().parse(code, permission)
|
91
|
-
global_vars, orig_global_vars = ctx, ctx.copy()
|
92
|
-
|
93
|
-
# No code.
|
94
|
-
if not code_block.body:
|
95
|
-
return {} if outputs_intermediate else None
|
96
|
-
|
97
|
-
stdout = io.StringIO()
|
98
|
-
with contextlib.redirect_stdout(stdout):
|
99
|
-
if hasattr(code_block.body[-1], 'value'):
|
100
|
-
last_expr = code_block.body.pop() # pytype: disable=attribute-error
|
101
|
-
result_vars = [RESULT_KEY]
|
102
|
-
|
103
|
-
if isinstance(last_expr, ast.Assign):
|
104
|
-
for name_node in last_expr.targets:
|
105
|
-
result_vars.append(name_node.id)
|
106
|
-
|
107
|
-
last_expr = ast.Expression(last_expr.value) # pytype: disable=attribute-error
|
108
|
-
|
109
|
-
try:
|
110
|
-
# Execute the lines before the last expression.
|
111
|
-
# NOTE(daiyip): Only a `globals` dict is specified here, which will also
|
112
|
-
# be used to output intermediate values by `exec`. We do not specify a
|
113
|
-
# separate `locals` dict here, for - "If exec gets two separate objects
|
114
|
-
# as globals and locals, the code will be executed as if it were
|
115
|
-
# embedded in a class definition." - as the Python document explains.
|
116
|
-
# The outcome is that new functions defined in the code block could not
|
117
|
-
# be called by other newly defined functions.
|
118
|
-
# Refer to https://stackoverflow.com/questions/
|
119
|
-
# 73940751/why-cant-i-call-a-function-from-another-function-using-exec
|
120
|
-
# for more details.
|
121
|
-
exec(compile(code_block, '', mode='exec'), global_vars) # pylint: disable=exec-used
|
122
|
-
|
123
|
-
# Evaluate the last expression.
|
124
|
-
result = eval( # pylint: disable=eval-used
|
125
|
-
compile(last_expr, '', mode='eval'), global_vars
|
126
|
-
)
|
127
|
-
except Exception as e:
|
128
|
-
raise errors.CodeError(code, e) from e
|
129
|
-
|
130
|
-
for result_var in result_vars:
|
131
|
-
global_vars[result_var] = result
|
132
|
-
else:
|
133
|
-
try:
|
134
|
-
exec(compile(code_block, '', mode='exec'), global_vars) # pylint: disable=exec-used
|
135
|
-
except Exception as e:
|
136
|
-
raise errors.CodeError(code, e) from e
|
137
|
-
global_vars[RESULT_KEY] = list(global_vars.values())[-1]
|
138
|
-
|
139
|
-
if outputs_intermediate:
|
140
|
-
outputs = {}
|
141
|
-
for k, v in global_vars.items():
|
142
|
-
if k == '__builtins__':
|
143
|
-
continue
|
144
|
-
if k not in orig_global_vars or v is not orig_global_vars[k]:
|
145
|
-
outputs[k] = v
|
146
|
-
# Add stdout to outputs.
|
147
|
-
outputs[STDOUT_KEY] = stdout.getvalue()
|
148
|
-
return outputs
|
149
|
-
return global_vars[RESULT_KEY]
|
150
|
-
|
151
|
-
|
152
|
-
def sandbox_call(
|
153
|
-
func: Callable[..., Any],
|
154
|
-
*args,
|
155
|
-
timeout: float | None = None,
|
156
|
-
**kwargs) -> Any:
|
157
|
-
"""Calls a function with sandboxing.
|
158
|
-
|
159
|
-
Args:
|
160
|
-
func: Function to call.
|
161
|
-
*args: Positional arguments for `func`
|
162
|
-
timeout: Execution timeout in seconds. If None, wait `func` to complete.
|
163
|
-
**kwargs: Keyword arguments for `func`.
|
164
|
-
|
165
|
-
Returns:
|
166
|
-
Return value from `func`.
|
167
|
-
|
168
|
-
Raises:
|
169
|
-
TimeoutError: If the execution time exceeds the timeout.
|
170
|
-
Exception: Exception raised from `func`.
|
171
|
-
"""
|
172
|
-
def _call(q, *args, **kwargs):
|
173
|
-
# NOTE(daiyip): if `q` is closed by the main process when `q.put` is called
|
174
|
-
# on a subprocess, ValueError will be raised. This is okay since the main
|
175
|
-
# process is no longer waiting for the result, and the subprocess could
|
176
|
-
# recycled with non-zero error code, which does not affect the main
|
177
|
-
# process.
|
178
|
-
def _run():
|
179
|
-
r = func(*args, **kwargs)
|
180
|
-
try:
|
181
|
-
return pg.to_json_str(r)
|
182
|
-
except Exception as e:
|
183
|
-
raise errors.SerializationError(
|
184
|
-
f'Cannot serialize sandbox result: {r}', e
|
185
|
-
) from e
|
186
|
-
|
187
|
-
try:
|
188
|
-
q.put(_run())
|
189
|
-
except Exception as e: # pylint: disable=broad-exception-caught
|
190
|
-
q.put(e)
|
191
|
-
|
192
|
-
q = multiprocessing.Queue()
|
193
|
-
try:
|
194
|
-
p = multiprocessing.Process(
|
195
|
-
target=_call, args=tuple([q] + list(args)), kwargs=kwargs)
|
196
|
-
p.start()
|
197
|
-
p.join(timeout=timeout)
|
198
|
-
if p.is_alive():
|
199
|
-
p.terminate()
|
200
|
-
raise TimeoutError(f'Execution time exceed {timeout} seconds.')
|
201
|
-
x = q.get()
|
202
|
-
if isinstance(x, Exception):
|
203
|
-
raise x
|
204
|
-
try:
|
205
|
-
return pg.from_json_str(x)
|
206
|
-
except Exception as e:
|
207
|
-
raise errors.SerializationError(
|
208
|
-
'Cannot deserialize the output from sandbox.', e
|
209
|
-
) from e
|
210
|
-
finally:
|
211
|
-
q.close()
|
212
|
-
|
213
|
-
|
214
|
-
def call(
|
215
|
-
func: Callable[..., Any],
|
216
|
-
*args,
|
217
|
-
sandbox: bool | None = None,
|
218
|
-
timeout: float | None = None,
|
219
|
-
**kwargs
|
220
|
-
) -> Any:
|
221
|
-
"""Calls a function with sandbox support.
|
222
|
-
|
223
|
-
Args:
|
224
|
-
func: Function to call.
|
225
|
-
*args: Postional args that will be passed to `func`.
|
226
|
-
sandbox: If True, run code in sandbox; If False, run code in current
|
227
|
-
process. If None, run in sandbox first, if the output could not be
|
228
|
-
serialized and pass to current process, run the code again in current
|
229
|
-
process.
|
230
|
-
timeout: Execution timeout in seconds. If None, wait the code the complete.
|
231
|
-
**kwargs: Keyword args that will be passed to `func`.
|
48
|
+
returns_stdout: If True, the stdout (a str) will be returned.
|
49
|
+
outputs_intermediate: Applicable when returns_stdout is False. If True,
|
50
|
+
intermediate output will be outputted as a dict, with the last line's
|
51
|
+
value accessible by key '__result__' and the std output accessible by
|
52
|
+
key '__stdout__'. Otherwise the value of the last line will be returned.
|
232
53
|
|
233
54
|
Returns:
|
234
|
-
The
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
Exception: Exception that are raised from `func`.
|
55
|
+
The value of the last line of the code block. Or a dict of variable
|
56
|
+
names of all locals to their evaluated values as the output of the code to
|
57
|
+
run. The value for the last line can be accessed by key '__result__'. Or the
|
58
|
+
stdout as a str.
|
239
59
|
"""
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
return func(*args, **kwargs)
|
248
|
-
elif sandbox:
|
249
|
-
return sandbox_call(func, *args, timeout=timeout, **kwargs)
|
250
|
-
else:
|
251
|
-
return func(*args, **kwargs)
|
60
|
+
return pg.coding.evaluate(
|
61
|
+
parsing.clean(code),
|
62
|
+
global_vars=global_vars,
|
63
|
+
permission=permission,
|
64
|
+
returns_stdout=returns_stdout,
|
65
|
+
outputs_intermediate=outputs_intermediate,
|
66
|
+
)
|
252
67
|
|
253
68
|
|
254
69
|
def run(
|
255
70
|
code: str,
|
256
71
|
*,
|
257
72
|
global_vars: dict[str, Any] | None = None,
|
258
|
-
permission:
|
73
|
+
permission: CodePermission | None = None, # pylint: disable=redefined-outer-name
|
74
|
+
returns_stdout: bool = False,
|
259
75
|
outputs_intermediate: bool = False,
|
260
76
|
sandbox: bool | None = None,
|
261
77
|
timeout: float | None = None,
|
@@ -271,9 +87,11 @@ def run(
|
|
271
87
|
code: Python code to run.
|
272
88
|
global_vars: An optional dict of
|
273
89
|
permission: Permission for the Python code to run.
|
274
|
-
|
275
|
-
|
276
|
-
|
90
|
+
returns_stdout: If True, the stdout (a str) will be returned.
|
91
|
+
outputs_intermediate: Applicable when returns_stdout is False. If True,
|
92
|
+
intermediate output will be outputted as a dict, with the last line's
|
93
|
+
value accessible by key '__result__' and the std output accessible by
|
94
|
+
key '__stdout__'. Otherwise the value of the last line will be returned.
|
277
95
|
sandbox: If True, run code in sandbox; If False, run code in current
|
278
96
|
process. If None, run in sandbox first, if the output could not be
|
279
97
|
serialized and pass to current process, run the code again in current
|
@@ -283,13 +101,19 @@ def run(
|
|
283
101
|
Returns:
|
284
102
|
The value of the last line of the code block. Or a dict of variable
|
285
103
|
names of all locals to their evaluated values as the output of the code to
|
286
|
-
run. The value for the last line can be accessed by key '__result__'.
|
104
|
+
run. The value for the last line can be accessed by key '__result__'. Or the
|
105
|
+
stdout as a str.
|
287
106
|
|
288
107
|
Raises:
|
289
108
|
TimeoutError: If the execution time exceeds the timeout.
|
290
109
|
Exception: Exception that are raised from the code.
|
291
110
|
"""
|
292
|
-
return
|
293
|
-
|
111
|
+
return pg.coding.run(
|
112
|
+
parsing.clean(code),
|
113
|
+
global_vars=global_vars,
|
114
|
+
permission=permission,
|
115
|
+
returns_stdout=returns_stdout,
|
294
116
|
outputs_intermediate=outputs_intermediate,
|
295
|
-
sandbox=sandbox,
|
117
|
+
sandbox=sandbox,
|
118
|
+
timeout=timeout,
|
119
|
+
)
|
@@ -14,11 +14,8 @@
|
|
14
14
|
"""Tests for Python code execution."""
|
15
15
|
|
16
16
|
import inspect
|
17
|
-
import time
|
18
17
|
import unittest
|
19
|
-
from langfun.core.coding.python import errors
|
20
18
|
from langfun.core.coding.python import execution
|
21
|
-
from langfun.core.coding.python import permissions
|
22
19
|
import pyglove as pg
|
23
20
|
|
24
21
|
|
@@ -63,6 +60,15 @@ class EvaluateTest(unittest.TestCase):
|
|
63
60
|
),
|
64
61
|
3,
|
65
62
|
)
|
63
|
+
with self.assertRaisesRegex(execution.CodeError, 'ValueError'):
|
64
|
+
execution.evaluate(
|
65
|
+
"""
|
66
|
+
def foo():
|
67
|
+
raise ValueError("intentional error")
|
68
|
+
foo()
|
69
|
+
""",
|
70
|
+
permission=execution.CodePermission.ALL
|
71
|
+
)
|
66
72
|
|
67
73
|
def test_class_def(self):
|
68
74
|
ret = execution.evaluate(
|
@@ -73,7 +79,7 @@ class EvaluateTest(unittest.TestCase):
|
|
73
79
|
def __call__(self):
|
74
80
|
return self.x + self.y
|
75
81
|
""",
|
76
|
-
permission=
|
82
|
+
permission=execution.CodePermission.ALL,
|
77
83
|
global_vars=dict(pg=pg),
|
78
84
|
outputs_intermediate=True,
|
79
85
|
)
|
@@ -91,7 +97,7 @@ class EvaluateTest(unittest.TestCase):
|
|
91
97
|
def bar(z):
|
92
98
|
return z + foo(z, z)
|
93
99
|
""",
|
94
|
-
permission=
|
100
|
+
permission=execution.CodePermission.ALL,
|
95
101
|
outputs_intermediate=True,
|
96
102
|
)
|
97
103
|
self.assertEqual(
|
@@ -102,23 +108,33 @@ class EvaluateTest(unittest.TestCase):
|
|
102
108
|
self.assertIs(ret['__result__'], ret['bar'])
|
103
109
|
|
104
110
|
def test_function_def_and_call(self):
|
105
|
-
|
111
|
+
code = (
|
106
112
|
"""
|
107
113
|
def foo(x, y):
|
108
114
|
return x + y
|
109
115
|
|
110
116
|
def bar(z):
|
117
|
+
print(f'z is {z}')
|
111
118
|
return z + foo(z, z)
|
112
119
|
|
113
120
|
bar(1)
|
114
|
-
"""
|
115
|
-
|
121
|
+
"""
|
122
|
+
)
|
123
|
+
ret = execution.evaluate(
|
124
|
+
code,
|
125
|
+
permission=execution.CodePermission.ALL,
|
116
126
|
outputs_intermediate=True,
|
117
127
|
)
|
118
128
|
self.assertEqual(
|
119
129
|
list(ret.keys()), ['foo', 'bar', '__result__', '__stdout__']
|
120
130
|
)
|
121
131
|
self.assertEqual(ret['__result__'], 3)
|
132
|
+
ret = execution.evaluate(
|
133
|
+
code,
|
134
|
+
permission=execution.CodePermission.ALL,
|
135
|
+
returns_stdout=True,
|
136
|
+
)
|
137
|
+
self.assertEqual(ret, 'z is 1\n')
|
122
138
|
|
123
139
|
def test_complex(self):
|
124
140
|
ret = execution.evaluate(
|
@@ -134,7 +150,7 @@ class EvaluateTest(unittest.TestCase):
|
|
134
150
|
k = A(1, 2)
|
135
151
|
k(foo(3, 4))
|
136
152
|
""",
|
137
|
-
permission=
|
153
|
+
permission=execution.CodePermission.ALL,
|
138
154
|
global_vars=dict(pg=pg),
|
139
155
|
outputs_intermediate=True,
|
140
156
|
)
|
@@ -148,18 +164,18 @@ class EvaluateTest(unittest.TestCase):
|
|
148
164
|
|
149
165
|
def test_run_with_error(self):
|
150
166
|
with self.assertRaisesRegex(
|
151
|
-
|
167
|
+
execution.CodeError, 'NameError: name .* is not defined'
|
152
168
|
):
|
153
169
|
execution.evaluate(
|
154
170
|
"""
|
155
171
|
x = 1
|
156
172
|
y = x + z
|
157
173
|
""",
|
158
|
-
permission=
|
174
|
+
permission=execution.CodePermission.ALL,
|
159
175
|
)
|
160
|
-
with self.assertRaisesRegex(
|
176
|
+
with self.assertRaisesRegex(execution.CodeError, 'ValueError'):
|
161
177
|
execution.evaluate(
|
162
|
-
'raise ValueError()', permission=
|
178
|
+
'raise ValueError()', permission=execution.CodePermission.ALL
|
163
179
|
)
|
164
180
|
|
165
181
|
|
@@ -168,82 +184,6 @@ class Foo(pg.Object):
|
|
168
184
|
y: int
|
169
185
|
|
170
186
|
|
171
|
-
class SandboxCallTest(unittest.TestCase):
|
172
|
-
|
173
|
-
def test_basics(self):
|
174
|
-
def f(x, y):
|
175
|
-
return x + y
|
176
|
-
self.assertEqual(execution.sandbox_call(f, 1, y=2), 3)
|
177
|
-
|
178
|
-
def test_complex_type(self):
|
179
|
-
def f(x, y):
|
180
|
-
return Foo(x, y)
|
181
|
-
|
182
|
-
self.assertEqual(execution.sandbox_call(f, 1, 2), Foo(1, 2))
|
183
|
-
|
184
|
-
def test_timeout(self):
|
185
|
-
def f(x):
|
186
|
-
time.sleep(x)
|
187
|
-
|
188
|
-
self.assertIsNone(execution.sandbox_call(f, 0, timeout=1))
|
189
|
-
with self.assertRaises(TimeoutError):
|
190
|
-
execution.sandbox_call(f, 2, timeout=1)
|
191
|
-
|
192
|
-
def test_raise(self):
|
193
|
-
def f(x):
|
194
|
-
if x == 0:
|
195
|
-
raise ValueError()
|
196
|
-
|
197
|
-
self.assertIsNone(execution.sandbox_call(f, 1))
|
198
|
-
with self.assertRaises(ValueError):
|
199
|
-
execution.sandbox_call(f, 0)
|
200
|
-
|
201
|
-
|
202
|
-
class CallTest(unittest.TestCase):
|
203
|
-
|
204
|
-
def test_call_without_sandboxing(self):
|
205
|
-
def foo(x, y):
|
206
|
-
return x + y
|
207
|
-
|
208
|
-
self.assertEqual(
|
209
|
-
execution.call(foo, 1, y=2, sandbox=False),
|
210
|
-
3
|
211
|
-
)
|
212
|
-
|
213
|
-
def test_call_with_sandboxing(self):
|
214
|
-
def foo(x, y):
|
215
|
-
return x + y
|
216
|
-
|
217
|
-
self.assertEqual(
|
218
|
-
execution.call(foo, 1, y=2, sandbox=True),
|
219
|
-
3
|
220
|
-
)
|
221
|
-
|
222
|
-
def make_cls():
|
223
|
-
class A(pg.Object):
|
224
|
-
x: str
|
225
|
-
return A
|
226
|
-
|
227
|
-
with self.assertRaises(errors.SerializationError):
|
228
|
-
execution.call(make_cls, sandbox=True)
|
229
|
-
|
230
|
-
def test_call_with_automatic_sandboxing(self):
|
231
|
-
def foo(x, y):
|
232
|
-
return x + y
|
233
|
-
|
234
|
-
self.assertEqual(
|
235
|
-
execution.call(foo, 1, y=2),
|
236
|
-
3
|
237
|
-
)
|
238
|
-
|
239
|
-
def make_cls():
|
240
|
-
class A(pg.Object):
|
241
|
-
x: str
|
242
|
-
return A
|
243
|
-
|
244
|
-
self.assertTrue(inspect.isclass(execution.call(make_cls)))
|
245
|
-
|
246
|
-
|
247
187
|
class RunTest(unittest.TestCase):
|
248
188
|
|
249
189
|
def test_run_without_sandboxing(self):
|
@@ -88,6 +88,8 @@ class PythonCode(pg.Object):
|
|
88
88
|
sandbox: bool | None = None,
|
89
89
|
timeout: int | None = 5,
|
90
90
|
global_vars: dict[str, Any] | None = None,
|
91
|
+
returns_stdout: bool = False,
|
92
|
+
outputs_intermediate: bool = False,
|
91
93
|
autofix: int = 3,
|
92
94
|
autofix_lm: lf.LanguageModel | None = None,
|
93
95
|
) -> Any:
|
@@ -101,13 +103,22 @@ class PythonCode(pg.Object):
|
|
101
103
|
timeout: Timeout in seconds. If None, there is no timeout. Applicable when
|
102
104
|
sandbox is set to True.
|
103
105
|
global_vars: Global variables that could be accessed from the source code.
|
106
|
+
returns_stdout: If True, the stdout (a str) will be returned.
|
107
|
+
outputs_intermediate: Applicable when returns_stdout is False. If True,
|
108
|
+
intermediate output will be outputted as a dict, with the last line's
|
109
|
+
value accessible by key '__result__' and the std output accessible by
|
110
|
+
key '__stdout__'. Otherwise the value of the last line will be returned.
|
104
111
|
autofix: Number of attempts to auto fix the generated code. If 0, autofix
|
105
112
|
is disabled.
|
106
113
|
autofix_lm: Language model to be used. If not specified, it will try to
|
107
114
|
use the `lm` under `lf.context`.
|
108
115
|
|
109
116
|
Returns:
|
110
|
-
The value of the last expression in the source code.
|
117
|
+
The value of the last expression in the source code. Or a dict of local
|
118
|
+
variable names defined in the source code to their values if
|
119
|
+
`outputs_intermediate` is set to True. The value for the last line can be
|
120
|
+
accessed by key '__result__'. Or the stdout as a str if `returns_stdout`
|
121
|
+
is set to True.
|
111
122
|
|
112
123
|
Raises:
|
113
124
|
TimeoutError: If `sandbox` is True and timeout has reached.
|
@@ -121,6 +132,8 @@ class PythonCode(pg.Object):
|
|
121
132
|
max_attempts=autofix,
|
122
133
|
lm=autofix_lm,
|
123
134
|
returns_code=True,
|
135
|
+
returns_stdout=returns_stdout,
|
136
|
+
outputs_intermediate=outputs_intermediate,
|
124
137
|
)
|
125
138
|
self.rebind(source=updated_code)
|
126
139
|
return result
|
@@ -158,18 +171,14 @@ class PythonCode(pg.Object):
|
|
158
171
|
TimeoutError: If `sandbox` is True and timeout has reached.
|
159
172
|
Exception: Any errors that the source code has raised.
|
160
173
|
"""
|
161
|
-
|
162
|
-
self.source,
|
163
|
-
global_vars=global_vars,
|
174
|
+
return self(
|
164
175
|
sandbox=sandbox,
|
165
176
|
timeout=timeout,
|
177
|
+
global_vars=global_vars,
|
178
|
+
autofix=autofix,
|
179
|
+
autofix_lm=autofix_lm,
|
166
180
|
outputs_intermediate=True,
|
167
|
-
max_attempts=autofix,
|
168
|
-
lm=autofix_lm,
|
169
|
-
returns_code=True,
|
170
181
|
)
|
171
|
-
self.rebind(source=updated_code)
|
172
|
-
return result
|
173
182
|
|
174
183
|
|
175
184
|
class PythonFunction(pg.Object):
|
@@ -218,5 +227,6 @@ class PythonFunction(pg.Object):
|
|
218
227
|
TimeoutError: If `sandbox` is True and timeout has reached.
|
219
228
|
Exception: Any errors that the source code has raised.
|
220
229
|
"""
|
221
|
-
return
|
222
|
-
self.implementation, *args, sandbox=sandbox, timeout=timeout, **kwargs
|
230
|
+
return pg.coding.maybe_sandbox_call(
|
231
|
+
self.implementation, *args, sandbox=sandbox, timeout=timeout, **kwargs
|
232
|
+
)
|
@@ -16,7 +16,7 @@
|
|
16
16
|
import inspect
|
17
17
|
import unittest
|
18
18
|
from langfun.core.coding.python import generation
|
19
|
-
|
19
|
+
import pyglove as pg
|
20
20
|
|
21
21
|
|
22
22
|
class PythonCodeTest(unittest.TestCase):
|
@@ -58,7 +58,7 @@ class PythonCodeTest(unittest.TestCase):
|
|
58
58
|
)
|
59
59
|
|
60
60
|
def test_call_class_def(self):
|
61
|
-
with
|
61
|
+
with pg.coding.permission(pg.coding.CodePermission.CLASS_DEFINITION):
|
62
62
|
v = generation.PythonCode("""
|
63
63
|
class A:
|
64
64
|
pass
|