langfun 0.1.2.dev202509250804__py3-none-any.whl → 0.1.2.dev202509270803__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/env/base_test.py CHANGED
@@ -18,12 +18,13 @@ import unittest
18
18
  from langfun.env import base_sandbox
19
19
  from langfun.env import interface
20
20
  from langfun.env import test_utils
21
+ from langfun.env.event_handlers import base as event_handler_base
21
22
 
22
23
 
23
24
  TestingEnvironment = test_utils.TestingEnvironment
24
25
  TestingSandbox = test_utils.TestingSandbox
25
26
  TestingFeature = test_utils.TestingFeature
26
- TestingEnvironmentEventHandler = test_utils.TestingEnvironmentEventHandler
27
+ TestingEventHandler = test_utils.TestingEventHandler
27
28
 
28
29
 
29
30
  class EnvironmentTests(unittest.TestCase):
@@ -42,9 +43,8 @@ class EnvironmentTests(unittest.TestCase):
42
43
  self.assertEqual(env.min_pool_size, 0)
43
44
  self.assertEqual(env.max_pool_size, 0)
44
45
  self.assertEqual(env.sandbox_pool, [])
45
- self.assertEqual(env.id, interface.EnvironmentId('testing-env'))
46
+ self.assertEqual(env.id, interface.Environment.Id('testing-env'))
46
47
  self.assertEqual(env.outage_grace_period, 1)
47
- self.assertEqual(env.stats_report_interval, 60)
48
48
  self.assertEqual(env.features['test_feature'].name, 'test_feature')
49
49
 
50
50
  self.assertIsNone(env.start_time)
@@ -60,12 +60,16 @@ class EnvironmentTests(unittest.TestCase):
60
60
 
61
61
  with env.sandbox('session1') as sb:
62
62
  self.assertEqual(
63
- sb.id, interface.SandboxId(environment_id=env.id, sandbox_id='0')
63
+ sb.id, interface.Sandbox.Id(environment_id=env.id, sandbox_id='0')
64
64
  )
65
65
  self.assertEqual(sb.session_id, 'session1')
66
66
  self.assertEqual(sb.working_dir, '/tmp/testing-env/0')
67
67
  self.assertTrue(sb.is_online)
68
68
  self.assertIs(sb.test_feature, sb.features['test_feature'])
69
+ self.assertEqual(
70
+ sb.test_feature.working_dir,
71
+ '/tmp/testing-env/0/test_feature'
72
+ )
69
73
  with self.assertRaises(AttributeError):
70
74
  _ = sb.test_feature2
71
75
  self.assertFalse(sb.is_online)
@@ -74,14 +78,26 @@ class EnvironmentTests(unittest.TestCase):
74
78
  with self.assertRaises(AttributeError):
75
79
  _ = env.test_feature2
76
80
 
81
+ def test_del(self):
82
+ env = TestingEnvironment(
83
+ features={'test_feature': TestingFeature()},
84
+ pool_size=0,
85
+ outage_grace_period=1,
86
+ outage_retry_interval=0,
87
+ sandbox_keepalive_interval=0,
88
+ )
89
+ env.start()
90
+ sb = env.acquire()
91
+ del sb
92
+ del env
93
+
77
94
  def test_acquire_env_offline(self):
78
95
  env = TestingEnvironment(
79
96
  features={'test_feature': TestingFeature()},
80
97
  pool_size=0,
81
98
  outage_grace_period=1,
82
99
  outage_retry_interval=0,
83
- keepalive_interval=0,
84
- stats_report_interval=1,
100
+ sandbox_keepalive_interval=0,
85
101
  )
86
102
  with self.assertRaises(interface.EnvironmentOutageError):
87
103
  env.acquire()
@@ -92,12 +108,14 @@ class EnvironmentTests(unittest.TestCase):
92
108
  pool_size=0,
93
109
  outage_grace_period=1,
94
110
  outage_retry_interval=0,
