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.
Files changed (145) hide show
  1. langfun/__init__.py +22 -2
  2. langfun/core/__init__.py +17 -5
  3. langfun/core/agentic/__init__.py +30 -0
  4. langfun/core/agentic/action.py +854 -0
  5. langfun/core/agentic/action_eval.py +150 -0
  6. langfun/core/agentic/action_eval_test.py +109 -0
  7. langfun/core/agentic/action_test.py +136 -0
  8. langfun/core/coding/python/__init__.py +5 -11
  9. langfun/core/coding/python/correction.py +37 -28
  10. langfun/core/coding/python/correction_test.py +29 -3
  11. langfun/core/coding/python/execution.py +40 -216
  12. langfun/core/coding/python/execution_test.py +29 -89
  13. langfun/core/coding/python/generation.py +21 -11
  14. langfun/core/coding/python/generation_test.py +2 -2
  15. langfun/core/coding/python/parsing.py +108 -193
  16. langfun/core/coding/python/parsing_test.py +2 -105
  17. langfun/core/component.py +69 -2
  18. langfun/core/component_test.py +54 -0
  19. langfun/core/concurrent.py +414 -117
  20. langfun/core/concurrent_test.py +111 -24
  21. langfun/core/console.py +18 -5
  22. langfun/core/console_test.py +17 -0
  23. langfun/core/eval/__init__.py +17 -0
  24. langfun/core/eval/base.py +767 -140
  25. langfun/core/eval/base_test.py +238 -53
  26. langfun/core/eval/matching.py +80 -76
  27. langfun/core/eval/matching_test.py +19 -9
  28. langfun/core/eval/patching.py +130 -0
  29. langfun/core/eval/patching_test.py +170 -0
  30. langfun/core/eval/scoring.py +37 -28
  31. langfun/core/eval/scoring_test.py +21 -3
  32. langfun/core/eval/v2/__init__.py +42 -0
  33. langfun/core/eval/v2/checkpointing.py +380 -0
  34. langfun/core/eval/v2/checkpointing_test.py +228 -0
  35. langfun/core/eval/v2/eval_test_helper.py +136 -0
  36. langfun/core/eval/v2/evaluation.py +725 -0
  37. langfun/core/eval/v2/evaluation_test.py +180 -0
  38. langfun/core/eval/v2/example.py +305 -0
  39. langfun/core/eval/v2/example_test.py +128 -0
  40. langfun/core/eval/v2/experiment.py +1048 -0
  41. langfun/core/eval/v2/experiment_test.py +433 -0
  42. langfun/core/eval/v2/metric_values.py +156 -0
  43. langfun/core/eval/v2/metric_values_test.py +80 -0
  44. langfun/core/eval/v2/metrics.py +357 -0
  45. langfun/core/eval/v2/metrics_test.py +203 -0
  46. langfun/core/eval/v2/progress.py +348 -0
  47. langfun/core/eval/v2/progress_test.py +82 -0
  48. langfun/core/eval/v2/progress_tracking.py +210 -0
  49. langfun/core/eval/v2/progress_tracking_test.py +66 -0
  50. langfun/core/eval/v2/reporting.py +270 -0
  51. langfun/core/eval/v2/reporting_test.py +158 -0
  52. langfun/core/eval/v2/runners.py +488 -0
  53. langfun/core/eval/v2/runners_test.py +334 -0
  54. langfun/core/langfunc.py +3 -21
  55. langfun/core/langfunc_test.py +26 -8
  56. langfun/core/language_model.py +686 -48
  57. langfun/core/language_model_test.py +681 -44
  58. langfun/core/llms/__init__.py +100 -12
  59. langfun/core/llms/anthropic.py +488 -0
  60. langfun/core/llms/anthropic_test.py +235 -0
  61. langfun/core/llms/cache/base.py +21 -2
  62. langfun/core/llms/cache/in_memory.py +13 -0
  63. langfun/core/llms/cache/in_memory_test.py +88 -28
  64. langfun/core/llms/compositional.py +101 -0
  65. langfun/core/llms/compositional_test.py +73 -0
  66. langfun/core/llms/deepseek.py +117 -0
  67. langfun/core/llms/deepseek_test.py +61 -0
  68. langfun/core/llms/fake.py +39 -26
  69. langfun/core/llms/fake_test.py +136 -11
  70. langfun/core/llms/gemini.py +507 -0
  71. langfun/core/llms/gemini_test.py +195 -0
  72. langfun/core/llms/google_genai.py +62 -218
  73. langfun/core/llms/google_genai_test.py +9 -197
  74. langfun/core/llms/groq.py +276 -0
  75. langfun/core/llms/groq_test.py +64 -0
  76. langfun/core/llms/llama_cpp.py +15 -40
  77. langfun/core/llms/llama_cpp_test.py +4 -30
  78. langfun/core/llms/openai.py +436 -226
  79. langfun/core/llms/openai_compatible.py +179 -0
  80. langfun/core/llms/openai_compatible_test.py +495 -0
  81. langfun/core/llms/openai_test.py +35 -174
  82. langfun/core/llms/rest.py +113 -0
  83. langfun/core/llms/rest_test.py +111 -0
  84. langfun/core/llms/vertexai.py +192 -0
  85. langfun/core/llms/vertexai_test.py +52 -0
  86. langfun/core/logging.py +284 -0
  87. langfun/core/logging_test.py +125 -0
  88. langfun/core/message.py +319 -9
  89. langfun/core/message_test.py +190 -13
  90. langfun/core/modalities/__init__.py +6 -2
  91. langfun/core/modalities/audio.py +30 -0
  92. langfun/core/modalities/audio_test.py +63 -0
  93. langfun/core/modalities/image.py +39 -20
  94. langfun/core/modalities/image_test.py +52 -9
  95. langfun/core/modalities/mime.py +206 -29
  96. langfun/core/modalities/mime_test.py +90 -9
  97. langfun/core/modalities/ms_office.py +117 -0
  98. langfun/core/modalities/ms_office_test.py +389 -0
  99. langfun/core/modalities/pdf.py +22 -0
  100. langfun/core/modalities/pdf_test.py +57 -0
  101. langfun/core/modalities/video.py +9 -23
  102. langfun/core/modalities/video_test.py +3 -3
  103. langfun/core/modality.py +26 -3
  104. langfun/core/modality_test.py +2 -2
  105. langfun/core/sampling.py +11 -11
  106. langfun/core/structured/__init__.py +15 -16
  107. langfun/core/structured/completion.py +32 -5
  108. langfun/core/structured/completion_test.py +9 -8
  109. langfun/core/structured/description.py +2 -2
  110. langfun/core/structured/description_test.py +3 -3
  111. langfun/core/structured/function_generation.py +278 -0
  112. langfun/core/structured/function_generation_test.py +399 -0
  113. langfun/core/structured/mapping.py +150 -46
  114. langfun/core/structured/mapping_test.py +105 -0
  115. langfun/core/structured/parsing.py +33 -21
  116. langfun/core/structured/parsing_test.py +71 -22
  117. langfun/core/structured/querying.py +746 -0
  118. langfun/core/structured/{prompting_test.py → querying_test.py} +545 -60
  119. langfun/core/structured/schema.py +208 -99
  120. langfun/core/structured/schema_generation.py +1 -1
  121. langfun/core/structured/schema_generation_test.py +2 -2
  122. langfun/core/structured/schema_test.py +133 -34
  123. langfun/core/structured/scoring.py +125 -19
  124. langfun/core/structured/scoring_test.py +30 -0
  125. langfun/core/structured/tokenization.py +64 -0
  126. langfun/core/structured/tokenization_test.py +48 -0
  127. langfun/core/template.py +240 -11
  128. langfun/core/template_test.py +146 -1
  129. langfun/core/templates/conversation.py +9 -0
  130. langfun/core/templates/conversation_test.py +4 -3
  131. langfun/core/templates/selfplay_test.py +14 -2
  132. langfun-0.1.2.dev202501140804.dist-info/METADATA +225 -0
  133. langfun-0.1.2.dev202501140804.dist-info/RECORD +153 -0
  134. {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/WHEEL +1 -1
  135. langfun/core/coding/python/errors.py +0 -108
  136. langfun/core/coding/python/errors_test.py +0 -99
  137. langfun/core/coding/python/permissions.py +0 -90
  138. langfun/core/coding/python/permissions_test.py +0 -86
  139. langfun/core/structured/prompting.py +0 -217
  140. langfun/core/text_formatting.py +0 -162
  141. langfun/core/text_formatting_test.py +0 -47
  142. langfun-0.0.2.dev20240330.dist-info/METADATA +0 -99
  143. langfun-0.0.2.dev20240330.dist-info/RECORD +0 -102
  144. {langfun-0.0.2.dev20240330.dist-info → langfun-0.1.2.dev202501140804.dist-info}/LICENSE +0 -0
  145. {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 ast
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
- # Key in returned dict that captures stdout.
29
- STDOUT_KEY = '__stdout__'
30
-
31
- # Key in the returned dict that represents the final result.
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: permissions.CodePermission | None = None,
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
- outputs_intermediate: If True, intermediate output will be outputted as a
75
- dict, with the last line's value accessible by key '__result__'. Otherwise
76
- the value of the last line will be returned.
77
-
78
- Returns:
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 return value of `func`.
235
-
236
- Raises:
237
- TimeoutError: If the execution time exceeds the timeout.
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
- if sandbox is None:
241
- try:
242
- return sandbox_call(func, *args, timeout=timeout, **kwargs)
243
- # NOTE(daiyip): output could be serialized across processes, giving it
244
- # already finishes on sandbox, so it should be much safer to run under
245
- # current process.
246
- except errors.SerializationError:
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: permissions.CodePermission | None = None,
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
- outputs_intermediate: If True, all variables created as locals will be
275
- returned, with the final result accessible by key '__result__'. Otherwise
276
- only the final result will be returned.
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 call(
293
- evaluate, code=code, global_vars=global_vars, permission=permission,
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, timeout=timeout)
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=permissions.CodePermission.ALL,
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=permissions.CodePermission.ALL,
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
- ret = execution.evaluate(
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
- permission=permissions.CodePermission.ALL,
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=permissions.CodePermission.ALL,
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
- errors.CodeError, 'NameError: name .* is not defined'
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=permissions.CodePermission.ALL,
174
+ permission=execution.CodePermission.ALL,
159
175
  )
160
- with self.assertRaisesRegex(errors.CodeError, 'ValueError'):
176
+ with self.assertRaisesRegex(execution.CodeError, 'ValueError'):
161
177
  execution.evaluate(
162
- 'raise ValueError()', permission=permissions.CodePermission.ALL
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
- result, updated_code = correction.run_with_correction(
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 execution.call(
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
- from langfun.core.coding.python import permissions
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 permissions.permission(permissions.CodePermission.CLASS_DEFINITION):
61
+ with pg.coding.permission(pg.coding.CodePermission.CLASS_DEFINITION):
62
62
  v = generation.PythonCode("""
63
63
  class A:
64
64
  pass