langfun 0.1.2.dev202509120804__py3-none-any.whl → 0.1.2.dev202512150805__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.
Files changed (162) hide show
  1. langfun/__init__.py +1 -1
  2. langfun/core/__init__.py +7 -1
  3. langfun/core/agentic/__init__.py +8 -1
  4. langfun/core/agentic/action.py +740 -112
  5. langfun/core/agentic/action_eval.py +9 -2
  6. langfun/core/agentic/action_test.py +189 -24
  7. langfun/core/async_support.py +104 -5
  8. langfun/core/async_support_test.py +23 -0
  9. langfun/core/coding/python/correction.py +19 -9
  10. langfun/core/coding/python/execution.py +14 -12
  11. langfun/core/coding/python/generation.py +21 -16
  12. langfun/core/coding/python/sandboxing.py +23 -3
  13. langfun/core/component.py +42 -3
  14. langfun/core/concurrent.py +70 -6
  15. langfun/core/concurrent_test.py +9 -2
  16. langfun/core/console.py +1 -1
  17. langfun/core/data/conversion/anthropic.py +12 -3
  18. langfun/core/data/conversion/anthropic_test.py +8 -6
  19. langfun/core/data/conversion/gemini.py +11 -2
  20. langfun/core/data/conversion/gemini_test.py +48 -9
  21. langfun/core/data/conversion/openai.py +145 -31
  22. langfun/core/data/conversion/openai_test.py +161 -17
  23. langfun/core/eval/base.py +48 -44
  24. langfun/core/eval/base_test.py +5 -5
  25. langfun/core/eval/matching.py +5 -2
  26. langfun/core/eval/patching.py +3 -3
  27. langfun/core/eval/scoring.py +4 -3
  28. langfun/core/eval/v2/__init__.py +3 -0
  29. langfun/core/eval/v2/checkpointing.py +148 -46
  30. langfun/core/eval/v2/checkpointing_test.py +9 -2
  31. langfun/core/eval/v2/config_saver.py +37 -0
  32. langfun/core/eval/v2/config_saver_test.py +36 -0
  33. langfun/core/eval/v2/eval_test_helper.py +104 -3
  34. langfun/core/eval/v2/evaluation.py +102 -19
  35. langfun/core/eval/v2/evaluation_test.py +9 -3
  36. langfun/core/eval/v2/example.py +50 -40
  37. langfun/core/eval/v2/example_test.py +16 -8
  38. langfun/core/eval/v2/experiment.py +95 -20
  39. langfun/core/eval/v2/experiment_test.py +19 -0
  40. langfun/core/eval/v2/metric_values.py +31 -3
  41. langfun/core/eval/v2/metric_values_test.py +32 -0
  42. langfun/core/eval/v2/metrics.py +157 -44
  43. langfun/core/eval/v2/metrics_test.py +39 -18
  44. langfun/core/eval/v2/progress.py +31 -1
  45. langfun/core/eval/v2/progress_test.py +27 -0
  46. langfun/core/eval/v2/progress_tracking.py +13 -5
  47. langfun/core/eval/v2/progress_tracking_test.py +9 -1
  48. langfun/core/eval/v2/reporting.py +88 -71
  49. langfun/core/eval/v2/reporting_test.py +24 -6
  50. langfun/core/eval/v2/runners/__init__.py +30 -0
  51. langfun/core/eval/v2/{runners.py → runners/base.py} +73 -180
  52. langfun/core/eval/v2/runners/beam.py +354 -0
  53. langfun/core/eval/v2/runners/beam_test.py +153 -0
  54. langfun/core/eval/v2/runners/ckpt_monitor.py +350 -0
  55. langfun/core/eval/v2/runners/ckpt_monitor_test.py +213 -0
  56. langfun/core/eval/v2/runners/debug.py +40 -0
  57. langfun/core/eval/v2/runners/debug_test.py +76 -0
  58. langfun/core/eval/v2/runners/parallel.py +243 -0
  59. langfun/core/eval/v2/runners/parallel_test.py +182 -0
  60. langfun/core/eval/v2/runners/sequential.py +47 -0
  61. langfun/core/eval/v2/runners/sequential_test.py +169 -0
  62. langfun/core/langfunc.py +45 -130
  63. langfun/core/langfunc_test.py +7 -5
  64. langfun/core/language_model.py +189 -36
  65. langfun/core/language_model_test.py +54 -3
  66. langfun/core/llms/__init__.py +14 -1
  67. langfun/core/llms/anthropic.py +157 -2
  68. langfun/core/llms/azure_openai.py +29 -17
  69. langfun/core/llms/cache/base.py +25 -3
  70. langfun/core/llms/cache/in_memory.py +48 -7
  71. langfun/core/llms/cache/in_memory_test.py +14 -4
  72. langfun/core/llms/compositional.py +25 -1
  73. langfun/core/llms/deepseek.py +30 -2
  74. langfun/core/llms/fake.py +32 -1
  75. langfun/core/llms/gemini.py +90 -12
  76. langfun/core/llms/gemini_test.py +110 -0
  77. langfun/core/llms/google_genai.py +52 -1
  78. langfun/core/llms/groq.py +28 -3
  79. langfun/core/llms/llama_cpp.py +23 -4
  80. langfun/core/llms/openai.py +120 -3
  81. langfun/core/llms/openai_compatible.py +148 -27
  82. langfun/core/llms/openai_compatible_test.py +207 -20
  83. langfun/core/llms/openai_test.py +0 -2
  84. langfun/core/llms/rest.py +16 -1
  85. langfun/core/llms/vertexai.py +78 -8
  86. langfun/core/logging.py +1 -1
  87. langfun/core/mcp/__init__.py +10 -0
  88. langfun/core/mcp/client.py +177 -0
  89. langfun/core/mcp/client_test.py +71 -0
  90. langfun/core/mcp/session.py +241 -0
  91. langfun/core/mcp/session_test.py +54 -0
  92. langfun/core/mcp/testing/simple_mcp_client.py +33 -0
  93. langfun/core/mcp/testing/simple_mcp_server.py +33 -0
  94. langfun/core/mcp/tool.py +254 -0
  95. langfun/core/mcp/tool_test.py +197 -0
  96. langfun/core/memory.py +1 -0
  97. langfun/core/message.py +160 -55
  98. langfun/core/message_test.py +65 -81
  99. langfun/core/modalities/__init__.py +8 -0
  100. langfun/core/modalities/audio.py +21 -1
  101. langfun/core/modalities/image.py +73 -3
  102. langfun/core/modalities/image_test.py +116 -0
  103. langfun/core/modalities/mime.py +78 -4
  104. langfun/core/modalities/mime_test.py +59 -0
  105. langfun/core/modalities/pdf.py +19 -1
  106. langfun/core/modalities/video.py +21 -1
  107. langfun/core/modality.py +167 -29
  108. langfun/core/modality_test.py +42 -12
  109. langfun/core/natural_language.py +1 -1
  110. langfun/core/sampling.py +4 -4
  111. langfun/core/sampling_test.py +20 -4
  112. langfun/core/structured/__init__.py +2 -24
  113. langfun/core/structured/completion.py +34 -44
  114. langfun/core/structured/completion_test.py +23 -43
  115. langfun/core/structured/description.py +54 -50
  116. langfun/core/structured/function_generation.py +29 -12
  117. langfun/core/structured/mapping.py +81 -37
  118. langfun/core/structured/parsing.py +95 -79
  119. langfun/core/structured/parsing_test.py +0 -3
  120. langfun/core/structured/querying.py +230 -154
  121. langfun/core/structured/querying_test.py +69 -33
  122. langfun/core/structured/schema/__init__.py +49 -0
  123. langfun/core/structured/schema/base.py +664 -0
  124. langfun/core/structured/schema/base_test.py +531 -0
  125. langfun/core/structured/schema/json.py +174 -0
  126. langfun/core/structured/schema/json_test.py +121 -0
  127. langfun/core/structured/schema/python.py +316 -0
  128. langfun/core/structured/schema/python_test.py +410 -0
  129. langfun/core/structured/schema_generation.py +33 -14
  130. langfun/core/structured/scoring.py +47 -36
  131. langfun/core/structured/tokenization.py +26 -11
  132. langfun/core/subscription.py +2 -2
  133. langfun/core/template.py +175 -50
  134. langfun/core/template_test.py +123 -17
  135. langfun/env/__init__.py +43 -0
  136. langfun/env/base_environment.py +827 -0
  137. langfun/env/base_environment_test.py +473 -0
  138. langfun/env/base_feature.py +304 -0
  139. langfun/env/base_feature_test.py +228 -0
  140. langfun/env/base_sandbox.py +842 -0
  141. langfun/env/base_sandbox_test.py +1235 -0
  142. langfun/env/event_handlers/__init__.py +14 -0
  143. langfun/env/event_handlers/chain.py +233 -0
  144. langfun/env/event_handlers/chain_test.py +253 -0
  145. langfun/env/event_handlers/event_logger.py +472 -0
  146. langfun/env/event_handlers/event_logger_test.py +304 -0
  147. langfun/env/event_handlers/metric_writer.py +726 -0
  148. langfun/env/event_handlers/metric_writer_test.py +214 -0
  149. langfun/env/interface.py +1640 -0
  150. langfun/env/interface_test.py +153 -0
  151. langfun/env/load_balancers.py +59 -0
  152. langfun/env/load_balancers_test.py +141 -0
  153. langfun/env/test_utils.py +507 -0
  154. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/METADATA +7 -3
  155. langfun-0.1.2.dev202512150805.dist-info/RECORD +217 -0
  156. langfun/core/eval/v2/runners_test.py +0 -343
  157. langfun/core/structured/schema.py +0 -987
  158. langfun/core/structured/schema_test.py +0 -982
  159. langfun-0.1.2.dev202509120804.dist-info/RECORD +0 -172
  160. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/WHEEL +0 -0
  161. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/licenses/LICENSE +0 -0
  162. {langfun-0.1.2.dev202509120804.dist-info → langfun-0.1.2.dev202512150805.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,14 @@
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.chain import EventHandlerChain
7
+
8
+ from langfun.env.event_handlers.event_logger import EventLogger
9
+ from langfun.env.event_handlers.event_logger import ConsoleEventLogger
10
+
11
+ from langfun.env.event_handlers.metric_writer import MetricWriter
12
+
13
+ # pylint: enable=g-importing-member
14
+ # pylint: enable=g-bad-import-order
@@ -0,0 +1,233 @@
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
+ """Environment event handler chain."""
15
+
16
+ from typing import Sequence
17
+ from langfun.env import interface
18
+
19
+
20
+ class EventHandlerChain(interface.EventHandler):
21
+ """Environment event handler chain."""
22
+
23
+ def __init__(self, handlers: Sequence[interface.EventHandler]):
24
+ super().__init__()
25
+ self._handlers = list(handlers)
26
+
27
+ def add(self, handler: interface.EventHandler):
28
+ self._handlers.append(handler)
29
+
30
+ def remove(self, handler: interface.EventHandler):
31
+ self._handlers.remove(handler)
32
+
33
+ def on_environment_starting(
34
+ self,
35
+ environment: interface.Environment,
36
+ ) -> None:
37
+ """Called when the environment is starting."""
38
+ for handler in self._handlers:
39
+ handler.on_environment_starting(environment)
40
+
41
+ def on_environment_shutting_down(
42
+ self,
43
+ environment: interface.Environment,
44
+ offline_duration: float,
45
+ ) -> None:
46
+ """Called when the environment is shutting down."""
47
+ for handler in self._handlers:
48
+ handler.on_environment_shutting_down(environment, offline_duration)
49
+
50
+ def on_environment_start(
51
+ self,
52
+ environment: interface.Environment,
53
+ duration: float,
54
+ error: BaseException | None
55
+ ) -> None:
56
+ """Called when the environment is started."""
57
+ for handler in self._handlers:
58
+ handler.on_environment_start(environment, duration, error)
59
+
60
+ def on_environment_housekeep(
61
+ self,
62
+ environment: interface.Environment,
63
+ counter: int,
64
+ duration: float,
65
+ error: BaseException | None,
66
+ **kwargs
67
+ ) -> None:
68
+ """Called when the environment is housekeeping."""
69
+ for handler in self._handlers:
70
+ handler.on_environment_housekeep(
71
+ environment, counter, duration, error, **kwargs
72
+ )
73
+
74
+ def on_environment_shutdown(
75
+ self,
76
+ environment: interface.Environment,
77
+ duration: float,
78
+ lifetime: float,
79
+ error: BaseException | None
80
+ ) -> None:
81
+ """Called when the environment is shutdown."""
82
+ for handler in self._handlers:
83
+ handler.on_environment_shutdown(environment, duration, lifetime, error)
84
+
85
+ def on_sandbox_start(
86
+ self,
87
+ sandbox: interface.Sandbox,
88
+ duration: float,
89
+ error: BaseException | None
90
+ ) -> None:
91
+ for handler in self._handlers:
92
+ handler.on_sandbox_start(sandbox, duration, error)
93
+
94
+ def on_sandbox_status_change(
95
+ self,
96
+ sandbox: interface.Sandbox,
97
+ old_status: interface.Sandbox.Status,
98
+ new_status: interface.Sandbox.Status,
99
+ span: float
100
+ ) -> None:
101
+ for handler in self._handlers:
102
+ handler.on_sandbox_status_change(sandbox, old_status, new_status, span)
103
+
104
+ def on_sandbox_shutdown(
105
+ self,
106
+ sandbox: interface.Sandbox,
107
+ duration: float,
108
+ lifetime: float,
109
+ error: BaseException | None
110
+ ) -> None:
111
+ for handler in self._handlers:
112
+ handler.on_sandbox_shutdown(sandbox, duration, lifetime, error)
113
+
114
+ def on_sandbox_session_start(
115
+ self,
116
+ sandbox: interface.Sandbox,
117
+ session_id: str,
118
+ duration: float,
119
+ error: BaseException | None
120
+ ) -> None:
121
+ """Called when a sandbox session starts."""
122
+ for handler in self._handlers:
123
+ handler.on_sandbox_session_start(sandbox, session_id, duration, error)
124
+
125
+ def on_sandbox_session_end(
126
+ self,
127
+ sandbox: interface.Sandbox,
128
+ session_id: str,
129
+ duration: float,
130
+ lifetime: float,
131
+ error: BaseException | None
132
+ ) -> None:
133
+ """Called when a sandbox session ends."""
134
+ for handler in self._handlers:
135
+ handler.on_sandbox_session_end(
136
+ sandbox, session_id, duration, lifetime, error
137
+ )
138
+
139
+ def on_sandbox_activity(
140
+ self,
141
+ name: str,
142
+ sandbox: interface.Sandbox,
143
+ session_id: str | None,
144
+ duration: float,
145
+ error: BaseException | None,
146
+ **kwargs
147
+ ) -> None:
148
+ """Called when a sandbox activity is performed."""
149
+ for handler in self._handlers:
150
+ handler.on_sandbox_activity(
151
+ name, sandbox, session_id, duration, error, **kwargs
152
+ )
153
+
154
+ def on_sandbox_housekeep(
155
+ self,
156
+ sandbox: interface.Sandbox,
157
+ counter: int,
158
+ duration: float,
159
+ error: BaseException | None,
160
+ **kwargs
161
+ ) -> None:
162
+ """Called when a sandbox feature is housekeeping."""
163
+ for handler in self._handlers:
164
+ handler.on_sandbox_housekeep(sandbox, counter, duration, error, **kwargs)
165
+
166
+ def on_feature_setup(
167
+ self,
168
+ feature: interface.Feature,
169
+ duration: float,
170
+ error: BaseException | None
171
+ ) -> None:
172
+ """Called when a sandbox feature is setup."""
173
+ for handler in self._handlers:
174
+ handler.on_feature_setup(feature, duration, error)
175
+
176
+ def on_feature_teardown(
177
+ self,
178
+ feature: interface.Feature,
179
+ duration: float,
180
+ error: BaseException | None
181
+ ) -> None:
182
+ """Called when a sandbox feature is teardown."""
183
+ for handler in self._handlers:
184
+ handler.on_feature_teardown(feature, duration, error)
185
+
186
+ def on_feature_setup_session(
187
+ self,
188
+ feature: interface.Feature,
189
+ session_id: str | None,
190
+ duration: float,
191
+ error: BaseException | None
192
+ ) -> None:
193
+ """Called when a sandbox feature is setup."""
194
+ for handler in self._handlers:
195
+ handler.on_feature_setup_session(feature, session_id, duration, error)
196
+
197
+ def on_feature_teardown_session(
198
+ self,
199
+ feature: interface.Feature,
200
+ session_id: str,
201
+ duration: float,
202
+ error: BaseException | None
203
+ ) -> None:
204
+ """Called when a sandbox feature is teardown."""
205
+ for handler in self._handlers:
206
+ handler.on_feature_teardown_session(feature, session_id, duration, error)
207
+
208
+ def on_feature_activity(
209
+ self,
210
+ name: str,
211
+ feature: interface.Feature,
212
+ session_id: str | None,
213
+ duration: float,
214
+ error: BaseException | None,
215
+ **kwargs
216
+ ) -> None:
217
+ """Called when a feature activity is performed."""
218
+ for handler in self._handlers:
219
+ handler.on_feature_activity(
220
+ name, feature, session_id, duration, error, **kwargs
221
+ )
222
+
223
+ def on_feature_housekeep(
224
+ self,
225
+ feature: interface.Feature,
226
+ counter: int,
227
+ duration: float,
228
+ error: BaseException | None,
229
+ **kwargs
230
+ ) -> None:
231
+ """Called when a sandbox feature is housekeeping."""
232
+ for handler in self._handlers:
233
+ handler.on_feature_housekeep(feature, counter, duration, error, **kwargs)
@@ -0,0 +1,253 @@
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
+
15
+ import unittest
16
+ from langfun.env import interface
17
+ from langfun.env.event_handlers import chain
18
+
19
+
20
+ class MockEventHandler(interface.EventHandler):
21
+
22
+ def __init__(self):
23
+ self.calls = []
24
+
25
+ def _record_call(self, method_name, *args, **kwargs):
26
+ self.calls.append((method_name, args, kwargs))
27
+
28
+ def on_environment_starting(self, environment):
29
+ self._record_call('on_environment_starting', environment)
30
+
31
+ def on_environment_shutting_down(self, environment, offline_duration):
32
+ self._record_call(
33
+ 'on_environment_shutting_down', environment, offline_duration
34
+ )
35
+
36
+ def on_environment_start(self, environment, duration, error):
37
+ self._record_call('on_environment_start', environment, duration, error)
38
+
39
+ def on_environment_housekeep(
40
+ self, environment, counter, duration, error, **kwargs
41
+ ):
42
+ self._record_call(
43
+ 'on_environment_housekeep',
44
+ environment, counter, duration, error, **kwargs
45
+ )
46
+
47
+ def on_environment_shutdown(self, environment, duration, lifetime, error):
48
+ self._record_call(
49
+ 'on_environment_shutdown', environment, duration, lifetime, error
50
+ )
51
+
52
+ def on_sandbox_start(self, sandbox, duration, error):
53
+ self._record_call('on_sandbox_start', sandbox, duration, error)
54
+
55
+ def on_sandbox_status_change(
56
+ self, sandbox, old_status, new_status, span
57
+ ):
58
+ self._record_call(
59
+ 'on_sandbox_status_change',
60
+ sandbox,
61
+ old_status,
62
+ new_status,
63
+ span,
64
+ )
65
+
66
+ def on_sandbox_shutdown(
67
+ self, sandbox, duration, lifetime, error
68
+ ):
69
+ self._record_call('on_sandbox_shutdown', sandbox, duration, lifetime, error)
70
+
71
+ def on_sandbox_session_start(self, sandbox, session_id, duration, error):
72
+ self._record_call(
73
+ 'on_sandbox_session_start', sandbox, session_id, duration, error
74
+ )
75
+
76
+ def on_sandbox_session_end(
77
+ self, sandbox, session_id, duration, lifetime, error
78
+ ):
79
+ self._record_call(
80
+ 'on_sandbox_session_end',
81
+ sandbox,
82
+ session_id,
83
+ duration,
84
+ lifetime,
85
+ error,
86
+ )
87
+
88
+ def on_sandbox_activity(
89
+ self,
90
+ name,
91
+ sandbox,
92
+ session_id,
93
+ duration,
94
+ error,
95
+ **kwargs,
96
+ ):
97
+ self._record_call(
98
+ 'on_sandbox_activity',
99
+ name,
100
+ sandbox,
101
+ session_id,
102
+ duration,
103
+ error,
104
+ **kwargs,
105
+ )
106
+
107
+ def on_sandbox_housekeep(self, sandbox, counter, duration, error, **kwargs):
108
+ self._record_call(
109
+ 'on_sandbox_housekeep',
110
+ sandbox,
111
+ counter,
112
+ duration,
113
+ error,
114
+ **kwargs,
115
+ )
116
+
117
+ def on_feature_setup(self, feature, duration, error):
118
+ self._record_call('on_feature_setup', feature, duration, error)
119
+
120
+ def on_feature_teardown(self, feature, duration, error):
121
+ self._record_call('on_feature_teardown', feature, duration, error)
122
+
123
+ def on_feature_setup_session(self, feature, session_id, duration, error):
124
+ self._record_call(
125
+ 'on_feature_setup_session',
126
+ feature,
127
+ session_id,
128
+ duration,
129
+ error,
130
+ )
131
+
132
+ def on_feature_teardown_session(self, feature, session_id, duration, error):
133
+ self._record_call(
134
+ 'on_feature_teardown_session',
135
+ feature,
136
+ session_id,
137
+ duration,
138
+ error,
139
+ )
140
+
141
+ def on_feature_activity(
142
+ self, name, feature, session_id, duration, error, **kwargs,
143
+ ):
144
+ self._record_call(
145
+ 'on_feature_activity',
146
+ name,
147
+ feature,
148
+ session_id,
149
+ duration,
150
+ error,
151
+ **kwargs,
152
+ )
153
+
154
+ def on_feature_housekeep(
155
+ self, feature, counter, duration, error, **kwargs
156
+ ):
157
+ self._record_call(
158
+ 'on_feature_housekeep',
159
+ feature,
160
+ counter,
161
+ duration,
162
+ error,
163
+ **kwargs,
164
+ )
165
+
166
+
167
+ class EventHandlerChainTest(unittest.TestCase):
168
+
169
+ def test_chain(self):
170
+ handler1 = MockEventHandler()
171
+ handler2 = MockEventHandler()
172
+ chain_handler = chain.EventHandlerChain([handler1, handler2])
173
+
174
+ env = object()
175
+ sandbox = object()
176
+ feature = object()
177
+
178
+ chain_handler.on_environment_starting(env)
179
+ chain_handler.on_environment_shutting_down(env, 1.0)
180
+ chain_handler.on_environment_start(env, 2.0, None)
181
+ chain_handler.on_environment_housekeep(env, 1, 3.0, None, a=1)
182
+ chain_handler.on_environment_shutdown(env, 4.0, 5.0, None)
183
+ chain_handler.on_sandbox_start(sandbox, 6.0, None)
184
+ chain_handler.on_sandbox_status_change(sandbox, 'old', 'new', 7.0)
185
+ chain_handler.on_sandbox_shutdown(sandbox, 8.0, 9.0, None)
186
+ chain_handler.on_sandbox_session_start(sandbox, 's2', 16.0, None)
187
+ chain_handler.on_sandbox_session_end(sandbox, 's2', 17.0, 18.0, None)
188
+ chain_handler.on_sandbox_activity('act', sandbox, 's2', 19.0, None, d=4)
189
+ chain_handler.on_sandbox_housekeep(sandbox, 2, 10.0, None, b=2)
190
+ chain_handler.on_feature_setup(feature, 11.0, None)
191
+ chain_handler.on_feature_teardown(feature, 12.0, None)
192
+ chain_handler.on_feature_setup_session(feature, 's1', 13.0, None)
193
+ chain_handler.on_feature_teardown_session(feature, 's1', 14.0, None)
194
+ chain_handler.on_feature_activity('act', feature, 's1', 16.0, None, d=5)
195
+ chain_handler.on_feature_housekeep(feature, 3, 15.0, None, c=3)
196
+
197
+ self.assertEqual(handler1.calls, handler2.calls)
198
+ self.assertEqual(
199
+ handler1.calls,
200
+ [
201
+ ('on_environment_starting', (env,), {}),
202
+ ('on_environment_shutting_down', (env, 1.0), {}),
203
+ ('on_environment_start', (env, 2.0, None), {}),
204
+ ('on_environment_housekeep', (env, 1, 3.0, None), {'a': 1}),
205
+ ('on_environment_shutdown', (env, 4.0, 5.0, None), {}),
206
+ ('on_sandbox_start', (sandbox, 6.0, None), {}),
207
+ ('on_sandbox_status_change', (sandbox, 'old', 'new', 7.0), {}),
208
+ ('on_sandbox_shutdown', (sandbox, 8.0, 9.0, None), {}),
209
+ ('on_sandbox_session_start', (sandbox, 's2', 16.0, None), {}),
210
+ ('on_sandbox_session_end', (sandbox, 's2', 17.0, 18.0, None), {}),
211
+ (
212
+ 'on_sandbox_activity',
213
+ ('act', sandbox, 's2', 19.0, None),
214
+ {'d': 4},
215
+ ),
216
+ ('on_sandbox_housekeep', (sandbox, 2, 10.0, None), {'b': 2}),
217
+ ('on_feature_setup', (feature, 11.0, None), {}),
218
+ ('on_feature_teardown', (feature, 12.0, None), {}),
219
+ ('on_feature_setup_session', (feature, 's1', 13.0, None), {}),
220
+ ('on_feature_teardown_session', (feature, 's1', 14.0, None), {}),
221
+ (
222
+ 'on_feature_activity',
223
+ ('act', feature, 's1', 16.0, None),
224
+ {'d': 5}
225
+ ),
226
+ ('on_feature_housekeep', (feature, 3, 15.0, None), {'c': 3}),
227
+ ],
228
+ )
229
+
230
+ def test_add_remove(self):
231
+ handler1 = MockEventHandler()
232
+ handler2 = MockEventHandler()
233
+ chain_handler = chain.EventHandlerChain([handler1])
234
+ chain_handler.add(handler2)
235
+ env = object()
236
+ chain_handler.on_environment_starting(env)
237
+ self.assertEqual(handler1.calls, [('on_environment_starting', (env,), {})])
238
+ self.assertEqual(handler2.calls, [('on_environment_starting', (env,), {})])
239
+
240
+ chain_handler.remove(handler1)
241
+ chain_handler.on_environment_start(env, 1.0, None)
242
+ self.assertEqual(handler1.calls, [('on_environment_starting', (env,), {})])
243
+ self.assertEqual(
244
+ handler2.calls,
245
+ [
246
+ ('on_environment_starting', (env,), {}),
247
+ ('on_environment_start', (env, 1.0, None), {}),
248
+ ],
249
+ )
250
+
251
+
252
+ if __name__ == '__main__':
253
+ unittest.main()