langfun 0.1.2.dev202509240805__py3-none-any.whl → 0.1.2.dev202509260805__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/interface.py CHANGED
@@ -22,60 +22,6 @@ from typing import Annotated, Any, ContextManager, ClassVar, Iterator, Optional
22
22
 
23
23
  import pyglove as pg
24
24
 
25
- #
26
- # Environemnt identifiers.
27
- #
28
-
29
-
30
- @dataclasses.dataclass(frozen=True)
31
- class EnvironmentId:
32
- """Identifier for an environment."""
33
- environment_id: str
34
-
35
- def __str__(self) -> str:
36
- return self.environment_id
37
-
38
- def working_dir(self, root_dir: str | None) -> str | None:
39
- """Returns the download directory for the service."""
40
- if root_dir is None:
41
- return None
42
- return os.path.join(root_dir, _make_path_compatible(self.environment_id))
43
-
44
- # Enable automatic conversion from str to EnvironmentId.
45
- pg.typing.register_converter(str, EnvironmentId, EnvironmentId)
46
-
47
-
48
- @dataclasses.dataclass(frozen=True)
49
- class SandboxId:
50
- """Identifier for a sandbox."""
51
- environment_id: EnvironmentId
52
- sandbox_id: str
53
-
54
- def __str__(self) -> str:
55
- return f'{self.environment_id}/{self.sandbox_id}'
56
-
57
- def working_dir(self, root_dir: str | None) -> str | None:
58
- """Returns the download directory for the sandbox."""
59
- if root_dir is None:
60
- return None
61
- return os.path.join(
62
- self.environment_id.working_dir(root_dir),
63
- _make_path_compatible(self.sandbox_id)
64
- )
65
-
66
-
67
- def _make_path_compatible(id_str: str) -> str:
68
- """Makes a path compatible with CNS."""
69
- return id_str.translate(
70
- str.maketrans({
71
- '@': '_',
72
- ':': '_',
73
- '#': '_',
74
- ' ': '',
75
- })
76
- )
77
-
78
-
79
25
  #
80
26
  # Environment errors.
81
27
  #
@@ -216,144 +162,6 @@ class SessionTeardownError(SandboxError):
216
162
  not isinstance(e, SandboxStateError) for e in self.errors.values()
217
163
  )
218
164
 
219
-
220
- #
221
- # Event handler.
222
- #
223
-
224
-
225
- class SessionEventHandler:
226
- """Base class for session event handlers."""
227
-
228
- def on_session_start(
229
- self,
230
- environment: 'Environment',
231
- sandbox: 'Sandbox',
232
- session_id: str,
233
- error: BaseException | None
234
- ) -> None:
235
- """Called when a sandbox session starts."""
236
-
237
- def on_session_activity(
238
- self,
239
- session_id: str,
240
- name: str,
241
- environment: 'Environment',
242
- sandbox: 'Sandbox',
243
- feature: Optional['Feature'],
244
- error: BaseException | None,
245
- **kwargs
246
- ) -> None:
247
- """Called when a sandbox activity is performed."""
248
-
249
- def on_session_end(
250
- self,
251
- environment: 'Environment',
252
- sandbox: 'Sandbox',
253
- session_id: str,
254
- error: BaseException | None
255
- ) -> None:
256
- """Called when a sandbox session ends."""
257
-
258
-
259
- class FeatureEventHandler:
260
- """Base class for feature event handlers."""
261
-
262
- def on_feature_setup(
263
- self,
264
- environment: 'Environment',
265
- sandbox: 'Sandbox',
266
- feature: 'Feature',
267
- error: BaseException | None
268
- ) -> None:
269
- """Called when a sandbox feature is setup."""
270
-
271
- def on_feature_teardown(
272
- self,
273
- environment: 'Environment',
274
- sandbox: 'Sandbox',
275
- feature: 'Feature',
276
- error: BaseException | None
277
- ) -> None:
278
- """Called when a sandbox feature is teardown."""
279
-
280
- def on_feature_teardown_session(
281
- self,
282
- environment: 'Environment',
283
- sandbox: 'Sandbox',
284
- feature: 'Feature',
285
- session_id: str,
286
- error: BaseException | None
287
- ) -> None:
288
- """Called when a feature is teardown with a session."""
289
-
290
- def on_feature_setup_session(
291
- self,
292
- environment: 'Environment',
293
- sandbox: 'Sandbox',
294
- feature: 'Feature',
295
- session_id: str | None,
296
- error: BaseException | None
297
- ) -> None:
298
- """Called when a feature is setup with a session."""
299
-
300
- def on_feature_housekeep(
301
- self,
302
- environment: 'Environment',
303
- sandbox: 'Sandbox',
304
- feature: 'Feature',
305
- error: BaseException | None
306
- ) -> None:
307
- """Called when a sandbox feature is housekeeping."""
308
-
309
-
310
- class SandboxEventHandler(FeatureEventHandler, SessionEventHandler):
311
- """Base class for sandbox event handlers."""
312
-
313
- def on_sandbox_start(
314
- self,
315
- environment: 'Environment',
316
- sandbox: 'Sandbox',
317
- error: BaseException | None
318
- ) -> None:
319
- """Called when a sandbox is started."""
320
-
321
- def on_sandbox_status_change(
322
- self,
323
- environment: 'Environment',
324
- sandbox: 'Sandbox',
325
- old_status: 'Sandbox.Status',
326
- new_status: 'Sandbox.Status',
327
- ) -> None:
328
- """Called when a sandbox status changes."""
329
-
330
- def on_sandbox_shutdown(
331
- self,
332
- environment: 'Environment',
333
- sandbox: 'Sandbox',
334
- error: BaseException | None
335
- ) -> None:
336
- """Called when a sandbox is shutdown."""
337
-
338
-
339
- class EnvironmentEventHandler(SandboxEventHandler):
340
- """Base class for environment event handlers."""
341
-
342
- def on_environment_start(
343
- self,
344
- environment: 'Environment',
345
- error: BaseException | None
346
- ) -> None:
347
- """Called when the environment is started."""
348
-
349
- def on_environment_shutdown(
350
- self,
351
- environment: 'Environment',
352
- error: BaseException | None
353
- ) -> None:
354
- """Called when the environment is shutdown."""
355
-
356
-
357
165
  #
