langfun 0.1.2.dev202509240805__py3-none-any.whl → 0.1.2.dev202509250804__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.

@@ -0,0 +1,473 @@
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
+ """Test utils for base environment."""
15
+
16
+ import contextlib
17
+ import time
18
+ from typing import Iterator, Type
19
+
20
+ from langfun.env import base_environment
21
+ from langfun.env import base_feature
22
+ from langfun.env import base_sandbox
23
+ from langfun.env import interface
24
+ import pyglove as pg
25
+
26
+
27
+ class TestingEnvironment(base_environment.BaseEnvironment):
28
+ """Testing environment for unit tests."""
29
+
30
+ simulate_start_error: Type[BaseException] | None = None
31
+ simulate_shutdown_error: Type[BaseException] | None = None
32
+ simulate_ping_error: Type[BaseException] | None = None
33
+ keepalive_interval: float | None = 60.0
34
+ offline: bool = False
35
+
36
+ @property
37
+ def id(self) -> interface.EnvironmentId:
38
+ return interface.EnvironmentId('testing-env')
39
+
40
+ def wait_for_housekeeping(self):
41
+ housekeep_counter = self.housekeep_counter
42
+ while self.housekeep_counter == housekeep_counter:
43
+ time.sleep(0.1)
44
+
45
+ def _create_sandbox(
46
+ self,
47
+ sandbox_id: str,
48
+ reusable: bool,
49
+ proactive_session_setup: bool,
50
+ ) -> base_sandbox.BaseSandbox:
51
+ if self.offline:
52
+ raise interface.EnvironmentError(
53
+ 'Unknown environment error.',
54
+ environment=self,
55
+ )
56
+ return TestingSandbox(
57
+ environment=self,
58
+ id=interface.SandboxId(
59
+ environment_id=self.id,
60
+ sandbox_id=sandbox_id
61
+ ),
62
+ reusable=reusable,
63
+ proactive_session_setup=proactive_session_setup,
64
+ simulate_start_error=self.simulate_start_error,
65
+ simulate_shutdown_error=self.simulate_shutdown_error,
66
+ simulate_ping_error=self.simulate_ping_error,
67
+ keepalive_interval=self.keepalive_interval,
68
+ )
69
+
70
+
71
+ class TestingSandbox(base_sandbox.BaseSandbox):
72
+ """Testing sandbox for unit tests."""
73
+
74
+ simulate_start_error: Type[BaseException] | None = None
75
+ simulate_shutdown_error: Type[BaseException] | None = None
76
+ simulate_ping_error: Type[BaseException] | None = None
77
+
78
+ def _on_bound(self) -> None:
79
+ super()._on_bound()
80
+ self._shell_history = []
81
+ self._ping_history = []
82
+
83
+ def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
84
+ if (error_type is interface.SandboxStateError or
85
+ issubclass(error_type, interface.SandboxStateError)):
86
+ kwargs['sandbox'] = self
87
+ raise error_type(message, **kwargs)
88
+ else:
89
+ raise error_type(message)
90
+
91
+ def wait_until(
92
+ self,
93
+ status: interface.Sandbox.Status | tuple[interface.Sandbox.Status, ...]
94
+ ) -> None:
95
+ if not isinstance(status, tuple):
96
+ status = (status,)
97
+ while self.status not in status:
98
+ time.sleep(0.1)
99
+
100
+ def wait_until_not(
101
+ self,
102
+ status: interface.Sandbox.Status | tuple[interface.Sandbox.Status, ...]
103
+ ) -> None:
104
+ if not isinstance(status, tuple):
105
+ status = (status,)
106
+ while self.status in status:
107
+ time.sleep(0.1)
108
+
109
+ def wait_until_next_housekeep(self) -> None:
110
+ housekeep_counter = self.housekeep_counter
111
+ while self.housekeep_counter == housekeep_counter:
112
+ time.sleep(0.1)
113
+
114
+ def _start(self) -> None:
115
+ if self.simulate_start_error:
116
+ self._raise_error('Sandbox start error', self.simulate_start_error)
117
+ super()._start()
118
+
119
+ def _shutdown(self) -> None:
120
+ if self.simulate_shutdown_error:
121
+ self._raise_error('Sandbox shutdown error', self.simulate_shutdown_error)
122
+ super()._shutdown()
123
+
124
+ @base_sandbox.sandbox_service(critical_errors=(RuntimeError,))
125
+ def shell(
126
+ self,
127
+ code: str,
128
+ raise_error: Type[BaseException] | None = None,
129
+ ) -> str:
130
+ self._shell_history.append(code)
131
+ if raise_error is not None:
132
+ self._raise_error(f'shell "{code}" failed', raise_error)
133
+ return f'shell "{code}" succeeded'
134
+
135
+ def _ping(self) -> None:
136
+ self._ping_history.append(not self.simulate_ping_error)
137
+ if self.simulate_ping_error:
138
+ self._raise_error('Ping error', self.simulate_ping_error, code='ping')
139
+
140
+
141
+ class TestingFeature(base_feature.BaseFeature):
142
+ """Testing feature for unit tests."""
143
+
144
+ housekeep_interval = 0
145
+ setup_session_delay: float = 0.0
146
+ simulate_housekeep_error: Type[BaseException] | None = None
147
+ simulate_setup_error: Type[BaseException] | None = None
148
+ simulate_teardown_error: Type[BaseException] | None = None
149
+ simulate_setup_session_error: Type[BaseException] | None = None
150
+ simulate_teardown_session_error: Type[BaseException] | None = None
151
+ call_end_session_on_teardown_session: bool = False
152
+
153
+ class Service:
154
+ """Sandbox."""
155
+
156
+ def __init__(self, sandbox: interface.Sandbox):
157
+ self._sandbox = sandbox
158
+
159
+ def do(self, code: str, raise_error: Type[BaseException] | None = None):
160
+ self._sandbox.shell(code, raise_error=raise_error)
161
+
162
+ def _raise_error(self, message, error_type: Type[BaseException], **kwargs):
163
+ self._sandbox._raise_error(message, error_type, **kwargs) # pylint: disable=protected-access
164
+
165
+ def _setup(self) -> None:
166
+ if self.simulate_setup_error:
167
+ self._raise_error(f'{self.name} setup error', self.simulate_setup_error)
168
+ self.sandbox.shell(f'"{self.name}" setup')
169
+
170
+ def _teardown(self) -> None:
171
+ if self.simulate_teardown_error:
172
+ self._raise_error(
173
+ f'{self.name} teardown error', self.simulate_teardown_error
174
+ )
175
+ self.sandbox.shell(f'"{self.name}" teardown')
176
+
177
+ def _setup_session(self) -> None:
178
+ if self.setup_session_delay > 0:
179
+ time.sleep(self.setup_session_delay)
180
+
181
+ if self.simulate_setup_session_error:
182
+ self._raise_error(
183
+ 'Feature session setup error', self.simulate_setup_session_error
184
+ )
185
+ self.sandbox.shell(f'"{self.name}" setup session')
186
+
187
+ def _teardown_session(self) -> None:
188
+ if self.simulate_teardown_session_error:
189
+ self._raise_error(
190
+ 'Feature session teardown error', self.simulate_teardown_session_error
191
+ )
192
+ self.sandbox.shell(f'"{self.name}" teardown session')
193
+ if self.call_end_session_on_teardown_session:
194
+ self.sandbox.end_session()
195
+
196
+ @base_sandbox.sandbox_service()
197
+ def num_shell_calls(self) -> int:
198
+ return len(self.sandbox._shell_history) # pylint: disable=protected-access
199
+
200
+ @base_sandbox.sandbox_service()
201
+ def bad_shell_call(self) -> None:
202
+ self.sandbox.shell('bad command', raise_error=RuntimeError)
203
+
204
+ @base_sandbox.sandbox_service()
205
+ def show_session_id(self):
206
+ return self.session_id
207
+
208
+ @base_sandbox.sandbox_service()
209
+ def call_with_varargs(self, code: str, *args, **kwargs):
210
+ del code, args, kwargs
211
+ return 0
212
+
213
+ def _on_bound(self) -> None:
214
+ super()._on_bound()
215
+ self._service = None
216
+
217
+ @base_sandbox.sandbox_service()
218
+ @contextlib.contextmanager
219
+ def my_service(self) -> Iterator[Service]:
220
+ try:
221
+ self._service = TestingFeature.Service(sandbox=self.sandbox)
222
+ yield self._service
223
+ finally:
224
+ self._service = None
225
+
226
+ def _housekeep(self) -> None:
227
+ if self.simulate_housekeep_error:
228
+ raise interface.SandboxStateError(
229
+ 'House keeping error', sandbox=self.sandbox
230
+ )
231
+
232
+
233
+ class TestingEnvironmentEventHandler(
234
+ pg.Object, interface.EnvironmentEventHandler
235
+ ):
236
+ """Testing environment event handler for unit tests."""
237
+
238
+ log_sandbox_status: bool = False
239
+ log_feature_setup: bool = True
240
+ log_session_setup: bool = False
241
+ log_housekeep: bool = False
242
+
243
+ def _on_bound(self) -> None:
244
+ super()._on_bound()
245
+ self._logs = []
246
+
247
+ @property
248
+ def logs(self) -> list[str]:
249
+ return self._logs
250
+
251
+ def _add_message(self, message: str, error: BaseException | None) -> None:
252
+ """Adds a message to the history."""
253
+ if error is None:
254
+ self._logs.append(message)
255
+ else:
256
+ self._logs.append(f'{message} with {error.__class__.__name__}')
257
+
258
+ def on_environment_start(
259
+ self,
260
+ environment: interface.Environment,
261
+ duration: float,
262
+ error: BaseException | None
263
+ ) -> None:
264
+ """Called when the environment is started."""
265
+ assert duration > 0
266
+ self._add_message(f'[{environment.id}] environment started', error)
267
+
268
+ def on_environment_housekeep(
269
+ self,
270
+ environment: interface.Environment,
271
+ counter: int,
272
+ duration: float,
273
+ error: BaseException | None
274
+ ) -> None:
275
+ """Called when the environment finishes a round of housekeeping."""
276
+ assert duration > 0
277
+ if self.log_housekeep:
278
+ self._add_message(
279
+ f'[{environment.id}] environment housekeeping {counter}', error
280
+ )
281
+
282
+ def on_environment_shutdown(
283
+ self,
284
+ environment: interface.Environment,
285
+ lifetime: float,
286
+ error: BaseException | None
287
+ ) -> None:
288
+ """Called when the environment is shutdown."""
289
+ assert lifetime is not None
290
+ self._add_message(f'[{environment.id}] environment shutdown', error)
291
+
292
+ def on_sandbox_start(
293
+ self,
294
+ environment: interface.Environment,
295
+ sandbox: interface.Sandbox,
296
+ duration: float,
297
+ error: BaseException | None
298
+ ) -> None:
299
+ assert isinstance(environment, TestingEnvironment)
300
+ assert duration > 0
301
+ self._add_message(f'[{sandbox.id}] sandbox started', error)
302
+
303
+ def on_sandbox_status_change(
304
+ self,
305
+ environment: interface.Environment,
306
+ sandbox: interface.Sandbox,
307
+ old_status: interface.Sandbox.Status,
308
+ new_status: interface.Sandbox.Status,
309
+ span: float
310
+ ) -> None:
311
+ assert span > 0
312
+ if self.log_sandbox_status:
313
+ self._add_message(
314
+ f'[{sandbox.id}] {old_status.value} -> {new_status.value}',
315
+ None,
316
+ )
317
+
318
+ def on_sandbox_shutdown(
319
+ self,
320
+ environment: interface.Environment,
321
+ sandbox: interface.Sandbox,
322
+ lifetime: float,
323
+ error: BaseException | None
324
+ ) -> None:
325
+ assert lifetime is not None
326
+ self._add_message(f'[{sandbox.id}] sandbox shutdown', error)
327
+
328
+ def on_sandbox_housekeep(
329
+ self,
330
+ environment: interface.Environment,
331
+ sandbox: interface.Sandbox,
332
+ counter: int,
333
+ duration: float,
334
+ error: BaseException | None
335
+ ) -> None:
336
+ assert duration > 0
337
+ if self.log_housekeep:
338
+ self._add_message(
339
+ f'[{sandbox.id}] sandbox housekeeping {counter}', error
340
+ )
341
+
342
+ def on_feature_setup(
343
+ self,
344
+ environment: interface.Environment,
345
+ sandbox: interface.Sandbox,
346
+ feature: interface.Feature,
347
+ duration: float,
348
+ error: BaseException | None
349
+ ) -> None:
350
+ """Called when a sandbox feature is setup."""
351
+ assert duration > 0
352
+ if self.log_feature_setup:
353
+ self._add_message(
354
+ f'[{sandbox.id}/{feature.name}] feature setup', error
355
+ )
356
+
357
+ def on_feature_teardown(
358
+ self,
359
+ environment: interface.Environment,
360
+ sandbox: interface.Sandbox,
361
+ feature: interface.Feature,
362
+ duration: float,
363
+ error: BaseException | None
364
+ ) -> None:
365
+ """Called when a sandbox feature is teardown."""
366
+ assert duration > 0
367
+ if self.log_feature_setup:
368
+ self._add_message(
369
+ f'[{sandbox.id}/{feature.name}] feature teardown', error
370
+ )
371
+
372
+ def on_feature_setup_session(
373
+ self,
374
+ environment: interface.Environment,
375
+ sandbox: interface.Sandbox,
376
+ feature: interface.Feature,
377
+ session_id: str | None,
378
+ duration: float,
379
+ error: BaseException | None
380
+ ) -> None:
381
+ """Called when a sandbox feature is setup."""
382
+ assert duration > 0
383
+ if self.log_session_setup:
384
+ self._add_message(
385
+ f'[{sandbox.id}/{feature.name}] feature setup session', error
386
+ )
387
+
388
+ def on_feature_teardown_session(
389
+ self,
390
+ environment: interface.Environment,
391
+ sandbox: interface.Sandbox,
392
+ feature: interface.Feature,
393
+ session_id: str,
394
+ duration: float,
395
+ error: BaseException | None
396
+ ) -> None:
397
+ """Called when a sandbox feature is teardown."""
398
+ assert duration > 0
399
+ if self.log_session_setup:
400
+ self._add_message(
401
+ f'[{sandbox.id}/{feature.name}] feature teardown session', error
402
+ )
403
+
404
+ def on_feature_housekeep(
405
+ self,
406
+ environment: interface.Environment,
407
+ sandbox: interface.Sandbox,
408
+ feature: interface.Feature,
409
+ counter: int,
410
+ duration: float,
411
+ error: BaseException | None
412
+ ) -> None:
413
+ """Called when a sandbox feature is housekeeping."""
414
+ assert duration > 0
415
+ if self.log_housekeep:
416
+ self._add_message(
417
+ f'[{sandbox.id}/{feature.name}] feature housekeeping {counter}', error
418
+ )
419
+
420
+ def on_session_start(
421
+ self,
422
+ environment: interface.Environment,
423
+ sandbox: interface.Sandbox,
424
+ session_id: str,
425
+ duration: float,
426
+ error: BaseException | None
427
+ ) -> None:
428
+ """Called when a sandbox session starts."""
429
+ assert duration > 0
430
+ self._add_message(
431
+ f'[{sandbox.id}] session {session_id!r} started', error
432
+ )
433
+
434
+ def on_session_end(
435
+ self,
436
+ environment: interface.Environment,
437
+ sandbox: interface.Sandbox,
438
+ session_id: str,
439
+ lifetime: float,
440
+ error: BaseException | None
441
+ ) -> None:
442
+ """Called when a sandbox session ends."""
443
+ assert lifetime > 0
444
+ self._add_message(
445
+ f'[{sandbox.id}] session {session_id!r} ended', error
446
+ )
447
+
448
+ def on_sandbox_activity(
449
+ self,
450
+ name: str,
451
+ environment: interface.Environment,
452
+ sandbox: interface.Sandbox,
453
+ feature: interface.Feature | None,
454
+ session_id: str | None,
455
+ duration: float,
456
+ error: BaseException | None,
457
+ *,
458
+ code: str | None = None,
459
+ **kwargs
460
+ ) -> None:
461
+ """Called when a sandbox activity is performed."""
462
+ assert duration > 0
463
+ del environment, kwargs
464
+ if session_id is None:
465
+ log_id = sandbox.id
466
+ else:
467
+ log_id = f'{sandbox.id}/{session_id}'
468
+
469
+ if feature is not None:
470
+ log_id = f'{log_id}/{feature.name}'
471
+ self._add_message(
472
+ f'[{log_id}] {name}: {code}', error
473
+ )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202509240805
3
+ Version: 0.1.2.dev202509250804
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -165,17 +165,18 @@ langfun/core/templates/demonstration.py,sha256=vCrgYubdZM5Umqcgp8NUVGXgr4P_c-fik
165
165
  langfun/core/templates/demonstration_test.py,sha256=SafcDQ0WgI7pw05EmPI2S4v1t3ABKzup8jReCljHeK4,2162
