langfun 0.1.2.dev202509120804__py3-none-any.whl → 0.1.2.dev202512040805__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 +1 -1
- langfun/core/__init__.py +7 -1
- langfun/core/agentic/__init__.py +8 -1
- langfun/core/agentic/action.py +740 -112
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +189 -24
- langfun/core/async_support.py +104 -5
- langfun/core/async_support_test.py +23 -0
- langfun/core/coding/python/correction.py +19 -9
- langfun/core/coding/python/execution.py +14 -12
- langfun/core/coding/python/generation.py +21 -16
- langfun/core/coding/python/sandboxing.py +23 -3
- langfun/core/component.py +42 -3
- langfun/core/concurrent.py +70 -6
- langfun/core/concurrent_test.py +9 -2
- langfun/core/console.py +1 -1
- langfun/core/data/conversion/anthropic.py +12 -3
- langfun/core/data/conversion/anthropic_test.py +8 -6
- langfun/core/data/conversion/gemini.py +11 -2
- langfun/core/data/conversion/gemini_test.py +48 -9
- langfun/core/data/conversion/openai.py +145 -31
- langfun/core/data/conversion/openai_test.py +161 -17
- langfun/core/eval/base.py +48 -44
- langfun/core/eval/base_test.py +5 -5
- langfun/core/eval/matching.py +5 -2
- langfun/core/eval/patching.py +3 -3
- langfun/core/eval/scoring.py +4 -3
- langfun/core/eval/v2/__init__.py +2 -0
- langfun/core/eval/v2/checkpointing.py +76 -7
- langfun/core/eval/v2/checkpointing_test.py +9 -2
- langfun/core/eval/v2/config_saver.py +37 -0
- langfun/core/eval/v2/config_saver_test.py +36 -0
- langfun/core/eval/v2/eval_test_helper.py +104 -3
- langfun/core/eval/v2/evaluation.py +92 -17
- langfun/core/eval/v2/evaluation_test.py +9 -3
- langfun/core/eval/v2/example.py +50 -40
- langfun/core/eval/v2/example_test.py +16 -8
- langfun/core/eval/v2/experiment.py +84 -15
- langfun/core/eval/v2/experiment_test.py +19 -0
- langfun/core/eval/v2/metric_values.py +31 -3
- langfun/core/eval/v2/metric_values_test.py +32 -0
- langfun/core/eval/v2/metrics.py +157 -44
- langfun/core/eval/v2/metrics_test.py +39 -18
- langfun/core/eval/v2/progress.py +31 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking.py +13 -5
- langfun/core/eval/v2/progress_tracking_test.py +9 -1
- langfun/core/eval/v2/reporting.py +90 -71
- langfun/core/eval/v2/reporting_test.py +24 -6
- langfun/core/eval/v2/runners/__init__.py +30 -0
- langfun/core/eval/v2/{runners.py → runners/base.py} +72 -180
- langfun/core/eval/v2/runners/beam.py +354 -0
- langfun/core/eval/v2/runners/beam_test.py +153 -0
- langfun/core/eval/v2/runners/ckpt_monitor.py +294 -0
- langfun/core/eval/v2/runners/ckpt_monitor_test.py +162 -0
- langfun/core/eval/v2/runners/debug.py +40 -0
- langfun/core/eval/v2/runners/debug_test.py +76 -0
- langfun/core/eval/v2/runners/parallel.py +243 -0
- langfun/core/eval/v2/runners/parallel_test.py +182 -0
- langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun/core/eval/v2/runners/sequential_test.py +169 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +7 -5
- langfun/core/language_model.py +189 -36
- langfun/core/language_model_test.py +54 -3
- langfun/core/llms/__init__.py +12 -1
- langfun/core/llms/anthropic.py +157 -2
- langfun/core/llms/azure_openai.py +29 -17
- langfun/core/llms/cache/base.py +25 -3
- langfun/core/llms/cache/in_memory.py +48 -7
- langfun/core/llms/cache/in_memory_test.py +14 -4
- langfun/core/llms/compositional.py +25 -1
- langfun/core/llms/deepseek.py +30 -2
- langfun/core/llms/fake.py +32 -1
- langfun/core/llms/gemini.py +64 -12
- langfun/core/llms/gemini_test.py +110 -0
- langfun/core/llms/google_genai.py +34 -1
- langfun/core/llms/groq.py +28 -3
- langfun/core/llms/llama_cpp.py +23 -4
- langfun/core/llms/openai.py +120 -3
- langfun/core/llms/openai_compatible.py +148 -27
- langfun/core/llms/openai_compatible_test.py +207 -20
- langfun/core/llms/openai_test.py +0 -2
- langfun/core/llms/rest.py +16 -1
- langfun/core/llms/vertexai.py +58 -8
- langfun/core/logging.py +1 -1
- langfun/core/mcp/__init__.py +10 -0
- langfun/core/mcp/client.py +177 -0
- langfun/core/mcp/client_test.py +71 -0
- langfun/core/mcp/session.py +241 -0
- langfun/core/mcp/session_test.py +54 -0
- langfun/core/mcp/testing/simple_mcp_client.py +33 -0
- langfun/core/mcp/testing/simple_mcp_server.py +33 -0
- langfun/core/mcp/tool.py +254 -0
- langfun/core/mcp/tool_test.py +197 -0
- langfun/core/memory.py +1 -0
- langfun/core/message.py +160 -55
- langfun/core/message_test.py +65 -81
- langfun/core/modalities/__init__.py +8 -0
- langfun/core/modalities/audio.py +21 -1
- langfun/core/modalities/image.py +73 -3
- langfun/core/modalities/image_test.py +116 -0
- langfun/core/modalities/mime.py +64 -3
- langfun/core/modalities/mime_test.py +11 -0
- langfun/core/modalities/pdf.py +19 -1
- langfun/core/modalities/video.py +21 -1
- langfun/core/modality.py +167 -29
- langfun/core/modality_test.py +42 -12
- langfun/core/natural_language.py +1 -1
- langfun/core/sampling.py +4 -4
- langfun/core/sampling_test.py +20 -4
- langfun/core/structured/__init__.py +2 -24
- langfun/core/structured/completion.py +34 -44
- langfun/core/structured/completion_test.py +23 -43
- langfun/core/structured/description.py +54 -50
- langfun/core/structured/function_generation.py +29 -12
- langfun/core/structured/mapping.py +81 -37
- langfun/core/structured/parsing.py +95 -79
- langfun/core/structured/parsing_test.py +0 -3
- langfun/core/structured/querying.py +230 -154
- langfun/core/structured/querying_test.py +69 -33
- langfun/core/structured/schema/__init__.py +49 -0
- langfun/core/structured/schema/base.py +664 -0
- langfun/core/structured/schema/base_test.py +531 -0
- langfun/core/structured/schema/json.py +174 -0
- langfun/core/structured/schema/json_test.py +121 -0
- langfun/core/structured/schema/python.py +316 -0
- langfun/core/structured/schema/python_test.py +410 -0
- langfun/core/structured/schema_generation.py +33 -14
- langfun/core/structured/scoring.py +47 -36
- langfun/core/structured/tokenization.py +26 -11
- langfun/core/subscription.py +2 -2
- langfun/core/template.py +175 -50
- langfun/core/template_test.py +123 -17
- langfun/env/__init__.py +43 -0
- langfun/env/base_environment.py +827 -0
- langfun/env/base_environment_test.py +473 -0
- langfun/env/base_feature.py +304 -0
- langfun/env/base_feature_test.py +228 -0
- langfun/env/base_sandbox.py +842 -0
- langfun/env/base_sandbox_test.py +1235 -0
- langfun/env/event_handlers/__init__.py +14 -0
- langfun/env/event_handlers/chain.py +233 -0
- langfun/env/event_handlers/chain_test.py +253 -0
- langfun/env/event_handlers/event_logger.py +472 -0
- langfun/env/event_handlers/event_logger_test.py +304 -0
- langfun/env/event_handlers/metric_writer.py +726 -0
- langfun/env/event_handlers/metric_writer_test.py +214 -0
- langfun/env/interface.py +1640 -0
- langfun/env/interface_test.py +153 -0
- langfun/env/load_balancers.py +59 -0
- langfun/env/load_balancers_test.py +141 -0
- langfun/env/test_utils.py +507 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/METADATA +7 -3
- langfun-0.1.2.dev202512040805.dist-info/RECORD +217 -0
- langfun/core/eval/v2/runners_test.py +0 -343
- langfun/core/structured/schema.py +0 -987
- langfun/core/structured/schema_test.py +0 -982
- langfun-0.1.2.dev202509120804.dist-info/RECORD +0 -172
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512040805.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,304 @@
|
|
|
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
|
+
"""Common base class for environment features.
|
|
15
|
+
|
|
16
|
+
This module provides an base class `BaseFeature` for environment features,
|
|
17
|
+
which provides event handlers for the feature lifecycle events, which can be
|
|
18
|
+
overridden by subclasses to provide custom behaviors. Please note that this base
|
|
19
|
+
class is intended to provide a convenient way to implement features, and not
|
|
20
|
+
all feature implementations need to subclass it. Also `BaseFeature` is not
|
|
21
|
+
coupled with `BaseEnvironment` and `BaseSandbox`, and is expected to work with
|
|
22
|
+
the `Environment` and `Sandbox` interfaces directly.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
import contextlib
|
|
26
|
+
import functools
|
|
27
|
+
import os
|
|
28
|
+
import re
|
|
29
|
+
import time
|
|
30
|
+
from typing import Annotated, Any, Callable, Iterator
|
|
31
|
+
|
|
32
|
+
from langfun.env import interface
|
|
33
|
+
import pyglove as pg
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BaseFeature(interface.Feature):
|
|
37
|
+
"""Common base class for environment features."""
|
|
38
|
+
|
|
39
|
+
is_sandbox_based: Annotated[
|
|
40
|
+
bool,
|
|
41
|
+
'Whether the feature is sandbox-based.'
|
|
42
|
+
] = True
|
|
43
|
+
|
|
44
|
+
applicable_images: Annotated[
|
|
45
|
+
list[str],
|
|
46
|
+
(
|
|
47
|
+
'A list of regular expressions for image IDs which enable '
|
|
48
|
+
'this feature. By default, all images are enabled.'
|
|
49
|
+
)
|
|
50
|
+
] = ['.*']
|
|
51
|
+
|
|
52
|
+
housekeep_interval: Annotated[
|
|
53
|
+
float | None,
|
|
54
|
+
'Interval in seconds for feature housekeeping.'
|
|
55
|
+
] = None
|
|
56
|
+
|
|
57
|
+
#
|
|
58
|
+
# Subclasses can override:
|
|
59
|
+
#
|
|
60
|
+
|
|
61
|
+
def _setup(self) -> None:
|
|
62
|
+
"""Subclasses can override this for custom setup.
|
|
63
|
+
|
|
64
|
+
NOTE: always call super()._setup() at the beginning of the implementation.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
def _teardown(self) -> None:
|
|
68
|
+
"""Subclasses can override this for custom teardown.
|
|
69
|
+
|
|
70
|
+
NOTE: always call super()._teardown() at the end of the implementation.
|
|
71
|
+
"""
|
|
72
|
+
|
|
73
|
+
def _setup_session(self) -> None:
|
|
74
|
+
"""Subclasses can override this for custom setup session.
|
|
75
|
+
|
|
76
|
+
NOTE: always call super()._setup_session() at the beginning of the
|
|
77
|
+
implementation.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
def _teardown_session(self) -> None:
|
|
81
|
+
"""Subclasses can override this for custom teardown session.
|
|
82
|
+
|
|
83
|
+
NOTE: always call super()._teardown_session() at the end of the
|
|
84
|
+
implementation.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
def _housekeep(self) -> None:
|
|
88
|
+
"""Performs housekeeping for the feature.
|
|
89
|
+
|
|
90
|
+
NOTE: always call super()._housekeep() at the beginning of the
|
|
91
|
+
implementation.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
#
|
|
95
|
+
# Init and properties
|
|
96
|
+
#
|
|
97
|
+
|
|
98
|
+
def _on_bound(self) -> None:
|
|
99
|
+
"""Called when the feature is bound."""
|
|
100
|
+
super()._on_bound()
|
|
101
|
+
self._sandbox = None
|
|
102
|
+
self._housekeep_counter = 0
|
|
103
|
+
|
|
104
|
+
@functools.cached_property
|
|
105
|
+
def name(self) -> str:
|
|
106
|
+
"""Returns the name of the feature."""
|
|
107
|
+
assert isinstance(self.sym_parent, dict), 'Feature is not put into a dict.'
|
|
108
|
+
return self.sym_path.key
|
|
109
|
+
|
|
110
|
+
def _on_parent_change(
|
|
111
|
+
self,
|
|
112
|
+
old_parent: pg.Symbolic | None,
|
|
113
|
+
new_parent: pg.Symbolic | None
|
|
114
|
+
) -> None:
|
|
115
|
+
"""Called when the feature is bound."""
|
|
116
|
+
super()._on_parent_change(old_parent, new_parent)
|
|
117
|
+
self.__dict__.pop('name', None)
|
|
118
|
+
|
|
119
|
+
@functools.cached_property
|
|
120
|
+
def environment(self) -> interface.Environment:
|
|
121
|
+
"""Returns the environment that the feature is running in."""
|
|
122
|
+
if self._sandbox is not None:
|
|
123
|
+
return self._sandbox.environment
|
|
124
|
+
env = self.sym_ancestor(lambda v: isinstance(v, interface.Environment))
|
|
125
|
+
assert env is not None, 'Feature is not put into an environment.'
|
|
126
|
+
return env
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def sandbox(self) -> interface.Sandbox | None:
|
|
130
|
+
"""Returns the sandbox that the feature is running in."""
|
|
131
|
+
assert self._sandbox is not None or not self.is_sandbox_based, (
|
|
132
|
+
'Feature has not been set up yet.'
|
|
133
|
+
)
|
|
134
|
+
return self._sandbox
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def working_dir(self) -> str | None:
|
|
138
|
+
"""Returns the working directory of the feature."""
|
|
139
|
+
sandbox_workdir = self.sandbox.working_dir
|
|
140
|
+
if sandbox_workdir is None:
|
|
141
|
+
return None
|
|
142
|
+
return os.path.join(sandbox_workdir, self.name)
|
|
143
|
+
|
|
144
|
+
def is_applicable(self, image_id: str) -> bool:
|
|
145
|
+
"""Returns True if the feature is applicable to the given image."""
|
|
146
|
+
return any(
|
|
147
|
+
re.fullmatch(regex, image_id) for regex in self.applicable_images
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
#
|
|
151
|
+
# Setup and teardown of the feature.
|
|
152
|
+
#
|
|
153
|
+
|
|
154
|
+
def _do(
|
|
155
|
+
self,
|
|
156
|
+
action: Callable[[], None],
|
|
157
|
+
event_handler: Callable[..., None],
|
|
158
|
+
) -> None:
|
|
159
|
+
"""Triggers an event handler."""
|
|
160
|
+
error = None
|
|
161
|
+
start_time = time.time()
|
|
162
|
+
try:
|
|
163
|
+
action()
|
|
164
|
+
except BaseException as e: # pylint: disable=broad-except
|
|
165
|
+
error = e
|
|
166
|
+
raise
|
|
167
|
+
finally:
|
|
168
|
+
event_handler(duration=time.time() - start_time, error=error)
|
|
169
|
+
|
|
170
|
+
def setup(self, sandbox: interface.Sandbox | None = None) -> None:
|
|
171
|
+
"""Sets up the feature."""
|
|
172
|
+
self._sandbox = sandbox
|
|
173
|
+
self._do(self._setup, self.on_setup)
|
|
174
|
+
|
|
175
|
+
def teardown(self) -> None:
|
|
176
|
+
"""Tears down the feature."""
|
|
177
|
+
self._do(self._teardown, event_handler=self.on_teardown)
|
|
178
|
+
|
|
179
|
+
def setup_session(self) -> None:
|
|
180
|
+
"""Sets up the feature for a user session."""
|
|
181
|
+
self._do(self._setup_session, event_handler=self.on_setup_session)
|
|
182
|
+
|
|
183
|
+
def teardown_session(self) -> None:
|
|
184
|
+
"""Teardowns the feature for a user session."""
|
|
185
|
+
self._do(self._teardown_session, self.on_teardown_session)
|
|
186
|
+
|
|
187
|
+
#
|
|
188
|
+
# Housekeeping.
|
|
189
|
+
#
|
|
190
|
+
|
|
191
|
+
def housekeep(self) -> None:
|
|
192
|
+
"""Performs housekeeping for the feature."""
|
|
193
|
+
try:
|
|
194
|
+
self._do(self._housekeep, self.on_housekeep)
|
|
195
|
+
finally:
|
|
196
|
+
self._housekeep_counter += 1
|
|
197
|
+
|
|
198
|
+
#
|
|
199
|
+
# Event handlers subclasses can override.
|
|
200
|
+
#
|
|
201
|
+
|
|
202
|
+
def on_setup(
|
|
203
|
+
self,
|
|
204
|
+
duration: float,
|
|
205
|
+
error: BaseException | None = None
|
|
206
|
+
) -> None:
|
|
207
|
+
"""Called when the feature is setup."""
|
|
208
|
+
self.environment.event_handler.on_feature_setup(
|
|
209
|
+
feature=self,
|
|
210
|
+
duration=duration,
|
|
211
|
+
error=error
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def on_teardown(
|
|
215
|
+
self,
|
|
216
|
+
duration: float,
|
|
217
|
+
error: BaseException | None = None
|
|
218
|
+
) -> None:
|
|
219
|
+
"""Called when the feature is teardown."""
|
|
220
|
+
self.environment.event_handler.on_feature_teardown(
|
|
221
|
+
feature=self,
|
|
222
|
+
duration=duration,
|
|
223
|
+
error=error
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
def on_housekeep(
|
|
227
|
+
self,
|
|
228
|
+
duration: float,
|
|
229
|
+
error: BaseException | None = None,
|
|
230
|
+
**kwargs
|
|
231
|
+
) -> None:
|
|
232
|
+
"""Called when the feature has done housekeeping."""
|
|
233
|
+
self.environment.event_handler.on_feature_housekeep(
|
|
234
|
+
feature=self,
|
|
235
|
+
counter=self._housekeep_counter,
|
|
236
|
+
duration=duration,
|
|
237
|
+
error=error,
|
|
238
|
+
**kwargs
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def on_setup_session(
|
|
242
|
+
self,
|
|
243
|
+
duration: float,
|
|
244
|
+
error: BaseException | None = None,
|
|
245
|
+
) -> None:
|
|
246
|
+
"""Called when the feature is setup for a user session."""
|
|
247
|
+
self.environment.event_handler.on_feature_setup_session(
|
|
248
|
+
feature=self,
|
|
249
|
+
session_id=self.session_id,
|
|
250
|
+
duration=duration,
|
|
251
|
+
error=error
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
def on_teardown_session(
|
|
255
|
+
self,
|
|
256
|
+
duration: float,
|
|
257
|
+
error: BaseException | None = None,
|
|
258
|
+
) -> None:
|
|
259
|
+
"""Called when the feature is teardown for a user session."""
|
|
260
|
+
self.environment.event_handler.on_feature_teardown_session(
|
|
261
|
+
feature=self,
|
|
262
|
+
session_id=self.session_id,
|
|
263
|
+
duration=duration,
|
|
264
|
+
error=error
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
def on_activity(
|
|
268
|
+
self,
|
|
269
|
+
name: str,
|
|
270
|
+
duration: float,
|
|
271
|
+
error: BaseException | None = None,
|
|
272
|
+
**kwargs
|
|
273
|
+
) -> None:
|
|
274
|
+
"""Called when a sandbox activity is performed."""
|
|
275
|
+
self.environment.event_handler.on_feature_activity(
|
|
276
|
+
name=f'{self.name}.{name}',
|
|
277
|
+
feature=self,
|
|
278
|
+
session_id=self.session_id,
|
|
279
|
+
duration=duration,
|
|
280
|
+
error=error,
|
|
281
|
+
**kwargs
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
@contextlib.contextmanager
|
|
285
|
+
def track_activity(
|
|
286
|
+
self,
|
|
287
|
+
name: str,
|
|
288
|
+
**kwargs: Any
|
|
289
|
+
) -> Iterator[None]:
|
|
290
|
+
"""Context manager that tracks a feature activity."""
|
|
291
|
+
start_time = time.time()
|
|
292
|
+
error = None
|
|
293
|
+
try:
|
|
294
|
+
yield None
|
|
295
|
+
except BaseException as e: # pylint: disable=broad-except
|
|
296
|
+
error = e
|
|
297
|
+
raise
|
|
298
|
+
finally:
|
|
299
|
+
self.on_activity(
|
|
300
|
+
name=name,
|
|
301
|
+
duration=time.time() - start_time,
|
|
302
|
+
error=error,
|
|
303
|
+
**kwargs
|
|
304
|
+
)
|
|
@@ -0,0 +1,228 @@
|
|
|
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 unittest
|
|
15
|
+
|
|
16
|
+
from langfun.env import test_utils
|
|
17
|
+
|
|
18
|
+
TestingEnvironment = test_utils.TestingEnvironment
|
|
19
|
+
TestingNonSandboxBasedFeature = test_utils.TestingNonSandboxBasedFeature
|
|
20
|
+
TestingEventHandler = test_utils.TestingEventHandler
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class NonSandboxBasedFeatureTests(unittest.TestCase):
|
|
24
|
+
|
|
25
|
+
def test_basics(self):
|
|
26
|
+
feature = TestingNonSandboxBasedFeature()
|
|
27
|
+
event_handler = TestingEventHandler(
|
|
28
|
+
log_session_setup=True,
|
|
29
|
+
log_feature_setup=True,
|
|
30
|
+
log_sandbox_status=True
|
|
31
|
+
)
|
|
32
|
+
env = TestingEnvironment(
|
|
33
|
+
image_ids=[],
|
|
34
|
+
features={'test_feature': feature},
|
|
35
|
+
event_handler=event_handler,
|
|
36
|
+
)
|
|
37
|
+
self.assertFalse(env.is_online)
|
|
38
|
+
self.assertEqual(len(list(env.non_sandbox_based_features())), 1)
|
|
39
|
+
with env:
|
|
40
|
+
self.assertTrue(env.is_online)
|
|
41
|
+
with env.test_feature('session1') as feature:
|
|
42
|
+
self.assertIsNone(feature.sandbox)
|
|
43
|
+
self.assertEqual(feature.session_id, 'session1')
|
|
44
|
+
|
|
45
|
+
self.assertEqual(
|
|
46
|
+
event_handler.logs,
|
|
47
|
+
[
|
|
48
|
+
'[testing-env/test_feature] feature setup',
|
|
49
|
+
'[testing-env] environment started',
|
|
50
|
+
'[testing-env/test_feature@session1] feature setup session',
|
|
51
|
+
'[testing-env/test_feature@session1] feature teardown session',
|
|
52
|
+
'[testing-env/test_feature] feature teardown',
|
|
53
|
+
'[testing-env] environment shutdown'
|
|
54
|
+
]
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
def test_feature_setup_error(self):
|
|
58
|
+
event_handler = TestingEventHandler(
|
|
59
|
+
log_session_setup=True,
|
|
60
|
+
log_feature_setup=True,
|
|
61
|
+
log_sandbox_status=True
|
|
62
|
+
)
|
|
63
|
+
env = TestingEnvironment(
|
|
64
|
+
image_ids=[],
|
|
65
|
+
features={
|
|
66
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
67
|
+
simulate_setup_error=ValueError
|
|
68
|
+
)
|
|
69
|
+
},
|
|
70
|
+
event_handler=event_handler,
|
|
71
|
+
)
|
|
72
|
+
with self.assertRaises(ValueError):
|
|
73
|
+
with env:
|
|
74
|
+
pass
|
|
75
|
+
self.assertEqual(
|
|
76
|
+
event_handler.logs,
|
|
77
|
+
[
|
|
78
|
+
'[testing-env/test_feature] feature setup with ValueError',
|
|
79
|
+
'[testing-env] environment started with ValueError',
|
|
80
|
+
'[testing-env/test_feature] feature teardown',
|
|
81
|
+
'[testing-env] environment shutdown'
|
|
82
|
+
]
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
def test_feature_teardown_error(self):
|
|
86
|
+
event_handler = TestingEventHandler(
|
|
87
|
+
log_session_setup=True,
|
|
88
|
+
log_feature_setup=True,
|
|
89
|
+
log_sandbox_status=True
|
|
90
|
+
)
|
|
91
|
+
env = TestingEnvironment(
|
|
92
|
+
image_ids=[],
|
|
93
|
+
features={
|
|
94
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
95
|
+
simulate_teardown_error=ValueError
|
|
96
|
+
)
|
|
97
|
+
},
|
|
98
|
+
event_handler=event_handler,
|
|
99
|
+
)
|
|
100
|
+
with env:
|
|
101
|
+
pass
|
|
102
|
+
self.assertEqual(
|
|
103
|
+
event_handler.logs,
|
|
104
|
+
[
|
|
105
|
+
'[testing-env/test_feature] feature setup',
|
|
106
|
+
'[testing-env] environment started',
|
|
107
|
+
'[testing-env/test_feature] feature teardown with ValueError',
|
|
108
|
+
'[testing-env] environment shutdown'
|
|
109
|
+
]
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
def test_feature_setup_session_error(self):
|
|
113
|
+
event_handler = TestingEventHandler(
|
|
114
|
+
log_session_setup=True,
|
|
115
|
+
log_feature_setup=True,
|
|
116
|
+
log_sandbox_status=True
|
|
117
|
+
)
|
|
118
|
+
env = TestingEnvironment(
|
|
119
|
+
image_ids=[],
|
|
120
|
+
features={
|
|
121
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
122
|
+
simulate_setup_session_error=ValueError
|
|
123
|
+
)
|
|
124
|
+
},
|
|
125
|
+
event_handler=event_handler,
|
|
126
|
+
)
|
|
127
|
+
with env:
|
|
128
|
+
with self.assertRaises(ValueError):
|
|
129
|
+
with env.test_feature('session1'):
|
|
130
|
+
pass
|
|
131
|
+
self.assertEqual(
|
|
132
|
+
event_handler.logs,
|
|
133
|
+
[
|
|
134
|
+
# pylint: disable=line-too-long
|
|
135
|
+
'[testing-env/test_feature] feature setup',
|
|
136
|
+
'[testing-env] environment started',
|
|
137
|
+
'[testing-env/test_feature@session1] feature setup session with ValueError',
|
|
138
|
+
'[testing-env/test_feature@session1] feature teardown session',
|
|
139
|
+
'[testing-env/test_feature] feature teardown',
|
|
140
|
+
'[testing-env] environment shutdown',
|
|
141
|
+
# pylint: enable=line-too-long
|
|
142
|
+
]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def test_feature_teardown_session_error(self):
|
|
146
|
+
event_handler = TestingEventHandler(
|
|
147
|
+
log_session_setup=True,
|
|
148
|
+
log_feature_setup=True,
|
|
149
|
+
log_sandbox_status=True
|
|
150
|
+
)
|
|
151
|
+
env = TestingEnvironment(
|
|
152
|
+
image_ids=[],
|
|
153
|
+
features={
|
|
154
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
155
|
+
simulate_teardown_session_error=ValueError
|
|
156
|
+
)
|
|
157
|
+
},
|
|
158
|
+
event_handler=event_handler,
|
|
159
|
+
)
|
|
160
|
+
with env:
|
|
161
|
+
with env.test_feature('session1'):
|
|
162
|
+
pass
|
|
163
|
+
self.assertEqual(
|
|
164
|
+
event_handler.logs,
|
|
165
|
+
[
|
|
166
|
+
# pylint: disable=line-too-long
|
|
167
|
+
'[testing-env/test_feature] feature setup',
|
|
168
|
+
'[testing-env] environment started',
|
|
169
|
+
'[testing-env/test_feature@session1] feature setup session',
|
|
170
|
+
'[testing-env/test_feature@session1] feature teardown session with ValueError',
|
|
171
|
+
'[testing-env/test_feature] feature teardown',
|
|
172
|
+
'[testing-env] environment shutdown',
|
|
173
|
+
# pylint: enable=line-too-long
|
|
174
|
+
]
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
def test_feature_housekeeping(self):
|
|
178
|
+
event_handler = TestingEventHandler(
|
|
179
|
+
log_sandbox_status=False,
|
|
180
|
+
log_feature_setup=False,
|
|
181
|
+
log_housekeep=True
|
|
182
|
+
)
|
|
183
|
+
env = TestingEnvironment(
|
|
184
|
+
image_ids=[],
|
|
185
|
+
features={
|
|
186
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
187
|
+
housekeep_interval=0.1
|
|
188
|
+
)
|
|
189
|
+
},
|
|
190
|
+
event_handler=event_handler,
|
|
191
|
+
housekeep_interval=0.2
|
|
192
|
+
)
|
|
193
|
+
with env:
|
|
194
|
+
env.wait_for_housekeeping()
|
|
195
|
+
self.assertIn(
|
|
196
|
+
'[testing-env/test_feature] feature housekeeping 0',
|
|
197
|
+
event_handler.logs
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def test_feature_housekeeping_error(self):
|
|
201
|
+
event_handler = TestingEventHandler(
|
|
202
|
+
log_sandbox_status=False,
|
|
203
|
+
log_feature_setup=False,
|
|
204
|
+
log_housekeep=True
|
|
205
|
+
)
|
|
206
|
+
env = TestingEnvironment(
|
|
207
|
+
image_ids=[],
|
|
208
|
+
features={
|
|
209
|
+
'test_feature': TestingNonSandboxBasedFeature(
|
|
210
|
+
simulate_housekeep_error=ValueError,
|
|
211
|
+
housekeep_interval=0.1
|
|
212
|
+
)
|
|
213
|
+
},
|
|
214
|
+
event_handler=event_handler,
|
|
215
|
+
housekeep_interval=0.2
|
|
216
|
+
)
|
|
217
|
+
with env:
|
|
218
|
+
env.wait_for_housekeeping()
|
|
219
|
+
self.assertFalse(env.is_online)
|
|
220
|
+
self.assertIn(
|
|
221
|
+
'[testing-env/test_feature] feature housekeeping 0 with ValueError',
|
|
222
|
+
event_handler.logs
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == '__main__':
|
|
227
|
+
unittest.main()
|
|
228
|
+
|