langfun 0.1.2.dev202501300804__py3-none-any.whl → 0.1.2.dev202501310804__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.
@@ -32,5 +32,9 @@ from langfun.core.coding.python.correction import correct
32
32
  from langfun.core.coding.python.correction import run_with_correction
33
33
  from langfun.core.coding.python.correction import CodeWithError
34
34
 
35
+ from langfun.core.coding.python.sandboxing import Sandbox
36
+ from langfun.core.coding.python.sandboxing import SandboxOutput
37
+ from langfun.core.coding.python.sandboxing import MultiProcessingSandbox
38
+
35
39
  # pylint: enable=g-importing-member
36
40
  # pylint: enable=g-bad-import-order
@@ -0,0 +1,152 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ """Python sandboxing."""
15
+
16
+ import abc
17
+ import os
18
+ import tempfile
19
+ from typing import Annotated
20
+
21
+ from langfun.core.coding.python import parsing
22
+ import pyglove as pg
23
+
24
+
25
+ class SandboxOutput(pg.Object):
26
+ """Sandbox output."""
27
+
28
+ stdout: Annotated[
29
+ str,
30
+ 'The stdout of the sandbox execution.'
31
+ ] = ''
32
+
33
+ stderr: Annotated[
34
+ str,
35
+ 'The stderr of the sandbox execution.'
36
+ ] = ''
37
+
38
+
39
+ class Sandbox(pg.Object):
40
+ """Interface for Python sandbox."""
41
+
42
+ def _on_bound(self):
43
+ super()._on_bound()
44
+ self._uploaded_files: dict[str, str] = {}
45
+
46
+ def run(
47
+ self,
48
+ code: str,
49
+ *,
50
+ timeout: int | None = 30,
51
+ **kwargs
52
+ ) -> SandboxOutput:
53
+ """Runs code in the sandbox. Raises pg.coding.CodeError if failed."""
54
+ return self._run(self.normalize_code(code), timeout=timeout, **kwargs)
55
+
56
+ def normalize_code(self, code: str) -> str:
57
+ """Returns normalized code runnable in the sandbox."""
58
+ for original_path, uploaded_path in self._uploaded_files.items():
59
+ code = code.replace(original_path, uploaded_path)
60
+ return parsing.clean(code)
61
+
62
+ @abc.abstractmethod
63
+ def _run(
64
+ self,
65
+ code: str,
66
+ *,
67
+ timeout: int | None = 30,
68
+ **kwargs
69
+ ) -> SandboxOutput:
70
+ """Runs code in the sandbox. Raises pg.coding.CodeError if failed."""
71
+
72
+ def upload(self, path: str) -> str:
73
+ """Uploads a file to the sandbox. Returns the uploaded path."""
74
+ uploaded_path = self._upload(path)
75
+ self._uploaded_files[path] = uploaded_path
76
+ return uploaded_path
77
+
78
+ @abc.abstractmethod
79
+ def _upload(self, path: str) -> str:
80
+ """Uploads a file to the sandbox."""
81
+
82
+ def setup(self) -> None:
83
+ """Sets up the sandbox."""
84
+ self._uploaded_files = {}
85
+ self._setup()
86
+
87
+ def _setup(self) -> None:
88
+ """Sets up the sandbox."""
89
+
90
+ def cleanup(self) -> None:
91
+ """Cleans up the sandbox."""
92
+ self._uploaded_files = {}
93
+ self._cleanup()
94
+
95
+ def _cleanup(self) -> None:
96
+ """Cleans up the sandbox."""
97
+
98
+ def __enter__(self):
99
+ """Enters the sandbox."""
100
+ self.setup()
101
+ return self
102
+
103
+ def __exit__(self, exc_type, exc_value, traceback):
104
+ """Exits the sandbox."""
105
+ self.cleanup()
106
+
107
+
108
+ class MultiProcessingSandbox(Sandbox):
109
+ """Sandbox using multiprocessing."""
110
+
111
+ def _on_bound(self):
112
+ super()._on_bound()
113
+ self._working_dir = None
114
+
115
+ @property
116
+ def working_dir(self) -> str | None:
117
+ """Returns the directory of the sandbox."""
118
+ return self._working_dir
119
+
120
+ def _setup(self) -> None:
121
+ """Sets up the sandbox."""
122
+ self._working_dir = tempfile.TemporaryDirectory()
123
+
124
+ def _cleanup(self) -> None:
125
+ """Cleans up the sandbox."""
126
+ assert self._working_dir is not None
127
+ self._working_dir.cleanup()
128
+
129
+ def _run(
130
+ self,
131
+ code: str,
132
+ *,
133
+ timeout: int | None = 30,
134
+ **kwargs
135
+ ) -> SandboxOutput:
136
+ """Runs code in the sandbox."""
137
+ stdout = pg.coding.run(
138
+ code, returns_stdout=True, sandbox=True, timeout=timeout
139
+ )
140
+ return SandboxOutput(stdout=stdout)
141
+
142
+ def _upload(self, path: str) -> str:
143
+ """Uploads a file to the sandbox."""
144
+ if self._working_dir is None:
145
+ raise ValueError('Sandbox is not set up.')
146
+
147
+ # Upload the file to the sandbox directory.
148
+ uploaded_path = os.path.join(self._working_dir.name, os.path.basename(path))
149
+ with pg.io.open(path, 'r') as r:
150
+ with pg.io.open(uploaded_path, 'w') as w:
151
+ w.write(r.read())
152
+ return uploaded_path
@@ -0,0 +1,62 @@
1
+ # Copyright 2025 The Langfun Authors
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ import inspect
15
+ import os
16
+ import tempfile
17
+ import unittest
18
+
19
+ from langfun.core.coding.python import sandboxing
20
+ import pyglove as pg
21
+
22
+
23
+ class MultiProcessingSandboxTest(unittest.TestCase):
24
+ """Tests for MultiProcessingSandbox."""
25
+
26
+ def test_basics(self):
27
+ with tempfile.TemporaryDirectory() as dir1:
28
+ input_file = os.path.join(dir1, 'test.json')
29
+ pg.save(pg.Dict(x=1, y=2), input_file)
30
+ with sandboxing.MultiProcessingSandbox() as sandbox:
31
+ self.assertIsNotNone(sandbox.working_dir)
32
+ sandbox_input_file = sandbox.upload(input_file)
33
+ code = (
34
+ f"""
35
+ import pyglove as pg
36
+ print(pg.load({input_file!r}).x)
37
+ """
38
+ )
39
+ self.assertEqual(
40
+ sandbox.normalize_code(code),
41
+ inspect.cleandoc(
42
+ f"""
43
+ import pyglove as pg
44
+ print(pg.load({sandbox_input_file!r}).x)
45
+ """
46
+ ),
47
+ )
48
+ self.assertEqual(sandbox.run(code).stdout, '1\n')
49
+
50
+ def test_bad_code(self):
51
+ with sandboxing.MultiProcessingSandbox() as sandbox:
52
+ with self.assertRaisesRegex(pg.coding.CodeError, '.* is not defined'):
53
+ sandbox.run('print(x)')
54
+
55
+ def test_bad_usages(self):
56
+ sandbox = sandboxing.MultiProcessingSandbox()
57
+ with self.assertRaisesRegex(ValueError, 'Sandbox is not set up.'):
58
+ sandbox.upload('abc.txt')
59
+
60
+
61
+ if __name__ == '__main__':
62
+ unittest.main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: langfun
3
- Version: 0.1.2.dev202501300804
3
+ Version: 0.1.2.dev202501310804
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -31,7 +31,7 @@ langfun/core/agentic/action_eval.py,sha256=ZtjTh34S7XPIUqandQ0YwAtzw-S7ofuZ7rRXn
31
31
  langfun/core/agentic/action_eval_test.py,sha256=tRUkWmOE9p0rpNOq19xAY2oDEnYsEEykjg6sUpAwJk0,2832