166
166
  langfun/core/templates/selfplay.py,sha256=yhgrJbiYwq47TgzThmHrDQTF4nDrTI09CWGhuQPNv-s,2273
167
167
  langfun/core/templates/selfplay_test.py,sha256=Ot__1P1M8oJfoTp-M9-PQ6HUXqZKyMwvZ5f7yQ3yfyM,2326
168
- langfun/env/__init__.py,sha256=WOht-DaYtVmO8U-viQbWmCUCCPcJwBELBiQYUfGAG5w,1532
169
- langfun/env/base_environment.py,sha256=73d6hs2XhJd2IK--H7Pfs-1PG5TfJ89x_yk94doq0G0,16465
170
- langfun/env/base_feature.py,sha256=lphS2GmwCYR-ypPGIzn0KcLzxUcO2l9R2ZbJqcqOkOI,5794
171
- langfun/env/base_sandbox.py,sha256=c-HskeubKOH-P2ZrQG3HFKkhtX3ZADYj4QIMemQNGbo,32202
172
- langfun/env/base_test.py,sha256=CIzZjfSkZEDsHARF4aoOZfq5T8DN8i8AW-swFsFw0Gk,60328
173
- langfun/env/interface.py,sha256=wOu_yQO8uEt5lU88FeqqaInBLg7WgYYtVeGtPsanE90,29031
168
+ langfun/env/__init__.py,sha256=1u5hRfO2i-shsPaoOsbYrnvwldo3Mvkd3BfqhV4dOC0,1538
169
+ langfun/env/base_environment.py,sha256=FvAVfwfh3sT_XDrYH3adWo_ED6af-t6U3DuVamxv6YU,17636
170
+ langfun/env/base_feature.py,sha256=UArKlAZMYpftakLD25apLw84RqtJA_XcA_nkFt_iBRQ,6162
171
+ langfun/env/base_sandbox.py,sha256=Ena8GN_DaJj-4oMLH6kJQh1TMmBwo6_aU6IcdNWKNrw,36436
172
+ langfun/env/base_test.py,sha256=sGNTylTxQsX9gfc8IMt6_7L9VgLn7q5W2xuDHl8LFuQ,57170
173
+ langfun/env/interface.py,sha256=cuuBrFgCCEvKZx5EkKfIIKu1XfeijLr7sff9jyjrdg0,33359
174
174
  langfun/env/interface_test.py,sha256=nrqFLtpHd0vnOjoIX9cjV5WD1c-7J3WW29aogNu_94A,1395