95
- keepalive_interval=0,
96
- stats_report_interval=1,
111
+ sandbox_keepalive_interval=0,
97
112
  )
98
113
  with env:
99
114
  sb = env.acquire()
100
115
  self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
116
+ self.assertIsNone(env.working_dir)
117
+ self.assertIsNone(sb.working_dir)
118
+ self.assertIsNone(sb.test_feature.working_dir)
101
119
 
102
120
  def test_acquire_no_pooling_with_error(self):
103
121
  env = TestingEnvironment(
@@ -109,8 +127,7 @@ class EnvironmentTests(unittest.TestCase):
109
127
  pool_size=0,
110
128
  outage_grace_period=1,
111
129
  outage_retry_interval=0,
112
- keepalive_interval=0,
113
- stats_report_interval=1,
130
+ sandbox_keepalive_interval=0,
114
131
  )
115
132
  with env:
116
133
  with self.assertRaises(interface.EnvironmentOutageError):
@@ -122,8 +139,7 @@ class EnvironmentTests(unittest.TestCase):
122
139
  pool_size=1,
123
140
  outage_grace_period=1,
124
141
  outage_retry_interval=0,
125
- keepalive_interval=0,
126
- stats_report_interval=1,
142
+ sandbox_keepalive_interval=0,
127
143
  )
128
144
  with env:
129
145
  sb = env.acquire()
@@ -135,8 +151,7 @@ class EnvironmentTests(unittest.TestCase):
135
151
  pool_size=1,
136
152
  outage_grace_period=1,
137
153
  outage_retry_interval=0,
138
- keepalive_interval=0,
139
- stats_report_interval=1,
154
+ sandbox_keepalive_interval=0,
140
155
  )
141
156
  with env:
142
157
  sb = env.acquire()
@@ -150,17 +165,76 @@ class EnvironmentTests(unittest.TestCase):
150
165
  pool_size=(1, 3),
151
166
  outage_grace_period=1,
152
167
  outage_retry_interval=0,
153
- keepalive_interval=0,
154
- stats_report_interval=1,
168
+ sandbox_keepalive_interval=0,
155
169
  )
156
170
  with env:
157
171
  self.assertEqual(len(env.sandbox_pool), 1)
172
+ self.assertEqual(
173
+ env.stats(),
174
+ {
175
+ 'sandbox': {
176
+ 'created': 0,
177
+ 'setting_up': 0,
178
+ 'ready': 1,
179
+ 'acquired': 0,
180
+ 'in_session': 0,
181
+ 'exiting_session': 0,
182
+ 'shutting_down': 0,
183
+ 'offline': 0,
184
+ }
185
+ }
186
+ )
158
187
  sb = env.acquire()
159
188
  self.assertEqual(sb.status, interface.Sandbox.Status.ACQUIRED)
189
+ self.assertEqual(
190
+ env.stats(),
191
+ {
192
+ 'sandbox': {
193
+ 'created': 0,
194
+ 'setting_up': 0,
195
+ 'ready': 0,
196
+ 'acquired': 1,
197
+ 'in_session': 0,
198
+ 'exiting_session': 0,
199
+ 'shutting_down': 0,
200
+ 'offline': 0,
201
+ }
202
+ }
203
+ )
160
204
  self.assertEqual(len(env.sandbox_pool), 1)
161
205
  sb2 = env.acquire()
162
206
  self.assertEqual(sb2.status, interface.Sandbox.Status.ACQUIRED)
163
207
  self.assertEqual(len(env.sandbox_pool), 2)
208
+ self.assertEqual(
209
+ env.stats(),
210
+ {
211
+ 'sandbox': {
212
+ 'created': 0,
213
+ 'setting_up': 0,
214
+ 'ready': 0,
215
+ 'acquired': 2,
216
+ 'in_session': 0,
217
+ 'exiting_session': 0,
218
+ 'shutting_down': 0,
219
+ 'offline': 0,
220
+ }
221
+ }
222
+ )
223
+ self.assertEqual(
224
+ env.stats(),
225
+ {
226
+ 'sandbox': {
227
+ 'created': 0,
228
+ 'setting_up': 0,
229
+ 'ready': 0,
230
+ 'acquired': 0,
231
+ 'in_session': 0,
232
+ 'exiting_session': 0,
233
+ 'shutting_down': 0,
234
+ 'offline': 0,
235
+ }
236
+ }
237
+ )
164
238
 
