langfun 0.1.2.dev202501290804__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.
- langfun/core/coding/python/__init__.py +4 -0
- langfun/core/coding/python/sandboxing.py +152 -0
- langfun/core/coding/python/sandboxing_test.py +62 -0
- {langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/METADATA +1 -1
- {langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/RECORD +8 -6
- {langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/LICENSE +0 -0
- {langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/top_level.txt +0 -0
@@ -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()
|
@@ -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=
|
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.
|
150
|
-
langfun-0.1.2.
|
151
|
-
langfun-0.1.2.
|
152
|
-
langfun-0.1.2.
|
153
|
-
langfun-0.1.2.
|
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,,
|
File without changes
|
File without changes
|
{langfun-0.1.2.dev202501290804.dist-info → langfun-0.1.2.dev202501310804.dist-info}/top_level.txt
RENAMED
File without changes
|