175
175
  langfun/env/load_balancers.py,sha256=qRhCthqzjZIQBwta8qC1C0s0J-VQAVomJQqI7Nqv-r4,1948
176
176
  langfun/env/load_balancers_test.py,sha256=gJKJ3K5_WS7hsz9Jv3nPEjlb04Z6MmrZpfCu86siYKU,3967
177
- langfun-0.1.2.dev202509240805.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
178
- langfun-0.1.2.dev202509240805.dist-info/METADATA,sha256=r1KTHcsTEVX6kFjs88_yhZ3xY-k0urqHENZadNUnyi8,7380
179
- langfun-0.1.2.dev202509240805.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
180
- langfun-0.1.2.dev202509240805.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
181
- langfun-0.1.2.dev202509240805.dist-info/RECORD,,
177
+ langfun/env/test_utils.py,sha256=piNl09bQUW0GzEyQkyTCCJwzfxQ3z8s3M1lYcHCnmdo,14248
178
+ langfun-0.1.2.dev202509250804.dist-info/licenses/LICENSE,sha256=WNHhf_5RCaeuKWyq_K39vmp9F28LxKsB4SpomwSZ2L0,11357
179
+ langfun-0.1.2.dev202509250804.dist-info/METADATA,sha256=pAaeHsBcf9oirhLuF2RMulQm-r08T0lz48kO4xWv6Rw,7380
180
+ langfun-0.1.2.dev202509250804.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
181
+ langfun-0.1.2.dev202509250804.dist-info/top_level.txt,sha256=RhlEkHxs1qtzmmtWSwYoLVJAc1YrbPtxQ52uh8Z9VvY,8
182
+ langfun-0.1.2.dev202509250804.dist-info/RECORD,,