32
32
  langfun/core/agentic/action_test.py,sha256=Gu7P5XQvzqbKawn2jjyTpWaARzzhzO04KkC1TuBnUnw,4612
33
33
  langfun/core/coding/__init__.py,sha256=5utju_fwEsImaiftx4oXKl9FAM8p281k8-Esdh_-m1w,835
34
- langfun/core/coding/python/__init__.py,sha256=K6jb2le6Hq-aaKqAF4pGW5JRwSyO-RnUTMIxxaVcsNA,1540
34
+ langfun/core/coding/python/__init__.py,sha256=4ByknuoNU-mOIHwHKnTtmo6oD64oMFtlqPlYWmA5Wic,1736
35
35
  langfun/core/coding/python/correction.py,sha256=7zBedlhQKMPA4cfchUMxAOFl6Zl5RqCyllRHGWys40s,7092
36
36
  langfun/core/coding/python/correction_test.py,sha256=sie88lAbsV15bvkRcYC88pgToybZYXI32Xmg_ym5V1A,4175
37
37
  langfun/core/coding/python/execution.py,sha256=tsXnJ-11RqNDro0C-6LwbHkqPuNVJ_cLxAq8-C5Wz20,4442
@@ -40,6 +40,8 @@ langfun/core/coding/python/generation.py,sha256=sA6t97qBflO3WtuL9axknEVQPgqXHeqa
40
40
  langfun/core/coding/python/generation_test.py,sha256=dcF5ef1UApzRfTpvICiChpynkzZ1mDsE0DvH0iMpTvg,2743