165
239
  def test_acquire_with_growing_pool_failure(self):
166
240
  env = TestingEnvironment(
@@ -168,8 +242,7 @@ class EnvironmentTests(unittest.TestCase):
168
242
  pool_size=(1, 3),
169
243
  outage_grace_period=1,
170
244
  outage_retry_interval=0,
171
- keepalive_interval=0,
172
- stats_report_interval=1,
245
+ sandbox_keepalive_interval=0,
173
246
  )
174
247
  with env:
175
248
  self.assertEqual(len(env.sandbox_pool), 1)
@@ -184,15 +257,14 @@ class EnvironmentTests(unittest.TestCase):
184
257
  with self.assertRaises(interface.EnvironmentOutageError):
185
258
  env.acquire()
186
259
 
187
- def test_maintenance_error(self):
260
+ def test_housekeep_error(self):
188
261
  env = TestingEnvironment(
189
262
  features={'test_feature': TestingFeature()},
190
263
  pool_size=1,
191
264
  proactive_session_setup=True,
192
265
  outage_grace_period=1,
193
266
  outage_retry_interval=0,
194
- keepalive_interval=0,
195
- stats_report_interval=1,
267
+ sandbox_keepalive_interval=0,
196
268
  )
197
269
  with env:
198
270
  self.assertEqual(len(env.sandbox_pool), 1)
@@ -216,7 +288,7 @@ class SandboxStatusTests(unittest.TestCase):
216
288
 
217
289
  def setUp(self):
218
290
  super().setUp()
219
- self.event_handler = TestingEnvironmentEventHandler(
291
+ self.event_handler = TestingEventHandler(
220
292
  log_sandbox_status=True,
221
293
  log_feature_setup=True,
222
294
  log_session_setup=True,
@@ -246,6 +318,7 @@ class SandboxStatusTests(unittest.TestCase):
246
318
  'feature2': TestingFeature(),
247
319
  },
248
320
  )
321
+ self.assertFalse(env.enable_pooling)
249
322
  with env:
250
323
  with env.sandbox('session1') as sb:
251
324
  sb.shell('echo "hello"')
@@ -294,6 +367,7 @@ class SandboxStatusTests(unittest.TestCase):
294
367
  pool_size=1,
295
368
  proactive_session_setup=True,
296
369
  )
370
+ self.assertTrue(env.enable_pooling)
297
371
  with env:
298
372
  with env.sandbox('session1') as sb:
299
373
  sb.shell('echo "hello"')
@@ -343,7 +417,7 @@ class SandboxStatusTests(unittest.TestCase):
343
417
  features={'test_feature': TestingFeature(setup_session_delay=0.5)},
344
418
  pool_size=1,
345
419
  )