358
166
  # Interface for sandbox-based environment.
359
167
  #
@@ -362,6 +170,20 @@ class EnvironmentEventHandler(SandboxEventHandler):
362
170
  class Environment(pg.Object):
363
171
  """Base class for an environment."""
364
172
 
173
+ @dataclasses.dataclass(frozen=True)
174
+ class Id:
175
+ """Identifier for an environment."""
176
+ environment_id: str
177
+
178
+ def __str__(self) -> str:
179
+ return self.environment_id
180
+
181
+ def working_dir(self, root_dir: str | None) -> str | None:
182
+ """Returns the download directory for the service."""
183
+ if root_dir is None:
184
+ return None
185
+ return os.path.join(root_dir, _make_path_compatible(self.environment_id))
186
+
365
187
  class Status(enum.Enum):
366
188
  """Environment state.
367
189
 
@@ -384,13 +206,6 @@ class Environment(pg.Object):
384
206
  'Features to be exposed by the environment.'
385
207
  ] = {}
386
208
 
387
- event_handlers: Annotated[
388
- list[EnvironmentEventHandler],
389
- (
390
- 'User handler for the environment events.'
391
- )
392
- ] = []
393
-
394
209
  _ENV_STACK: Annotated[
395
210
  ClassVar[list['Environment']],
396
211
  'Recording the environments stacked through context managers.'
@@ -402,7 +217,7 @@ class Environment(pg.Object):
402
217
 
403
218
  @property
404
219
  @abc.abstractmethod
405
- def id(self) -> EnvironmentId:
220
+ def id(self) -> Id:
406
221
  """Returns the identifier for the environment."""
407
222
 
408
223
  @property
@@ -466,6 +281,10 @@ class Environment(pg.Object):
466
281
  Environment._ENV_STACK.pop()
467
282
  self.shutdown()
468
283
 
284
+ def __del__(self):
285
+ """Deletes the environment."""
286
+ self.shutdown()
287
+
469
288
  @classmethod
470
289
  def current(cls) -> Optional['Environment']:
471
290
  """Returns the current environment."""
@@ -506,49 +325,78 @@ class Environment(pg.Object):
506
325
  raise AttributeError(name)
507
326
 
508
327
 
328
+ # Enable automatic conversion from str to Environment.Id.
329
+ pg.typing.register_converter(str, Environment.Id, Environment.Id)
330
+
331
+
509
332
  class Sandbox(pg.Object):
510
333
  """Interface for sandboxes."""
511
334
 
335
+ @dataclasses.dataclass(frozen=True, slots=True)
336
+ class Id:
337
+ """Identifier for a sandbox."""
338
+ environment_id: Environment.Id
339
+ sandbox_id: str
340
+
341
+ def __str__(self) -> str:
342
+ return f'{self.environment_id}/{self.sandbox_id}'
343
+
344
+ def working_dir(self, root_dir: str | None) -> str | None:
345
+ """Returns the download directory for the sandbox."""
346
+ if root_dir is None:
347
+ return None
348
+ return os.path.join(
349
+ self.environment_id.working_dir(root_dir),
350
+ _make_path_compatible(self.sandbox_id)
351
+ )
352
+
512
353
  class Status(enum.Enum):
513
- """Sandbox state.
354
+ r"""Sandbox state.
514
355
 
515
356
  State transitions:
516
357
 
517
- +---------------+ +---------------+
518
- | <OFFLINE> | <------ | SHUTTING_DOWN |
519
- +---------------+ +---------------+
520
- ^ ^
521
- | |
522
- (shutdown)| +------------------------+
523
- | |
524
- +-----------+ (call start) +------------+ |
525
- | <CREATED> | -------------> | SETTING_UP | <----------------+ |
526
- +-----------+ +------------+ | |
527
- | | |
528
- | (start succeeded) | |
529
- | OR (_setup_session) | |
530
- v | |
531
- +---------+ | |
532
- | READY | | |
533
- +---------+ | |
534
- | | |
535
- | (set_acquired) | |
536
- v | |
537
- +----------+ | |
538
- | ACQUIRED | | |
539
- +----------+ | |
540
- | | |
541
- | (call start_session) | |
542
- +------------+ | |
543
- | SETTING_UP | --(failed) -----------+
544
- +------------+ | |
545
- | | |
546
- v (start_session succeeded)| |
547
- +--------------+ | |
548
- | IN_SESSION |--(end_session)--+ |
549
- +--------------+ |
550
- | |
551
- +------(shutdown)---------------+
358
+ (sandbox / feature
359
+ +------------+ teardown) +---------------+
360
+ | <OFFLINE> | <--------------------- | SHUTTING_DOWN |
361
+ +------------+ +---------------+
362
+ ^ ^
363
+ / \
364
+ (setup failed) / \
365
+ / \
366
+ +-----------+ (start) +------------+ \
367
+ | <CREATED> | --------> | SETTING_UP | \
368
+ +-----------+ ^ +------------+ \
369
+ / | \
370
+ / | (sandbox / \
371
+ / | feature /session \
372
+ / v setup succeeded) \
373
+ / +---------+ \
374
+ / | READY | \
375
+ / +---------+ \
376
+ / | \
377
+ / | (acquire) \
378
+ / v \
379
+ / +----------+ \
380
+ | | ACQUIRED | \
381
+ | +----------+ |
382
+ | | |
383
+ | | (start_session) |
384
+ | +------------+ |
385
+ | | SETTING_UP |-- (setup failed) ------>+
386
+ | +------------+ |
387
+ | | |
388
+ | v (succeeded) |
389
+ | +--------------+ |
390
+ | | IN_SESSION |- (op failed) -------->+
391
+ | +--------------+ |
392
+ | | |
393
+ | | (end_session) |
394
+ | | |
395
+ | v (session teardown |
396
+ (setup next +-----------------+ failed OR |
397
+ session for <---------| EXITING_SESSION |- non-reusable -----+
398
+ reusable sandbox) +-----------------+ sandbox)
399
+
552
400
  """