41
41
  langfun/core/coding/python/parsing.py,sha256=jvGDIwoaY3mdGXeFhjP27w0ukO0TtdCC7G4ODVNp8S4,4554
42
42
  langfun/core/coding/python/parsing_test.py,sha256=PIexYpSEhgNaSd4T6QYWzWHzm3sL4VhQJ4dhdvJAQk8,5005
43
+ langfun/core/coding/python/sandboxing.py,sha256=yeEdydMkfHk3Hj3-5ykeROpYyLbRfZ4BwGWJYvFBmSI,4001
44
+ langfun/core/coding/python/sandboxing_test.py,sha256=H_0_pd-_uS-ci5yYhmDTR6-hyzosAFkExziAHndfdDo,2023
43
45
  langfun/core/eval/__init__.py,sha256=OEXr1ZRuvLuhJJfuQ1ZWQ-SvYzjyrtiAAEogYaB7E6o,1933
44
46
  langfun/core/eval/base.py,sha256=XXerMVkK4wREo7K1_aCyay6vDjw3mfs389XThAdzv50,75768
45
47
  langfun/core/eval/base_test.py,sha256=-LsIV9DXlDal0EnOlaWpibJvfef0NbxtZAm0OH_abAE,27189
@@ -146,8 +148,8 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
146
148
  langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
147
149
  langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
148
150
  langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
149
- langfun-0.1.2.dev202501300804.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
150
- langfun-0.1.2.dev202501300804.dist-info/METADATA,sha256=sfy8YuZ4CkHAAwZCgEhkMUWQGumIpcO4IzzsfldRz_0,8172
151
- langfun-0.1.2.dev202501300804.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
152
- langfun-0.1.2.dev202501300804.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
153
- langfun-0.1.2.dev202501300804.dist-info/RECORD,,
151
+ langfun-0.1.2.dev202501310804.dist-info/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
152
+ langfun-0.1.2.dev202501310804.dist-info/METADATA,sha256=sj-19_JA8zAxgd8nwfbuVD26CsbkCbsVg_jcraR2sME,8172
153
+ langfun-0.1.2.dev202501310804.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
154
+ langfun-0.1.2.dev202501310804.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
155
+ langfun-0.1.2.dev202501310804.dist-info/RECORD,,