346
- event_handler = TestingEnvironmentEventHandler(
420
+ event_handler = TestingEventHandler(
347
421
  log_sandbox_status=True,
348
422
  log_feature_setup=True,
349
423
  log_session_setup=True,
@@ -919,6 +993,7 @@ class SandboxStatusTests(unittest.TestCase):
919
993
  self.assertEqual(len(sb.state_errors), 0)
920
994
  sb.shell('echo bar')
921
995
  self.assertEqual(sb.status, interface.Sandbox.Status.IN_SESSION)
996
+ sb.wait_until_not(interface.Sandbox.Status.SETTING_UP)
922
997
  self.assertEqual(sb.status, interface.Sandbox.Status.READY)
923
998
  self.assertEqual(
924
999
  self.event_handler.logs,
@@ -1020,7 +1095,7 @@ class SandboxActivityTests(unittest.TestCase):
1020
1095
  env = TestingEnvironment(
1021
1096
  features={'test_feature': TestingFeature(housekeep_interval=0)},
1022
1097
  pool_size=1,
1023
- keepalive_interval=0,
1098
+ sandbox_keepalive_interval=0,
1024
1099
  )
1025
1100
  with env:
1026
1101
  with env.sandbox('session1') as sb:
@@ -1037,7 +1112,7 @@ class SandboxActivityTests(unittest.TestCase):
1037
1112
  pool_size=1,
1038
1113
  outage_grace_period=0,
1039
1114
  outage_retry_interval=0,
1040
- keepalive_interval=0,
1115
+ sandbox_keepalive_interval=0,
1041
1116
  )
1042
1117
  with env:
1043
1118
  with env.sandbox('session1') as sb:
@@ -1052,7 +1127,7 @@ class SandboxActivityTests(unittest.TestCase):
1052
1127
  while sb.housekeep_counter == housekeep_count or (
1053
1128
  sb.status == interface.Sandbox.Status.IN_SESSION
1054
1129
  ):
1055
- time.sleep(0.1)
1130
+ time.sleep(0.01)
1056
1131
  self.assertEqual(sb.status, interface.Sandbox.Status.OFFLINE)
1057
1132
 
1058
1133
  def test_remove_event_handler(self):
@@ -1061,10 +1136,9 @@ class SandboxActivityTests(unittest.TestCase):
1061
1136
  pool_size=1,
1062
1137
  outage_grace_period=0,
1063
1138
  outage_retry_interval=0,
1064
- keepalive_interval=0,
1065
- stats_report_interval=1,
1139
+ sandbox_keepalive_interval=0,
1066
1140
  )
1067
- event_handler = TestingEnvironmentEventHandler()
1141
+ event_handler = TestingEventHandler()
1068
1142
  with env:
1069
1143
  with env.sandbox('session1') as sb:
1070
1144
  sb.add_event_handler(event_handler)
@@ -1082,21 +1156,20 @@ class SandboxServiceTests(unittest.TestCase):
1082
1156
  def setUp(self):
1083
1157
  super().setUp()
1084
1158
  self.maxDiff = None
1085
- self.event_handler = TestingEnvironmentEventHandler()
1159
+ self.event_handler = TestingEventHandler()
1086
1160
  self.env = TestingEnvironment(
1087
1161
  features={'test_feature': TestingFeature()},
1088
1162
  pool_size=0,
1089
1163
  outage_grace_period=0,
1090
1164
  outage_retry_interval=0,
1091
- keepalive_interval=0,
1165
+ sandbox_keepalive_interval=0,
1092
1166
  event_handlers=[self.event_handler],
1093
- stats_report_interval=1,
1094
1167
  random_seed=1,
1095
1168
  )
1096
1169
 
1097
1170
  def test_service_call_activity_log(self):
1098
1171
 
1099
- class CustomEventHandler(interface.EnvironmentEventHandler):
1172
+ class CustomEventHandler(event_handler_base.EventHandler):
1100
1173
 
1101
1174
  def __init__(self):
1102
1175
  self.calls = []
@@ -0,0 +1,12 @@
1
+ """Environment event handlers."""
2
+
3
+ # pylint: disable=g-importing-member
4
+ # pylint: disable=g-bad-import-order
5
+
6
+ from langfun.env.event_handlers.base import EventHandler
7
+
8
+ from langfun.env.event_handlers.event_logger import EventLogger
9
+ from langfun.env.event_handlers.event_logger import ConsoleEventLogger
10
+
11
+ # pylint: enable=g-importing-member
12
+ # pylint: enable=g-bad-import-order
@@ -0,0 +1,271 @@
1
+
2
+ # Copyright 2025 The Langfun Authors
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ """Base classes for Langfun environment event handlers."""
16
+
17
+ from langfun.env import interface
18
+
19
+ Environment = interface.Environment
20
+ Sandbox = interface.Sandbox
21
+ Feature = interface.Feature
22
+
23
+
24
+ class _SessionEventHandler:
25
+ """Base class for session event handlers."""
26
+
27
+ def on_session_start(
28
+ self,
29
+ environment: Environment,
30
+ sandbox: Sandbox,
31
+ session_id: str,
32
+ duration: float,
33
+ error: BaseException | None
34
+ ) -> None:
35
+ """Called when a sandbox session starts.
36
+
37
+ Args:
38
+ environment: The environment.
39
+ sandbox: The sandbox.
40
+ session_id: The session ID.
41
+ duration: The time spent on starting the session.
42
+ error: The error that caused the session to start. If None, the session
43
+ started normally.
44
+ """
45
+
46
+ def on_session_end(
47
+ self,
48
+ environment: Environment,
49
+ sandbox: Sandbox,
50
+ session_id: str,
51
+ lifetime: float,
52
+ error: BaseException | None
53
+ ) -> None:
54
+ """Called when a sandbox session ends.
55
+
56
+ Args:
57
+ environment: The environment.
58
+ sandbox: The sandbox.
59
+ session_id: The session ID.
60
+ lifetime: The session lifetime in seconds.
61
+ error: The error that caused the session to end. If None, the session
62
+ ended normally.
63
+ """
64
+
65
+
66
+ class _FeatureEventHandler:
67
+ """Base class for feature event handlers."""
68
+
69
+ def on_feature_setup(
70
+ self,
71
+ environment: Environment,
72
+ sandbox: Sandbox,
73
+ feature: Feature,
74
+ duration: float,
75
+ error: BaseException | None
76
+ ) -> None:
77
+ """Called when a sandbox feature is setup."""
78
+
79
+ def on_feature_teardown(
80
+ self,
81
+ environment: Environment,
82
+ sandbox: Sandbox,
83
+ feature: Feature,
84
+ duration: float,
85
+ error: BaseException | None
86
+ ) -> None:
87
+ """Called when a sandbox feature is teardown."""
88
+
89
+ def on_feature_teardown_session(
90
+ self,
91
+ environment: Environment,
92
+ sandbox: Sandbox,
93
+ feature: Feature,
94
+ session_id: str,
95
+ duration: float,
96
+ error: BaseException | None
97
+ ) -> None:
98
+ """Called when a feature is teardown with a session."""
99
+
100
+ def on_feature_setup_session(
101
+ self,
102
+ environment: Environment,
103
+ sandbox: Sandbox,
104
+ feature: Feature,
105
+ session_id: str | None,
106
+ duration: float,
107
+ error: BaseException | None
108
+ ) -> None:
109
+ """Called when a feature is setup with a session."""
110
+
111
+ def on_feature_housekeep(
112
+ self,
113
+ environment: Environment,
114
+ sandbox: Sandbox,
115
+ feature: Feature,
116
+ counter: int,
117
+ duration: float,
118
+ error: BaseException | None
119
+ ) -> None:
120
+ """Called when a sandbox feature is housekeeping."""
121
+
122
+
123
+ class _SandboxEventHandler(_FeatureEventHandler, _SessionEventHandler):
124
+ """Base class for sandbox event handlers."""
125
+
126
+ def on_sandbox_start(
127
+ self,
128
+ environment: Environment,
129
+ sandbox: Sandbox,
130
+ duration: float,
131
+ error: BaseException | None
132
+ ) -> None:
133
+ """Called when a sandbox is started.
134
+
135
+ Args:
136
+ environment: The environment.
137
+ sandbox: The sandbox.
138
+ duration: The time spent on starting the sandbox.
139
+ error: The error that caused the sandbox to start. If None, the sandbox
140
+ started normally.
141
+ """
142
+
143
+ def on_sandbox_status_change(
144
+ self,
145
+ environment: Environment,
146
+ sandbox: Sandbox,
147
+ old_status: 'Sandbox.Status',
148
+ new_status: 'Sandbox.Status',
149
+ span: float,
150
+ ) -> None:
151
+ """Called when a sandbox status changes.
152
+
153
+ Args:
154
+ environment: The environment.
155
+ sandbox: The sandbox.
156
+ old_status: The old sandbox status.
157
+ new_status: The new sandbox status.
158
+ span: Time spent on the old status in seconds.
159
+ """
160
+
161
+ def on_sandbox_shutdown(
162
+ self,
163
+ environment: Environment,
164
+ sandbox: Sandbox,
165
+ lifetime: float,
166
+ error: BaseException | None
167
+ ) -> None:
168
+ """Called when a sandbox is shutdown.
169
+
170
+ Args:
171
+ environment: The environment.
172
+ sandbox: The sandbox.
173
+ lifetime: The sandbox lifetime in seconds.
174
+ error: The error that caused the sandbox to shutdown. If None, the
175
+ sandbox shutdown normally.
176
+ """
177
+
178
+ def on_sandbox_activity(
179
+ self,
180
+ name: str,
181
+ environment: Environment,
182
+ sandbox: Sandbox,
183
+ feature: Feature | None,
184
+ session_id: str | None,
185
+ duration: float,
186
+ error: BaseException | None,
187
+ **kwargs
188
+ ) -> None:
189
+ """Called when a sandbox activity is performed.
190
+
191
+ Args:
192
+ name: The name of the sandbox activity.
193
+ environment: The environment.
194
+ sandbox: The sandbox.
195
+ feature: The feature that is associated with the sandbox activity.
196
+ session_id: The session ID.
197
+ duration: The sandbox activity duration in seconds.
198
+ error: The error that caused the sandbox activity to perform. If None,
199
+ the sandbox activity performed normally.
200
+ **kwargs: The keyword arguments of the sandbox activity.
201
+ """
202
+
203
+ def on_sandbox_housekeep(
204
+ self,
205
+ environment: Environment,
206
+ sandbox: Sandbox,
207
+ counter: int,
208
+ duration: float,
209
+ error: BaseException | None
210
+ ) -> None:
211
+ """Called when a sandbox finishes a round of housekeeping.
212
+
213
+ Args:
214
+ environment: The environment.
215
+ sandbox: The sandbox.
216
+ counter: Zero-based counter of the housekeeping round.
217
+ duration: The sandbox housekeeping duration in seconds.
218
+ error: The error that caused the sandbox to housekeeping. If None, the
219
+ sandbox housekeeping normally.
220
+ """
221
+
222
+
223
+ class EventHandler(_SandboxEventHandler):
224
+ """Base class for event handlers of an environment."""
225
+
226
+ def on_environment_start(
227
+ self,
228
+ environment: Environment,
229
+ duration: float,
230
+ error: BaseException | None
231
+ ) -> None:
232
+ """Called when the environment is started.
233
+
234
+ Args:
235
+ environment: The environment.
236
+ duration: The environment start duration in seconds.
237
+ error: The error that failed the environment start. If None, the
238
+ environment started normally.
239
+ """
240
+
241
+ def on_environment_housekeep(
242
+ self,
243
+ environment: Environment,
244
+ counter: int,
245
+ duration: float,
246
+ error: BaseException | None
247
+ ) -> None:
248
+ """Called when the environment finishes a round of housekeeping.
249
+
250
+ Args:
251
+ environment: The environment.
252
+ counter: Zero-based counter of the housekeeping round.
253
+ duration: The environment start duration in seconds.
254
+ error: The error that failed the housekeeping. If None, the
255
+ housekeeping succeeded.
256
+ """
257
+
258
+ def on_environment_shutdown(
259
+ self,
260
+ environment: Environment,
261
+ lifetime: float,
262
+ error: BaseException | None
263
+ ) -> None:
264
+ """Called when the environment is shutdown.
265
+
266
+ Args:
267
+ environment: The environment.
268
+ lifetime: The environment lifetime in seconds.
269
+ error: The error that caused the environment to shutdown. If None, the
270
+ environment shutdown normally.
271
+ """