langfun 0.1.2.dev202510200805__py3-none-any.whl → 0.1.2.dev202511160804__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.
Potentially problematic release.
This version of langfun might be problematic. Click here for more details.
- langfun/core/__init__.py +1 -0
- langfun/core/agentic/action.py +107 -12
- langfun/core/agentic/action_eval.py +9 -2
- langfun/core/agentic/action_test.py +25 -0
- langfun/core/async_support.py +32 -3
- 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 +1 -0
- 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 +9 -2
- langfun/core/data/conversion/gemini_test.py +12 -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 +4 -4
- 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 +1 -0
- langfun/core/eval/v2/checkpointing.py +39 -5
- langfun/core/eval/v2/checkpointing_test.py +1 -1
- langfun/core/eval/v2/eval_test_helper.py +97 -1
- langfun/core/eval/v2/evaluation.py +88 -16
- langfun/core/eval/v2/evaluation_test.py +9 -3
- langfun/core/eval/v2/example.py +45 -39
- langfun/core/eval/v2/example_test.py +3 -3
- langfun/core/eval/v2/experiment.py +51 -8
- 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 +30 -1
- langfun/core/eval/v2/progress_test.py +27 -0
- langfun/core/eval/v2/progress_tracking_test.py +3 -0
- langfun/core/eval/v2/reporting.py +90 -71
- langfun/core/eval/v2/reporting_test.py +20 -6
- langfun/core/eval/v2/runners/__init__.py +26 -0
- langfun/core/eval/v2/{runners.py → runners/base.py} +22 -124
- langfun/core/eval/v2/runners/debug.py +40 -0
- langfun/core/eval/v2/runners/debug_test.py +79 -0
- langfun/core/eval/v2/runners/parallel.py +100 -0
- langfun/core/eval/v2/runners/parallel_test.py +98 -0
- langfun/core/eval/v2/runners/sequential.py +47 -0
- langfun/core/eval/v2/runners/sequential_test.py +175 -0
- langfun/core/langfunc.py +45 -130
- langfun/core/langfunc_test.py +6 -4
- langfun/core/language_model.py +103 -16
- langfun/core/language_model_test.py +9 -3
- langfun/core/llms/__init__.py +7 -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 +14 -9
- langfun/core/llms/google_genai.py +29 -1
- langfun/core/llms/groq.py +28 -3
- langfun/core/llms/llama_cpp.py +23 -4
- langfun/core/llms/openai.py +36 -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 +12 -1
- langfun/core/llms/vertexai.py +51 -8
- langfun/core/logging.py +1 -1
- langfun/core/mcp/client.py +77 -22
- langfun/core/mcp/client_test.py +8 -35
- langfun/core/mcp/session.py +94 -29
- langfun/core/mcp/session_test.py +54 -0
- langfun/core/mcp/tool.py +151 -22
- 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 +19 -1
- langfun/core/modalities/mime.py +62 -3
- 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 +215 -142
- langfun/core/structured/querying_test.py +65 -29
- langfun/core/structured/schema/__init__.py +48 -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 +8 -2
- langfun/env/base_environment.py +320 -128
- langfun/env/base_environment_test.py +473 -0
- langfun/env/base_feature.py +92 -15
- langfun/env/base_feature_test.py +228 -0
- langfun/env/base_sandbox.py +84 -361
- langfun/env/base_sandbox_test.py +1235 -0
- langfun/env/event_handlers/__init__.py +1 -1
- langfun/env/event_handlers/chain.py +233 -0
- langfun/env/event_handlers/chain_test.py +253 -0
- langfun/env/event_handlers/event_logger.py +95 -98
- langfun/env/event_handlers/event_logger_test.py +21 -21
- langfun/env/event_handlers/metric_writer.py +225 -140
- langfun/env/event_handlers/metric_writer_test.py +23 -6
- langfun/env/interface.py +854 -40
- langfun/env/interface_test.py +112 -2
- langfun/env/load_balancers_test.py +23 -2
- langfun/env/test_utils.py +126 -84
- {langfun-0.1.2.dev202510200805.dist-info → langfun-0.1.2.dev202511160804.dist-info}/METADATA +1 -1
- langfun-0.1.2.dev202511160804.dist-info/RECORD +211 -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/env/base_test.py +0 -1481
- langfun/env/event_handlers/base.py +0 -350
- langfun-0.1.2.dev202510200805.dist-info/RECORD +0 -195
- {langfun-0.1.2.dev202510200805.dist-info → langfun-0.1.2.dev202511160804.dist-info}/WHEEL +0 -0
- {langfun-0.1.2.dev202510200805.dist-info → langfun-0.1.2.dev202511160804.dist-info}/licenses/LICENSE +0 -0
- {langfun-0.1.2.dev202510200805.dist-info → langfun-0.1.2.dev202511160804.dist-info}/top_level.txt +0 -0
langfun/env/interface_test.py
CHANGED
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
+
import contextlib
|
|
15
|
+
from typing import Iterator
|
|
14
16
|
import unittest
|
|
15
17
|
from langfun.env import interface
|
|
16
18
|
|
|
@@ -29,15 +31,123 @@ class IdTest(unittest.TestCase):
|
|
|
29
31
|
def test_sandbox_id(self):
|
|
30
32
|
sandbox_id = interface.Sandbox.Id(
|
|
31
33
|
environment_id=interface.Environment.Id('env'),
|
|
34
|
+
image_id='image:2025_01_01_00_00_00',
|
|
32
35
|
sandbox_id='sandbox'
|
|
33
36
|
)
|
|
34
|
-
self.assertEqual(str(sandbox_id), 'env/sandbox')
|
|
37
|
+
self.assertEqual(str(sandbox_id), 'env/image:2025_01_01_00_00_00:sandbox')
|
|
35
38
|
self.assertEqual(
|
|
36
39
|
sandbox_id.working_dir(root_dir='/tmp'),
|
|
37
|
-
'/tmp/env/sandbox'
|
|
40
|
+
'/tmp/env/image_2025_01_01_00_00_00/sandbox'
|
|
38
41
|
)
|
|
39
42
|
self.assertIsNone(sandbox_id.working_dir(root_dir=None))
|
|
40
43
|
|
|
44
|
+
def test_feature_id(self):
|
|
45
|
+
# For non-sandboxed feature.
|
|
46
|
+
feature_id = interface.Feature.Id(
|
|
47
|
+
container_id=interface.Environment.Id('env'),
|
|
48
|
+
feature_name='feature'
|
|
49
|
+
)
|
|
50
|
+
self.assertEqual(str(feature_id), 'env/feature')
|
|
51
|
+
self.assertEqual(
|
|
52
|
+
feature_id.working_dir(root_dir='/tmp'),
|
|
53
|
+
'/tmp/env/feature'
|
|
54
|
+
)
|
|
55
|
+
self.assertIsNone(feature_id.working_dir(root_dir=None))
|
|
56
|
+
|
|
57
|
+
# For sandboxed feature.
|
|
58
|
+
feature_id = interface.Feature.Id(
|
|
59
|
+
container_id=interface.Sandbox.Id(
|
|
60
|
+
environment_id=interface.Environment.Id('env'),
|
|
61
|
+
image_id='image1',
|
|
62
|
+
sandbox_id='0'
|
|
63
|
+
),
|
|
64
|
+
feature_name='feature'
|
|
65
|
+
)
|
|
66
|
+
self.assertEqual(str(feature_id), 'env/image1:0/feature')
|
|
67
|
+
self.assertEqual(
|
|
68
|
+
feature_id.working_dir(root_dir='/tmp'),
|
|
69
|
+
'/tmp/env/image1/0/feature'
|
|
70
|
+
)
|
|
71
|
+
self.assertIsNone(feature_id.working_dir(root_dir=None))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class TestingSandbox(interface.Sandbox):
|
|
75
|
+
|
|
76
|
+
id: interface.Sandbox.Id = interface.Sandbox.Id(
|
|
77
|
+
environment_id=interface.Environment.Id('env'),
|
|
78
|
+
image_id='test_image',
|
|
79
|
+
sandbox_id='0:0'
|
|
80
|
+
)
|
|
81
|
+
image_id: str = 'test_image'
|
|
82
|
+
features: dict[str, interface.Feature] = {}
|
|
83
|
+
status: interface.Sandbox.Status = interface.Sandbox.Status.READY
|
|
84
|
+
session_id: str | None = None
|
|
85
|
+
|
|
86
|
+
__test__ = False
|
|
87
|
+
|
|
88
|
+
def environment(self) -> interface.Environment:
|
|
89
|
+
pass
|
|
90
|
+
|
|
91
|
+
def _on_bound(self) -> None:
|
|
92
|
+
self.activities = []
|
|
93
|
+
|
|
94
|
+
def report_state_error(self, error: interface.SandboxStateError) -> None:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
def start(self) -> None:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
def shutdown(self) -> None:
|
|
101
|
+
pass
|
|
102
|
+
|
|
103
|
+
def start_session(self, session_id: str) -> None:
|
|
104
|
+
pass
|
|
105
|
+
|
|
106
|
+
def end_session(self, shutdown_sandbox: bool = False) -> None:
|
|
107
|
+
pass
|
|
108
|
+
|
|
109
|
+
@contextlib.contextmanager
|
|
110
|
+
def track_activity(
|
|
111
|
+
self,
|
|
112
|
+
name: str,
|
|
113
|
+
feature: interface.Feature | None = None,
|
|
114
|
+
**kwargs
|
|
115
|
+
) -> Iterator[None]:
|
|
116
|
+
error = None
|
|
117
|
+
try:
|
|
118
|
+
yield
|
|
119
|
+
except BaseException as e:
|
|
120
|
+
error = e
|
|
121
|
+
raise
|
|
122
|
+
finally:
|
|
123
|
+
self.activities.append((name, error, kwargs))
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class DecoratorTest(unittest.TestCase):
|
|
127
|
+
|
|
128
|
+
def test_treat_as_sandbox_state_error(self):
|
|
129
|
+
|
|
130
|
+
class SandboxA(TestingSandbox):
|
|
131
|
+
|
|
132
|
+
@interface.treat_as_sandbox_state_error(errors=(ValueError,))
|
|
133
|
+
def foo(self, bar: str) -> None:
|
|
134
|
+
raise ValueError(bar)
|
|
135
|
+
|
|
136
|
+
with self.assertRaises(interface.SandboxStateError):
|
|
137
|
+
SandboxA().foo('foo')
|
|
138
|
+
|
|
139
|
+
def test_log_sandbox_activity(self):
|
|
140
|
+
|
|
141
|
+
class SandboxB(TestingSandbox):
|
|
142
|
+
|
|
143
|
+
@interface.log_activity()
|
|
144
|
+
def bar(self, x: str) -> None:
|
|
145
|
+
pass
|
|
146
|
+
|
|
147
|
+
sb = SandboxB()
|
|
148
|
+
sb.bar('foo')
|
|
149
|
+
self.assertEqual(sb.activities, [('bar', None, {'x': 'foo'})])
|
|
150
|
+
|
|
41
151
|
|
|
42
152
|
if __name__ == '__main__':
|
|
43
153
|
unittest.main()
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
14
|
import concurrent.futures
|
|
15
|
+
import contextlib
|
|
15
16
|
import time
|
|
17
|
+
from typing import Any, Iterator
|
|
16
18
|
import unittest
|
|
17
19
|
|
|
18
20
|
from langfun.env import interface
|
|
@@ -22,6 +24,9 @@ from langfun.env import load_balancers
|
|
|
22
24
|
class TestingSandbox(interface.Sandbox):
|
|
23
25
|
sandbox_id: str
|
|
24
26
|
status: interface.Sandbox.Status = interface.Sandbox.Status.READY
|
|
27
|
+
image_id: str = 'test_image'
|
|
28
|
+
|
|
29
|
+
__test__ = False
|
|
25
30
|
|
|
26
31
|
def _on_bound(self) -> None:
|
|
27
32
|
super()._on_bound()
|
|
@@ -31,6 +36,7 @@ class TestingSandbox(interface.Sandbox):
|
|
|
31
36
|
def id(self) -> interface.Sandbox.Id:
|
|
32
37
|
return interface.Sandbox.Id(
|
|
33
38
|
environment_id=interface.Environment.Id('testing-env'),
|
|
39
|
+
image_id=self.image_id,
|
|
34
40
|
sandbox_id=self.sandbox_id
|
|
35
41
|
)
|
|
36
42
|
|
|
@@ -46,6 +52,9 @@ class TestingSandbox(interface.Sandbox):
|
|
|
46
52
|
def state_errors(self) -> list[interface.SandboxStateError]:
|
|
47
53
|
return []
|
|
48
54
|
|
|
55
|
+
def report_state_error(self, error: interface.SandboxStateError) -> None:
|
|
56
|
+
pass
|
|
57
|
+
|
|
49
58
|
def set_status(self, status: interface.Sandbox.Status) -> None:
|
|
50
59
|
self.rebind(status=status, skip_notification=True)
|
|
51
60
|
|
|
@@ -53,10 +62,10 @@ class TestingSandbox(interface.Sandbox):
|
|
|
53
62
|
self.set_status(self.Status.ACQUIRED)
|
|
54
63
|
|
|
55
64
|
def start(self) -> None:
|
|
56
|
-
|
|
65
|
+
pass
|
|
57
66
|
|
|
58
67
|
def shutdown(self) -> None:
|
|
59
|
-
|
|
68
|
+
pass
|
|
60
69
|
|
|
61
70
|
def start_session(self, session_id: str) -> None:
|
|
62
71
|
self._session_id = session_id
|
|
@@ -68,6 +77,18 @@ class TestingSandbox(interface.Sandbox):
|
|
|
68
77
|
def session_id(self) -> str | None:
|
|
69
78
|
return self._session_id
|
|
70
79
|
|
|
80
|
+
@contextlib.contextmanager
|
|
81
|
+
def track_activity(
|
|
82
|
+
self,
|
|
83
|
+
name: str,
|
|
84
|
+
feature: interface.Feature | None = None,
|
|
85
|
+
**kwargs: Any
|
|
86
|
+
) -> Iterator[None]:
|
|
87
|
+
try:
|
|
88
|
+
yield
|
|
89
|
+
finally:
|
|
90
|
+
pass
|
|
91
|
+
|
|
71
92
|
|
|
72
93
|
class RoundRobinTest(unittest.TestCase):
|
|
73
94
|
|
langfun/env/test_utils.py
CHANGED
|
@@ -21,19 +21,20 @@ from langfun.env import base_environment
|
|
|
21
21
|
from langfun.env import base_feature
|
|
22
22
|
from langfun.env import base_sandbox
|
|
23
23
|
from langfun.env import interface
|
|
24
|
-
from langfun.env.event_handlers import base as event_handler_base
|
|
25
24
|
import pyglove as pg
|
|
26
25
|
|
|
27
26
|
|
|
28
27
|
class TestingEnvironment(base_environment.BaseEnvironment):
|
|
29
28
|
"""Testing environment for unit tests."""
|
|
30
|
-
|
|
29
|
+
image_ids: list[str] = ['test_image']
|
|
31
30
|
housekeep_interval: float = 0.0
|
|
32
31
|
simulate_start_error: Type[BaseException] | None = None
|
|
33
32
|
simulate_shutdown_error: Type[BaseException] | None = None
|
|
34
33
|
simulate_ping_error: Type[BaseException] | None = None
|
|
35
34
|
offline: bool = False
|
|
36
35
|
|
|
36
|
+
__test__ = False
|
|
37
|
+
|
|
37
38
|
@property
|
|
38
39
|
def id(self) -> interface.Environment.Id:
|
|
39
40
|
return interface.Environment.Id('testing-env')
|
|
@@ -45,6 +46,7 @@ class TestingEnvironment(base_environment.BaseEnvironment):
|
|
|
45
46
|
|
|
46
47
|
def _create_sandbox(
|
|
47
48
|
self,
|
|
49
|
+
image_id: str,
|
|
48
50
|
sandbox_id: str,
|
|
49
51
|
reusable: bool,
|
|
50
52
|
proactive_session_setup: bool,
|
|
@@ -54,8 +56,10 @@ class TestingEnvironment(base_environment.BaseEnvironment):
|
|
|
54
56
|
environment=self,
|
|
55
57
|
id=interface.Sandbox.Id(
|
|
56
58
|
environment_id=self.id,
|
|
59
|
+
image_id=image_id,
|
|
57
60
|
sandbox_id=sandbox_id
|
|
58
61
|
),
|
|
62
|
+
image_id=image_id,
|
|
59
63
|
reusable=reusable,
|
|
60
64
|
proactive_session_setup=proactive_session_setup,
|
|
61
65
|
keepalive_interval=keepalive_interval,
|
|
@@ -72,6 +76,8 @@ class TestingSandbox(base_sandbox.BaseSandbox):
|
|
|
72
76
|
simulate_shutdown_error: Type[BaseException] | None = None
|
|
73
77
|
simulate_ping_error: Type[BaseException] | None = None
|
|
74
78
|
|
|
79
|
+
__test__ = False
|
|
80
|
+
|
|
75
81
|
def _on_bound(self) -> None:
|
|
76
82
|
super()._on_bound()
|
|
77
83
|
self._shell_history = []
|
|
@@ -109,7 +115,8 @@ class TestingSandbox(base_sandbox.BaseSandbox):
|
|
|
109
115
|
self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
|
|
110
116
|
super()._shutdown()
|
|
111
117
|
|
|
112
|
-
@
|
|
118
|
+
@interface.treat_as_sandbox_state_error(errors=(RuntimeError,))
|
|
119
|
+
@interface.log_activity()
|
|
113
120
|
def shell(
|
|
114
121
|
self,
|
|
115
122
|
code: str,
|
|
@@ -138,6 +145,8 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
138
145
|
simulate_teardown_session_error: Type[BaseException] | None = None
|
|
139
146
|
call_end_session_on_teardown_session: bool = False
|
|
140
147
|
|
|
148
|
+
__test__ = False
|
|
149
|
+
|
|
141
150
|
class Service:
|
|
142
151
|
"""Sandbox."""
|
|
143
152
|
|
|
@@ -181,19 +190,19 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
181
190
|
if self.call_end_session_on_teardown_session:
|
|
182
191
|
self.sandbox.end_session()
|
|
183
192
|
|
|
184
|
-
@
|
|
193
|
+
@interface.log_activity()
|
|
185
194
|
def num_shell_calls(self) -> int:
|
|
186
195
|
return len(self.sandbox._shell_history) # pylint: disable=protected-access
|
|
187
196
|
|
|
188
|
-
@
|
|
197
|
+
@interface.log_activity()
|
|
189
198
|
def bad_shell_call(self) -> None:
|
|
190
199
|
self.sandbox.shell('bad command', raise_error=RuntimeError)
|
|
191
200
|
|
|
192
|
-
@
|
|
201
|
+
@interface.log_activity()
|
|
193
202
|
def show_session_id(self):
|
|
194
203
|
return self.session_id
|
|
195
204
|
|
|
196
|
-
@
|
|
205
|
+
@interface.log_activity()
|
|
197
206
|
def call_with_varargs(self, code: str, *args, **kwargs):
|
|
198
207
|
del code, args, kwargs
|
|
199
208
|
return 0
|
|
@@ -202,7 +211,6 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
202
211
|
super()._on_bound()
|
|
203
212
|
self._service = None
|
|
204
213
|
|
|
205
|
-
@base_sandbox.sandbox_service()
|
|
206
214
|
@contextlib.contextmanager
|
|
207
215
|
def my_service(self) -> Iterator[Service]:
|
|
208
216
|
try:
|
|
@@ -218,9 +226,46 @@ class TestingFeature(base_feature.BaseFeature):
|
|
|
218
226
|
)
|
|
219
227
|
|
|
220
228
|
|
|
221
|
-
class
|
|
222
|
-
|
|
223
|
-
|
|
229
|
+
class TestingNonSandboxBasedFeature(base_feature.BaseFeature):
|
|
230
|
+
"""Testing non-sandbox based feature for unit tests."""
|
|
231
|
+
is_sandbox_based: bool = False
|
|
232
|
+
simulate_setup_error: Type[BaseException] | None = None
|
|
233
|
+
simulate_teardown_error: Type[BaseException] | None = None
|
|
234
|
+
simulate_setup_session_error: Type[BaseException] | None = None
|
|
235
|
+
simulate_teardown_session_error: Type[BaseException] | None = None
|
|
236
|
+
simulate_housekeep_error: Type[BaseException] | None = None
|
|
237
|
+
|
|
238
|
+
__test__ = False
|
|
239
|
+
|
|
240
|
+
def _setup(self) -> None:
|
|
241
|
+
if self.simulate_setup_error:
|
|
242
|
+
raise self.simulate_setup_error('Feature setup error')
|
|
243
|
+
|
|
244
|
+
def _teardown(self) -> None:
|
|
245
|
+
if self.simulate_teardown_error:
|
|
246
|
+
raise self.simulate_teardown_error('Feature teardown error')
|
|
247
|
+
|
|
248
|
+
def _setup_session(self) -> None:
|
|
249
|
+
if self.simulate_setup_session_error:
|
|
250
|
+
raise self.simulate_setup_session_error('Feature session setup error')
|
|
251
|
+
|
|
252
|
+
def _teardown_session(self) -> None:
|
|
253
|
+
if self.simulate_teardown_session_error:
|
|
254
|
+
raise self.simulate_teardown_session_error(
|
|
255
|
+
'Feature session teardown error'
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
def _housekeep(self) -> None:
|
|
259
|
+
if self.simulate_housekeep_error:
|
|
260
|
+
raise self.simulate_housekeep_error('Feature housekeeping error')
|
|
261
|
+
_ = self.foo(1)
|
|
262
|
+
|
|
263
|
+
@interface.log_activity()
|
|
264
|
+
def foo(self, x: int) -> int:
|
|
265
|
+
return x + 1
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class TestingEventHandler(pg.Object, interface.EventHandler):
|
|
224
269
|
"""Testing environment event handler for unit tests."""
|
|
225
270
|
|
|
226
271
|
log_sandbox_status: bool = False
|
|
@@ -228,6 +273,8 @@ class TestingEventHandler(
|
|
|
228
273
|
log_session_setup: bool = False
|
|
229
274
|
log_housekeep: bool = False
|
|
230
275
|
|
|
276
|
+
__test__ = False
|
|
277
|
+
|
|
231
278
|
def _on_bound(self) -> None:
|
|
232
279
|
super()._on_bound()
|
|
233
280
|
self._logs = []
|
|
@@ -281,7 +328,6 @@ class TestingEventHandler(
|
|
|
281
328
|
|
|
282
329
|
def on_sandbox_start(
|
|
283
330
|
self,
|
|
284
|
-
environment: interface.Environment,
|
|
285
331
|
sandbox: interface.Sandbox,
|
|
286
332
|
duration: float,
|
|
287
333
|
error: BaseException | None
|
|
@@ -291,7 +337,6 @@ class TestingEventHandler(
|
|
|
291
337
|
|
|
292
338
|
def on_sandbox_status_change(
|
|
293
339
|
self,
|
|
294
|
-
environment: interface.Environment,
|
|
295
340
|
sandbox: interface.Sandbox,
|
|
296
341
|
old_status: interface.Sandbox.Status,
|
|
297
342
|
new_status: interface.Sandbox.Status,
|
|
@@ -306,7 +351,6 @@ class TestingEventHandler(
|
|
|
306
351
|
|
|
307
352
|
def on_sandbox_shutdown(
|
|
308
353
|
self,
|
|
309
|
-
environment: interface.Environment,
|
|
310
354
|
sandbox: interface.Sandbox,
|
|
311
355
|
duration: float,
|
|
312
356
|
lifetime: float,
|
|
@@ -315,9 +359,53 @@ class TestingEventHandler(
|
|
|
315
359
|
assert duration > 0 and lifetime is not None
|
|
316
360
|
self._add_message(f'[{sandbox.id}] sandbox shutdown', error)
|
|
317
361
|
|
|
362
|
+
def on_sandbox_session_start(
|
|
363
|
+
self,
|
|
364
|
+
sandbox: interface.Sandbox,
|
|
365
|
+
session_id: str,
|
|
366
|
+
duration: float,
|
|
367
|
+
error: BaseException | None
|
|
368
|
+
) -> None:
|
|
369
|
+
"""Called when a sandbox session starts."""
|
|
370
|
+
assert duration > 0
|
|
371
|
+
self._add_message(
|
|
372
|
+
f'[{sandbox.id}] session {session_id!r} started', error
|
|
373
|
+
)
|
|
374
|
+
|
|
375
|
+
def on_sandbox_session_end(
|
|
376
|
+
self,
|
|
377
|
+
sandbox: interface.Sandbox,
|
|
378
|
+
session_id: str,
|
|
379
|
+
duration: float,
|
|
380
|
+
lifetime: float,
|
|
381
|
+
error: BaseException | None
|
|
382
|
+
) -> None:
|
|
383
|
+
"""Called when a sandbox session ends."""
|
|
384
|
+
assert duration > 0 and lifetime > 0
|
|
385
|
+
self._add_message(
|
|
386
|
+
f'[{sandbox.id}] session {session_id!r} ended', error
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
def on_sandbox_activity(
|
|
390
|
+
self,
|
|
391
|
+
name: str,
|
|
392
|
+
sandbox: interface.Sandbox,
|
|
393
|
+
session_id: str | None,
|
|
394
|
+
duration: float,
|
|
395
|
+
error: BaseException | None,
|
|
396
|
+
*,
|
|
397
|
+
code: str | None = None,
|
|
398
|
+
**kwargs
|
|
399
|
+
) -> None:
|
|
400
|
+
"""Called when a sandbox activity is performed."""
|
|
401
|
+
del kwargs
|
|
402
|
+
log_id = f'{sandbox.id}@{session_id or "<idle>"}'
|
|
403
|
+
self._add_message(
|
|
404
|
+
f'[{log_id}] {name}: {code}', error
|
|
405
|
+
)
|
|
406
|
+
|
|
318
407
|
def on_sandbox_housekeep(
|
|
319
408
|
self,
|
|
320
|
-
environment: interface.Environment,
|
|
321
409
|
sandbox: interface.Sandbox,
|
|
322
410
|
counter: int,
|
|
323
411
|
duration: float,
|
|
@@ -332,8 +420,6 @@ class TestingEventHandler(
|
|
|
332
420
|
|
|
333
421
|
def on_feature_setup(
|
|
334
422
|
self,
|
|
335
|
-
environment: interface.Environment,
|
|
336
|
-
sandbox: interface.Sandbox,
|
|
337
423
|
feature: interface.Feature,
|
|
338
424
|
duration: float,
|
|
339
425
|
error: BaseException | None
|
|
@@ -342,13 +428,11 @@ class TestingEventHandler(
|
|
|
342
428
|
assert duration > 0
|
|
343
429
|
if self.log_feature_setup:
|
|
344
430
|
self._add_message(
|
|
345
|
-
f'[{
|
|
431
|
+
f'[{feature.id}] feature setup', error
|
|
346
432
|
)
|
|
347
433
|
|
|
348
434
|
def on_feature_teardown(
|
|
349
435
|
self,
|
|
350
|
-
environment: interface.Environment,
|
|
351
|
-
sandbox: interface.Sandbox,
|
|
352
436
|
feature: interface.Feature,
|
|
353
437
|
duration: float,
|
|
354
438
|
error: BaseException | None
|
|
@@ -357,13 +441,11 @@ class TestingEventHandler(
|
|
|
357
441
|
assert duration > 0
|
|
358
442
|
if self.log_feature_setup:
|
|
359
443
|
self._add_message(
|
|
360
|
-
f'[{
|
|
444
|
+
f'[{feature.id}] feature teardown', error
|
|
361
445
|
)
|
|
362
446
|
|
|
363
447
|
def on_feature_setup_session(
|
|
364
448
|
self,
|
|
365
|
-
environment: interface.Environment,
|
|
366
|
-
sandbox: interface.Sandbox,
|
|
367
449
|
feature: interface.Feature,
|
|
368
450
|
session_id: str | None,
|
|
369
451
|
duration: float,
|
|
@@ -373,13 +455,12 @@ class TestingEventHandler(
|
|
|
373
455
|
assert duration > 0
|
|
374
456
|
if self.log_session_setup:
|
|
375
457
|
self._add_message(
|
|
376
|
-
f'[{
|
|
458
|
+
f'[{feature.id}@{session_id or "<idle>"}] feature setup session',
|
|
459
|
+
error
|
|
377
460
|
)
|
|
378
461
|
|
|
379
462
|
def on_feature_teardown_session(
|
|
380
463
|
self,
|
|
381
|
-
environment: interface.Environment,
|
|
382
|
-
sandbox: interface.Sandbox,
|
|
383
464
|
feature: interface.Feature,
|
|
384
465
|
session_id: str,
|
|
385
466
|
duration: float,
|
|
@@ -389,77 +470,38 @@ class TestingEventHandler(
|
|
|
389
470
|
assert duration > 0
|
|
390
471
|
if self.log_session_setup:
|
|
391
472
|
self._add_message(
|
|
392
|
-
f'[{
|
|
473
|
+
f'[{feature.id}@{session_id}] feature teardown session', error
|
|
393
474
|
)
|
|
394
475
|
|
|
395
|
-
def
|
|
476
|
+
def on_feature_activity(
|
|
396
477
|
self,
|
|
397
|
-
|
|
398
|
-
sandbox: interface.Sandbox,
|
|
478
|
+
name: str,
|
|
399
479
|
feature: interface.Feature,
|
|
400
|
-
|
|
480
|
+
session_id: str | None,
|
|
401
481
|
duration: float,
|
|
402
482
|
error: BaseException | None,
|
|
483
|
+
*,
|
|
484
|
+
code: str | None = None,
|
|
403
485
|
**kwargs
|
|
404
486
|
) -> None:
|
|
405
|
-
"""Called when a sandbox
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
self._add_message(
|
|
409
|
-
f'[{sandbox.id}/{feature.name}] feature housekeeping {counter}', error
|
|
410
|
-
)
|
|
411
|
-
|
|
412
|
-
def on_session_start(
|
|
413
|
-
self,
|
|
414
|
-
environment: interface.Environment,
|
|
415
|
-
sandbox: interface.Sandbox,
|
|
416
|
-
session_id: str,
|
|
417
|
-
duration: float,
|
|
418
|
-
error: BaseException | None
|
|
419
|
-
) -> None:
|
|
420
|
-
"""Called when a sandbox session starts."""
|
|
421
|
-
assert duration > 0
|
|
422
|
-
self._add_message(
|
|
423
|
-
f'[{sandbox.id}] session {session_id!r} started', error
|
|
424
|
-
)
|
|
425
|
-
|
|
426
|
-
def on_session_end(
|
|
427
|
-
self,
|
|
428
|
-
environment: interface.Environment,
|
|
429
|
-
sandbox: interface.Sandbox,
|
|
430
|
-
session_id: str,
|
|
431
|
-
duration: float,
|
|
432
|
-
lifetime: float,
|
|
433
|
-
error: BaseException | None
|
|
434
|
-
) -> None:
|
|
435
|
-
"""Called when a sandbox session ends."""
|
|
436
|
-
assert duration > 0 and lifetime > 0
|
|
487
|
+
"""Called when a sandbox activity is performed."""
|
|
488
|
+
del kwargs
|
|
489
|
+
log_id = f'{feature.id}@{session_id or "<idle>"}'
|
|
437
490
|
self._add_message(
|
|
438
|
-
f'[{
|
|
491
|
+
f'[{log_id}] {name}: {code}', error
|
|
439
492
|
)
|
|
440
493
|
|
|
441
|
-
def
|
|
494
|
+
def on_feature_housekeep(
|
|
442
495
|
self,
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
sandbox: interface.Sandbox,
|
|
446
|
-
feature: interface.Feature | None,
|
|
447
|
-
session_id: str | None,
|
|
496
|
+
feature: interface.Feature,
|
|
497
|
+
counter: int,
|
|
448
498
|
duration: float,
|
|
449
499
|
error: BaseException | None,
|
|
450
|
-
*,
|
|
451
|
-
code: str | None = None,
|
|
452
500
|
**kwargs
|
|
453
501
|
) -> None:
|
|
454
|
-
"""Called when a sandbox
|
|
455
|
-
|
|
456
|
-
if
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
if feature is not None:
|
|
462
|
-
log_id = f'{log_id}/{feature.name}'
|
|
463
|
-
self._add_message(
|
|
464
|
-
f'[{log_id}] {name}: {code}', error
|
|
465
|
-
)
|
|
502
|
+
"""Called when a sandbox feature is housekeeping."""
|
|
503
|
+
assert duration > 0
|
|
504
|
+
if self.log_housekeep:
|
|
505
|
+
self._add_message(
|
|
506
|
+
f'[{feature.id}] feature housekeeping {counter}', error
|
|
507
|
+
)
|