553
401
 
554
402
  # The sandbox is created, but not yet started.
@@ -566,6 +414,9 @@ class Sandbox(pg.Object):
566
414
  # The sandbox is in a user session.
567
415
  IN_SESSION = 'in_session'
568
416
 
417
+ # The sandbox is exiting a user session.
418
+ EXITING_SESSION = 'exiting_session'
419
+
569
420
  # The sandbox is being shut down.
570
421
  SHUTTING_DOWN = 'shutting_down'
571
422
 
@@ -584,7 +435,7 @@ class Sandbox(pg.Object):
584
435
 
585
436
  @property
586
437
  @abc.abstractmethod
587
- def id(self) -> SandboxId:
438
+ def id(self) -> Id:
588
439
  """Returns the identifier for the sandbox."""
589
440
 
590
441
  @property
@@ -607,24 +458,6 @@ class Sandbox(pg.Object):
607
458
  """Returns True if the sandbox is online."""
608
459
  return self.status.is_online
609
460
 
610
- @abc.abstractmethod
611
- def set_acquired(self) -> None:
612
- """Marks the sandbox as acquired."""
613
-
614
- @abc.abstractmethod
615
- def add_event_handler(
616
- self,
617
- event_handler: EnvironmentEventHandler
618
- ) -> None:
619
- """Sets the status of the sandbox."""
620
-
621
- @abc.abstractmethod
622
- def remove_event_handler(
623
- self,
624
- event_handler: EnvironmentEventHandler
625
- ) -> None:
626
- """Removes the status of the sandbox."""
627
-
628
461
  @property
629
462
  @abc.abstractmethod
630
463
  def state_errors(self) -> list[SandboxStateError]:
@@ -742,14 +575,18 @@ class Sandbox(pg.Object):
742
575
  """Ends the user session with the sandbox.
743
576
 
744
577
  State transitions:
745
- IN_SESSION -> READY: When user session exits normally, and sandbox is set
746
- to reuse.
747
- IN_SESSION -> SHUTTING_DOWN -> OFFLINE: When user session exits while
578
+ IN_SESSION -> EXITING_SESSION -> READY: When user session exits normally,
579
+ and sandbox is set to reuse.
580
+ IN_SESSION -> EXITING_SESSION -> SHUTTING_DOWN -> OFFLINE: When user
581
+ session exits while
748
582
  sandbox is set not to reuse, or session teardown fails.
749
- IN_SESSION -> SETTING_UP -> READY: When user session exits normally, and
750
- sandbox is set to reuse, and proactive session setup is enabled.
751
- IN_SESSION -> SETTING_UP -> SHUTTING_DOWN -> OFFLINE: When user session
752
- exits normally, and proactive session setup is enabled but fails.
583
+ IN_SESSION -> EXITING_SESSION -> SETTING_UP -> READY: When user session
584
+ exits normally, and sandbox is set to reuse, and proactive session setup
585
+ is enabled.
586
+ IN_SESSION -> EXITING_SESSION -> SETTING_UP -> SHUTTING_DOWN -> OFFLINE:
587
+ When user session exits normally, and proactive session setup is enabled
588
+ but fails.
589
+ EXITING_SESSION -> EXITING_SESSION: No operation.
753
590
  not IN_SESSION -> same state: No operation
754
591
 
755
592
  `end_session` should always be called for each `start_session` call, even
@@ -838,6 +675,10 @@ class Sandbox(pg.Object):
838
675
  return self.features[name]
839
676
  raise AttributeError(name)
840
677
 
678
+ def __del__(self):
679
+ """Deletes the sandbox."""
680
+ self.shutdown()
681
+
841
682
 
842
683
  class Feature(pg.Object):
843
684
  """Interface for sandbox features."""
@@ -969,3 +810,17 @@ class Feature(pg.Object):
969
810
  """Returns the current user session identifier."""
970
811
  assert self.sandbox is not None
971
812
  return self.sandbox.session_id
813
+
814
+
815
+ def _make_path_compatible(id_str: str) -> str:
816
+ """Makes a path compatible with CNS."""
817
+ return id_str.translate(
818
+ str.maketrans({
819
+ '@': '_',
820
+ ':': '_',
821
+ '#': '_',
822
+ ' ': '',
823
+ '<': '',
824
+ '>': '',
825
+ })
826
+ )
@@ -18,7 +18,7 @@ from langfun.env import interface
18
18
  class IdTest(unittest.TestCase):
19
19
 
20
20
  def test_environment_id(self):
21
- env_id = interface.EnvironmentId('env@1/a b:c#def')
21
+ env_id = interface.Environment.Id('env@1/a b:c#def')
22
22
  self.assertEqual(str(env_id), 'env@1/a b:c#def')
23
23
  self.assertEqual(
24
24
  env_id.working_dir(root_dir='/tmp'),
@@ -27,8 +27,8 @@ class IdTest(unittest.TestCase):
27
27
  self.assertIsNone(env_id.working_dir(root_dir=None))
28
28
 
29
29
  def test_sandbox_id(self):
30
- sandbox_id = interface.SandboxId(
31
- environment_id=interface.EnvironmentId('env'),
30
+ sandbox_id = interface.Sandbox.Id(
31
+ environment_id=interface.Environment.Id('env'),
32
32
  sandbox_id='sandbox'
33
33
  )
34
34
  self.assertEqual(str(sandbox_id), 'env/sandbox')
@@ -28,9 +28,9 @@ class TestingSandbox(interface.Sandbox):
28
28
  self._session_id = None
29
29
 
30
30
  @property
31
- def id(self) -> interface.SandboxId:
32
- return interface.SandboxId(
33
- environment_id=interface.EnvironmentId('testing-env'),
31
+ def id(self) -> interface.Sandbox.Id:
32
+ return interface.Sandbox.Id(
33
+ environment_id=interface.Environment.Id('testing-env'),
34
34
  sandbox_id=self.sandbox_id
35
35
  )
36
36
 
@@ -52,18 +52,6 @@ class TestingSandbox(interface.Sandbox):
52
52
  def set_acquired(self) -> None:
53
53
  self.set_status(self.Status.ACQUIRED)
54
54
 
55
- def add_event_handler(
56
- self,
57
- event_handler: interface.EnvironmentEventHandler
58
- ) -> None:
59
- pass
60
-
61
- def remove_event_handler(
62
- self,
63
- event_handler: interface.EnvironmentEventHandler
64
- ) -> None:
65
- pass
66
-
67
55
  def start(self) -> None:
68
56
  self.set_alive()
69
57