langfun 0.1.2.dev202511010804__tar.gz → 0.1.2.dev202511030805__tar.gz

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.

Files changed (205) hide show
  1. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/PKG-INFO +1 -1
  2. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/runners_test.py +3 -0
  3. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/__init__.py +1 -1
  4. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/base_environment.py +62 -5
  5. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/base_environment_test.py +5 -5
  6. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/base_feature.py +38 -20
  7. langfun-0.1.2.dev202511030805/langfun/env/base_feature_test.py +228 -0
  8. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/base_sandbox.py +27 -33
  9. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/base_sandbox_test.py +258 -251
  10. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/chain.py +58 -80
  11. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/chain_test.py +83 -111
  12. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/event_logger.py +77 -80
  13. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/event_logger_test.py +18 -18
  14. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/metric_writer.py +176 -140
  15. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/interface.py +493 -199
  16. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/interface_test.py +30 -1
  17. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/test_utils.py +110 -78
  18. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun.egg-info/PKG-INFO +1 -1
  19. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun.egg-info/SOURCES.txt +1 -0
  20. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/LICENSE +0 -0
  21. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/README.md +0 -0
  22. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/__init__.py +0 -0
  23. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/__init__.py +0 -0
  24. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/bounding_box_parser.py +0 -0
  25. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/bounding_box_parser_test.py +0 -0
  26. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/drawing.py +0 -0
  27. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/drawing_test.py +0 -0
  28. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/location.py +0 -0
  29. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/assistant/capabilities/gui/location_test.py +0 -0
  30. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/__init__.py +0 -0
  31. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/agentic/__init__.py +0 -0
  32. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/agentic/action.py +0 -0
  33. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/agentic/action_eval.py +0 -0
  34. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/agentic/action_eval_test.py +0 -0
  35. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/agentic/action_test.py +0 -0
  36. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/async_support.py +0 -0
  37. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/async_support_test.py +0 -0
  38. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/__init__.py +0 -0
  39. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/__init__.py +0 -0
  40. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/correction.py +0 -0
  41. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/correction_test.py +0 -0
  42. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/execution.py +0 -0
  43. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/execution_test.py +0 -0
  44. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/generation.py +0 -0
  45. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/generation_test.py +0 -0
  46. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/parsing.py +0 -0
  47. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/parsing_test.py +0 -0
  48. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/sandboxing.py +0 -0
  49. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/coding/python/sandboxing_test.py +0 -0
  50. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/component.py +0 -0
  51. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/component_test.py +0 -0
  52. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/concurrent.py +0 -0
  53. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/concurrent_test.py +0 -0
  54. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/console.py +0 -0
  55. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/console_test.py +0 -0
  56. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/__init__.py +0 -0
  57. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/__init__.py +0 -0
  58. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/anthropic.py +0 -0
  59. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/anthropic_test.py +0 -0
  60. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/gemini.py +0 -0
  61. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/gemini_test.py +0 -0
  62. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/openai.py +0 -0
  63. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/data/conversion/openai_test.py +0 -0
  64. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/__init__.py +0 -0
  65. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/base.py +0 -0
  66. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/base_test.py +0 -0
  67. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/matching.py +0 -0
  68. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/matching_test.py +0 -0
  69. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/patching.py +0 -0
  70. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/patching_test.py +0 -0
  71. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/scoring.py +0 -0
  72. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/scoring_test.py +0 -0
  73. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/__init__.py +0 -0
  74. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/checkpointing.py +0 -0
  75. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/checkpointing_test.py +0 -0
  76. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/eval_test_helper.py +0 -0
  77. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/evaluation.py +0 -0
  78. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/evaluation_test.py +0 -0
  79. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/example.py +0 -0
  80. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/example_test.py +0 -0
  81. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/experiment.py +0 -0
  82. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/experiment_test.py +0 -0
  83. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/metric_values.py +0 -0
  84. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/metric_values_test.py +0 -0
  85. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/metrics.py +0 -0
  86. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/metrics_test.py +0 -0
  87. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/progress.py +0 -0
  88. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/progress_test.py +0 -0
  89. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/progress_tracking.py +0 -0
  90. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/progress_tracking_test.py +0 -0
  91. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/reporting.py +0 -0
  92. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/reporting_test.py +0 -0
  93. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/eval/v2/runners.py +0 -0
  94. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/langfunc.py +0 -0
  95. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/langfunc_test.py +0 -0
  96. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/language_model.py +0 -0
  97. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/language_model_test.py +0 -0
  98. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/__init__.py +0 -0
  99. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/anthropic.py +0 -0
  100. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/anthropic_test.py +0 -0
  101. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/azure_openai.py +0 -0
  102. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/azure_openai_test.py +0 -0
  103. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/cache/__init__.py +0 -0
  104. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/cache/base.py +0 -0
  105. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/cache/in_memory.py +0 -0
  106. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/cache/in_memory_test.py +0 -0
  107. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/compositional.py +0 -0
  108. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/compositional_test.py +0 -0
  109. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/deepseek.py +0 -0
  110. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/deepseek_test.py +0 -0
  111. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/fake.py +0 -0
  112. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/fake_test.py +0 -0
  113. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/gemini.py +0 -0
  114. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/gemini_test.py +0 -0
  115. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/google_genai.py +0 -0
  116. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/google_genai_test.py +0 -0
  117. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/groq.py +0 -0
  118. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/groq_test.py +0 -0
  119. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/llama_cpp.py +0 -0
  120. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/llama_cpp_test.py +0 -0
  121. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/openai.py +0 -0
  122. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/openai_compatible.py +0 -0
  123. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/openai_compatible_test.py +0 -0
  124. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/openai_test.py +0 -0
  125. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/rest.py +0 -0
  126. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/rest_test.py +0 -0
  127. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/vertexai.py +0 -0
  128. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/llms/vertexai_test.py +0 -0
  129. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/logging.py +0 -0
  130. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/logging_test.py +0 -0
  131. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/__init__.py +0 -0
  132. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/client.py +0 -0
  133. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/client_test.py +0 -0
  134. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/session.py +0 -0
  135. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/session_test.py +0 -0
  136. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/testing/simple_mcp_client.py +0 -0
  137. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/testing/simple_mcp_server.py +0 -0
  138. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/tool.py +0 -0
  139. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/mcp/tool_test.py +0 -0
  140. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/memories/__init__.py +0 -0
  141. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/memories/conversation_history.py +0 -0
  142. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/memories/conversation_history_test.py +0 -0
  143. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/memory.py +0 -0
  144. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/message.py +0 -0
  145. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/message_test.py +0 -0
  146. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/__init__.py +0 -0
  147. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/audio.py +0 -0
  148. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/audio_test.py +0 -0
  149. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/image.py +0 -0
  150. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/image_test.py +0 -0
  151. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/mime.py +0 -0
  152. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/mime_test.py +0 -0
  153. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/pdf.py +0 -0
  154. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/pdf_test.py +0 -0
  155. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/video.py +0 -0
  156. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modalities/video_test.py +0 -0
  157. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modality.py +0 -0
  158. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/modality_test.py +0 -0
  159. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/natural_language.py +0 -0
  160. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/natural_language_test.py +0 -0
  161. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/sampling.py +0 -0
  162. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/sampling_test.py +0 -0
  163. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/__init__.py +0 -0
  164. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/completion.py +0 -0
  165. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/completion_test.py +0 -0
  166. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/description.py +0 -0
  167. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/description_test.py +0 -0
  168. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/function_generation.py +0 -0
  169. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/function_generation_test.py +0 -0
  170. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/mapping.py +0 -0
  171. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/mapping_test.py +0 -0
  172. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/parsing.py +0 -0
  173. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/parsing_test.py +0 -0
  174. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/querying.py +0 -0
  175. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/querying_test.py +0 -0
  176. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/schema.py +0 -0
  177. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/schema_generation.py +0 -0
  178. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/schema_generation_test.py +0 -0
  179. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/schema_test.py +0 -0
  180. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/scoring.py +0 -0
  181. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/scoring_test.py +0 -0
  182. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/tokenization.py +0 -0
  183. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/structured/tokenization_test.py +0 -0
  184. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/subscription.py +0 -0
  185. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/subscription_test.py +0 -0
  186. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/template.py +0 -0
  187. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/template_test.py +0 -0
  188. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/__init__.py +0 -0
  189. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/completion.py +0 -0
  190. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/completion_test.py +0 -0
  191. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/conversation.py +0 -0
  192. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/conversation_test.py +0 -0
  193. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/demonstration.py +0 -0
  194. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/demonstration_test.py +0 -0
  195. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/selfplay.py +0 -0
  196. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/core/templates/selfplay_test.py +0 -0
  197. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/__init__.py +0 -0
  198. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/event_handlers/metric_writer_test.py +0 -0
  199. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/load_balancers.py +0 -0
  200. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun/env/load_balancers_test.py +0 -0
  201. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun.egg-info/dependency_links.txt +0 -0
  202. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun.egg-info/requires.txt +0 -0
  203. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/langfun.egg-info/top_level.txt +0 -0
  204. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/setup.cfg +0 -0
  205. {langfun-0.1.2.dev202511010804 → langfun-0.1.2.dev202511030805}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: langfun
3
- Version: 0.1.2.dev202511010804
3
+ Version: 0.1.2.dev202511030805
4
4
  Summary: Langfun: Language as Functions.
5
5
  Home-page: https://github.com/google/langfun
6
6
  Author: Langfun Authors
@@ -100,6 +100,9 @@ class RunnerTest(unittest.TestCase):
100
100
  print(i, pg.diff(x, y))
101
101
  self.assertIs(x, y)
102
102
 
103
+
104
+ class SequentialRunnerTest(RunnerTest):
105
+
103
106
  def test_basic(self):
104
107
  plugin = TestPlugin()
105
108
  exp = eval_test_helper.test_experiment()
@@ -28,7 +28,7 @@ from langfun.env.interface import EventHandler
28
28
 
29
29
  # Decorators for sandbox/feature methods.
30
30
  from langfun.env.interface import treat_as_sandbox_state_error
31
- from langfun.env.interface import log_sandbox_activity
31
+ from langfun.env.interface import log_activity
32
32
 
33
33
  from langfun.env.base_environment import BaseEnvironment
34
34
  from langfun.env.base_sandbox import BaseSandbox
@@ -179,6 +179,7 @@ class BaseEnvironment(interface.Environment):
179
179
  )
180
180
  self._housekeep_thread = None
181
181
  self._offline_start_time = None
182
+ self._non_sandbox_based_features_with_setup_called = set()
182
183
 
183
184
  # Check image IDs and feature requirements.
184
185
  self._check_image_ids()
@@ -192,7 +193,9 @@ class BaseEnvironment(interface.Environment):
192
193
  if self.supports_dynamic_image_loading:
193
194
  return
194
195
  for name, feature in self.features.items():
195
- if any(feature.is_applicable(image_id) for image_id in self.image_ids):
196
+ if not feature.is_sandbox_based or any(
197
+ feature.is_applicable(image_id) for image_id in self.image_ids
198
+ ):
196
199
  continue
197
200
  raise ValueError(
198
201
  f'Feature {name!r} is not applicable to all available images: '
@@ -267,6 +270,13 @@ class BaseEnvironment(interface.Environment):
267
270
  def _start(self) -> None:
268
271
  """Implementation of starting the environment."""
269
272
  sandbox_startup_infos = []
273
+ self._non_sandbox_based_features_with_setup_called.clear()
274
+ # Setup all non-sandbox-based features.
275
+ for feature in self.non_sandbox_based_features():
276
+ self._non_sandbox_based_features_with_setup_called.add(feature.name)
277
+ feature.setup(sandbox=None)
278
+
279
+ # Setup sandbox pools.
270
280
  for image_id in self.image_ids:
271
281
  next_sandbox_id = 0
272
282
  if self.enable_pooling(image_id):
@@ -312,10 +322,15 @@ class BaseEnvironment(interface.Environment):
312
322
  self._housekeep_thread.join()
313
323
  self._housekeep_thread = None
314
324
 
315
- def _shutdown_sandbox(sandbox: base_sandbox.BaseSandbox) -> None:
316
- if sandbox is not None:
317
- sandbox.shutdown()
325
+ # Teardown all non-sandbox-based features.
326
+ for feature in self.non_sandbox_based_features():
327
+ if feature.name in self._non_sandbox_based_features_with_setup_called:
328
+ try:
329
+ feature.teardown()
330
+ except BaseException: # pylint: disable=broad-except
331
+ pass
318
332
 
333
+ # Shutdown sandbox pools.
319
334
  if self._sandbox_pool:
320
335
  sandboxes = []
321
336
  for sandbox in self._sandbox_pool.values():
@@ -323,6 +338,10 @@ class BaseEnvironment(interface.Environment):
323
338
  self._sandbox_pool = {}
324
339
 
325
340
  if sandboxes:
341
+ def _shutdown_sandbox(sandbox: base_sandbox.BaseSandbox) -> None:
342
+ if sandbox is not None:
343
+ sandbox.shutdown()
344
+
326
345
  _ = list(
327
346
  lf.concurrent_map(
328
347
  _shutdown_sandbox,
@@ -635,9 +654,45 @@ class BaseEnvironment(interface.Environment):
635
654
  indices_by_image_id[image_id].append(i)
636
655
  return indices_by_image_id
637
656
 
657
+ last_housekeep_time = {
658
+ f.name: time.time() for f in self.non_sandbox_based_features()
659
+ }
660
+
638
661
  while self._status not in (self.Status.SHUTTING_DOWN, self.Status.OFFLINE):
639
662
  housekeep_start_time = time.time()
663
+ feature_housekeep_successes = []
664
+ feature_housekeep_failures = []
665
+
666
+ # Housekeeping non-sandbox-based features.
667
+ for feature in self.non_sandbox_based_features():
668
+ if feature.housekeep_interval is None:
669
+ continue
670
+ if (last_housekeep_time[feature.name]
671
+ + feature.housekeep_interval < time.time()):
672
+ try:
673
+ feature.housekeep()
674
+ last_housekeep_time[feature.name] = time.time()
675
+ feature_housekeep_successes.append(feature.name)
676
+ except BaseException as e: # pylint: disable=broad-except
677
+ pg.logging.error(
678
+ '[%s/%s]: Feature housekeeping failed with error: %s.'
679
+ 'Shutting down environment...',
680
+ self.id,
681
+ feature.name,
682
+ e,
683
+ )
684
+ feature_housekeep_failures.append(feature.name)
685
+ self._housekeep_counter += 1
686
+ self.on_housekeep(
687
+ duration=time.time() - housekeep_start_time,
688
+ error=e,
689
+ feature_housekeep_successes=feature_housekeep_successes,
690
+ feature_housekeep_failures=feature_housekeep_failures,
691
+ )
692
+ self.shutdown()
693
+ return
640
694
 
695
+ # Replace dead sandboxes.
641
696
  is_online = True
642
697
  dead_sandbox_entries = []
643
698
  for image_id, sandboxes in self._sandbox_pool.items():
@@ -658,6 +713,8 @@ class BaseEnvironment(interface.Environment):
658
713
  duration = time.time() - housekeep_start_time
659
714
 
660
715
  kwargs = dict(
716
+ feature_housekeep_successes=feature_housekeep_successes,
717
+ feature_housekeep_failures=feature_housekeep_failures,
661
718
  dead_sandboxes=_indices_by_image_id(dead_sandbox_entries),
662
719
  replaced_sandboxes=replaced_indices_by_image_id,
663
720
  offline_duration=self.offline_duration,
@@ -666,7 +723,6 @@ class BaseEnvironment(interface.Environment):
666
723
  self.on_housekeep(duration, **kwargs)
667
724
  time.sleep(self.housekeep_interval)
668
725
  else:
669
- self.shutdown()
670
726
  self.on_housekeep(
671
727
  duration,
672
728
  interface.EnvironmentOutageError(
@@ -674,6 +730,7 @@ class BaseEnvironment(interface.Environment):
674
730
  ),
675
731
  **kwargs
676
732
  )
733
+ self.shutdown()
677
734
 
678
735
  def _replace_dead_sandboxes(
679
736
  self,
@@ -57,7 +57,7 @@ class BaseEnvironmentTests(unittest.TestCase):
57
57
  self.assertEqual(env.sandbox_pool, {})
58
58
  self.assertEqual(env.working_dir, '/tmp/testing-env')
59
59
 
60
- with env.sandbox(session_id='session1') as sb:
60
+ with env.sandbox('session1') as sb:
61
61
  self.assertEqual(
62
62
  sb.id, interface.Sandbox.Id(
63
63
  environment_id=env.id,
@@ -79,7 +79,7 @@ class BaseEnvironmentTests(unittest.TestCase):
79
79
  with self.assertRaisesRegex(
80
80
  ValueError, 'Environment .* does not serve image ID .*'
81
81
  ):
82
- env.sandbox('test_image2')
82
+ env.sandbox(image_id='test_image2')
83
83
 
84
84
  with env.test_feature() as feature:
85
85
  self.assertIsInstance(feature, TestingFeature)
@@ -174,7 +174,7 @@ class BaseEnvironmentTests(unittest.TestCase):
174
174
  with self.assertRaisesRegex(
175
175
  ValueError, 'Feature .* is not applicable to .*'
176
176
  ):
177
- with env.test_feature('test_image2'):
177
+ with env.test_feature(image_id='test_image2'):
178
178
  pass
179
179
 
180
180
  with env.test_feature2() as feature:
@@ -183,7 +183,7 @@ class BaseEnvironmentTests(unittest.TestCase):
183
183
  with env.test_feature3() as feature:
184
184
  self.assertEqual(feature.sandbox.image_id, 'test_image1')
185
185
 
186
- with env.test_feature3('test_image2') as feature:
186
+ with env.test_feature3(image_id='test_image2') as feature:
187
187
  self.assertEqual(feature.sandbox.image_id, 'test_image2')
188
188
 
189
189
  def test_feature_applicability_check(self):
@@ -218,7 +218,7 @@ class BaseEnvironmentTests(unittest.TestCase):
218
218
  pass
219
219
 
220
220
  # Dynamically loaded IDs.
221
- with env.test_feature2('test_image2') as feature:
221
+ with env.test_feature2(image_id='test_image2') as feature:
222
222
  self.assertEqual(feature.sandbox.image_id, 'test_image2')
223
223
 
224
224
  def test_pool_size(self):
@@ -11,9 +11,9 @@
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
- """Common base class for sandbox-based features.
14
+ """Common base class for environment features.
15
15
 
16
- This module provides an base class `BaseFeature` for sandbox-based features,
16
+ This module provides an base class `BaseFeature` for environment features,
17
17
  which provides event handlers for the feature lifecycle events, which can be
18
18
  overridden by subclasses to provide custom behaviors. Please note that this base
19
19
  class is intended to provide a convenient way to implement features, and not
@@ -22,18 +22,24 @@ coupled with `BaseEnvironment` and `BaseSandbox`, and is expected to work with
22
22
  the `Environment` and `Sandbox` interfaces directly.
23
23
  """
24
24
 
25
+ import contextlib
25
26
  import functools
26
27
  import os
27
28
  import re
28
29
  import time
29
- from typing import Annotated, Callable
30
+ from typing import Annotated, Any, Callable, Iterator
30
31
 
31
32
  from langfun.env import interface
32
33
  import pyglove as pg
33
34
 
34
35
 
35
36
  class BaseFeature(interface.Feature):
36
- """Common base class for sandbox-based features."""
37
+ """Common base class for environment features."""
38
+
39
+ is_sandbox_based: Annotated[
40
+ bool,
41
+ 'Whether the feature is sandbox-based.'
42
+ ] = True
37
43
 
38
44
  applicable_images: Annotated[
39
45
  list[str],
@@ -120,9 +126,11 @@ class BaseFeature(interface.Feature):
120
126
  return env
121
127
 
122
128
  @property
123
- def sandbox(self) -> interface.Sandbox:
129
+ def sandbox(self) -> interface.Sandbox | None:
124
130
  """Returns the sandbox that the feature is running in."""
125
- assert self._sandbox is not None, 'Feature has not been set up yet.'
131
+ assert self._sandbox is not None or not self.is_sandbox_based, (
132
+ 'Feature has not been set up yet.'
133
+ )
126
134
  return self._sandbox
127
135
 
128
136
  @property
@@ -159,7 +167,7 @@ class BaseFeature(interface.Feature):
159
167
  finally:
160
168
  event_handler(duration=time.time() - start_time, error=error)
161
169
 
162
- def setup(self, sandbox: interface.Sandbox) -> None:
170
+ def setup(self, sandbox: interface.Sandbox | None = None) -> None:
163
171
  """Sets up the feature."""
164
172
  self._sandbox = sandbox
165
173
  self._do(self._setup, self.on_setup)
@@ -198,8 +206,6 @@ class BaseFeature(interface.Feature):
198
206
  ) -> None:
199
207
  """Called when the feature is setup."""
200
208
  self.environment.event_handler.on_feature_setup(
201
- environment=self.environment,
202
- sandbox=self.sandbox,
203
209
  feature=self,
204
210
  duration=duration,
205
211
  error=error
@@ -212,8 +218,6 @@ class BaseFeature(interface.Feature):
212
218
  ) -> None:
213
219
  """Called when the feature is teardown."""
214
220
  self.environment.event_handler.on_feature_teardown(
215
- environment=self.environment,
216
- sandbox=self.sandbox,
217
221
  feature=self,
218
222
  duration=duration,
219
223
  error=error
@@ -227,8 +231,6 @@ class BaseFeature(interface.Feature):
227
231
  ) -> None:
228
232
  """Called when the feature has done housekeeping."""
229
233
  self.environment.event_handler.on_feature_housekeep(
230
- environment=self.environment,
231
- sandbox=self.sandbox,
232
234
  feature=self,
233
235
  counter=self._housekeep_counter,
234
236
  duration=duration,
@@ -243,8 +245,6 @@ class BaseFeature(interface.Feature):
243
245
  ) -> None:
244
246
  """Called when the feature is setup for a user session."""
245
247
  self.environment.event_handler.on_feature_setup_session(
246
- environment=self.environment,
247
- sandbox=self.sandbox,
248
248
  feature=self,
249
249
  session_id=self.session_id,
250
250
  duration=duration,
@@ -258,8 +258,6 @@ class BaseFeature(interface.Feature):
258
258
  ) -> None:
259
259
  """Called when the feature is teardown for a user session."""
260
260
  self.environment.event_handler.on_feature_teardown_session(
261
- environment=self.environment,
262
- sandbox=self.sandbox,
263
261
  feature=self,
264
262
  session_id=self.session_id,
265
263
  duration=duration,
@@ -274,13 +272,33 @@ class BaseFeature(interface.Feature):
274
272
  **kwargs
275
273
  ) -> None:
276
274
  """Called when a sandbox activity is performed."""
277
- self.environment.event_handler.on_sandbox_activity(
275
+ self.environment.event_handler.on_feature_activity(
278
276
  name=f'{self.name}.{name}',
279
- environment=self.environment,
280
- sandbox=self.sandbox,
281
277
  feature=self,
282
278
  session_id=self.session_id,
283
279
  duration=duration,
284
280
  error=error,
285
281
  **kwargs
286
282
  )
283
+
284
+ @contextlib.contextmanager
285
+ def track_activity(
286
+ self,
287
+ name: str,
288
+ **kwargs: Any
289
+ ) -> Iterator[None]:
290
+ """Context manager that tracks a feature activity."""
291
+ start_time = time.time()
292
+ error = None
293
+ try:
294
+ yield None
295
+ except BaseException as e: # pylint: disable=broad-except
296
+ error = e
297
+ raise
298
+ finally:
299
+ self.on_activity(
300
+ name=name,
301
+ duration=time.time() - start_time,
302
+ error=error,
303
+ **kwargs
304
+ )
@@ -0,0 +1,228 @@
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
+ import unittest
15
+
16
+ from langfun.env import test_utils
17
+
18
+ TestingEnvironment = test_utils.TestingEnvironment
19
+ TestingNonSandboxBasedFeature = test_utils.TestingNonSandboxBasedFeature
20
+ TestingEventHandler = test_utils.TestingEventHandler
21
+
22
+
23
+ class NonSandboxBasedFeatureTests(unittest.TestCase):
24
+
25
+ def test_basics(self):
26
+ feature = TestingNonSandboxBasedFeature()
27
+ event_handler = TestingEventHandler(
28
+ log_session_setup=True,
29
+ log_feature_setup=True,
30
+ log_sandbox_status=True
31
+ )
32
+ env = TestingEnvironment(
33
+ image_ids=[],
34
+ features={'test_feature': feature},
35
+ event_handler=event_handler,
36
+ )
37
+ self.assertFalse(env.is_online)
38
+ self.assertEqual(len(list(env.non_sandbox_based_features())), 1)
39
+ with env:
40
+ self.assertTrue(env.is_online)
41
+ with env.test_feature('session1') as feature:
42
+ self.assertIsNone(feature.sandbox)
43
+ self.assertEqual(feature.session_id, 'session1')
44
+
45
+ self.assertEqual(
46
+ event_handler.logs,
47
+ [
48
+ '[testing-env/test_feature] feature setup',
49
+ '[testing-env] environment started',
50
+ '[testing-env/test_feature@session1] feature setup session',
51
+ '[testing-env/test_feature@session1] feature teardown session',
52
+ '[testing-env/test_feature] feature teardown',
53
+ '[testing-env] environment shutdown'
54
+ ]
55
+ )
56
+
57
+ def test_feature_setup_error(self):
58
+ event_handler = TestingEventHandler(
59
+ log_session_setup=True,
60
+ log_feature_setup=True,
61
+ log_sandbox_status=True
62
+ )
63
+ env = TestingEnvironment(
64
+ image_ids=[],
65
+ features={
66
+ 'test_feature': TestingNonSandboxBasedFeature(
67
+ simulate_setup_error=ValueError
68
+ )
69
+ },
70
+ event_handler=event_handler,
71
+ )
72
+ with self.assertRaises(ValueError):
73
+ with env:
74
+ pass
75
+ self.assertEqual(
76
+ event_handler.logs,
77
+ [
78
+ '[testing-env/test_feature] feature setup with ValueError',
79
+ '[testing-env] environment started with ValueError',
80
+ '[testing-env/test_feature] feature teardown',
81
+ '[testing-env] environment shutdown'
82
+ ]
83
+ )
84
+
85
+ def test_feature_teardown_error(self):
86
+ event_handler = TestingEventHandler(
87
+ log_session_setup=True,
88
+ log_feature_setup=True,
89
+ log_sandbox_status=True
90
+ )
91
+ env = TestingEnvironment(
92
+ image_ids=[],
93
+ features={
94
+ 'test_feature': TestingNonSandboxBasedFeature(
95
+ simulate_teardown_error=ValueError
96
+ )
97
+ },
98
+ event_handler=event_handler,
99
+ )
100
+ with env:
101
+ pass
102
+ self.assertEqual(
103
+ event_handler.logs,
104
+ [
105
+ '[testing-env/test_feature] feature setup',
106
+ '[testing-env] environment started',
107
+ '[testing-env/test_feature] feature teardown with ValueError',
108
+ '[testing-env] environment shutdown'
109
+ ]
110
+ )
111
+
112
+ def test_feature_setup_session_error(self):
113
+ event_handler = TestingEventHandler(
114
+ log_session_setup=True,
115
+ log_feature_setup=True,
116
+ log_sandbox_status=True
117
+ )
118
+ env = TestingEnvironment(
119
+ image_ids=[],
120
+ features={
121
+ 'test_feature': TestingNonSandboxBasedFeature(
122
+ simulate_setup_session_error=ValueError
123
+ )
124
+ },
125
+ event_handler=event_handler,
126
+ )
127
+ with env:
128
+ with self.assertRaises(ValueError):
129
+ with env.test_feature('session1'):
130
+ pass
131
+ self.assertEqual(
132
+ event_handler.logs,
133
+ [
134
+ # pylint: disable=line-too-long
135
+ '[testing-env/test_feature] feature setup',
136
+ '[testing-env] environment started',
137
+ '[testing-env/test_feature@session1] feature setup session with ValueError',
138
+ '[testing-env/test_feature@session1] feature teardown session',
139
+ '[testing-env/test_feature] feature teardown',
140
+ '[testing-env] environment shutdown',
141
+ # pylint: enable=line-too-long
142
+ ]
143
+ )
144
+
145
+ def test_feature_teardown_session_error(self):
146
+ event_handler = TestingEventHandler(
147
+ log_session_setup=True,
148
+ log_feature_setup=True,
149
+ log_sandbox_status=True
150
+ )
151
+ env = TestingEnvironment(
152
+ image_ids=[],
153
+ features={
154
+ 'test_feature': TestingNonSandboxBasedFeature(
155
+ simulate_teardown_session_error=ValueError
156
+ )
157
+ },
158
+ event_handler=event_handler,
159
+ )
160
+ with env:
161
+ with env.test_feature('session1'):
162
+ pass
163
+ self.assertEqual(
164
+ event_handler.logs,
165
+ [
166
+ # pylint: disable=line-too-long
167
+ '[testing-env/test_feature] feature setup',
168
+ '[testing-env] environment started',
169
+ '[testing-env/test_feature@session1] feature setup session',
170
+ '[testing-env/test_feature@session1] feature teardown session with ValueError',
171
+ '[testing-env/test_feature] feature teardown',
172
+ '[testing-env] environment shutdown',
173
+ # pylint: enable=line-too-long
174
+ ]
175
+ )
176
+
177
+ def test_feature_housekeeping(self):
178
+ event_handler = TestingEventHandler(
179
+ log_sandbox_status=False,
180
+ log_feature_setup=False,
181
+ log_housekeep=True
182
+ )
183
+ env = TestingEnvironment(
184
+ image_ids=[],
185
+ features={
186
+ 'test_feature': TestingNonSandboxBasedFeature(
187
+ housekeep_interval=0.1
188
+ )
189
+ },
190
+ event_handler=event_handler,
191
+ housekeep_interval=0.2
192
+ )
193
+ with env:
194
+ env.wait_for_housekeeping()
195
+ self.assertIn(
196
+ '[testing-env/test_feature] feature housekeeping 0',
197
+ event_handler.logs
198
+ )
199
+
200
+ def test_feature_housekeeping_error(self):
201
+ event_handler = TestingEventHandler(
202
+ log_sandbox_status=False,
203
+ log_feature_setup=False,
204
+ log_housekeep=True
205
+ )
206
+ env = TestingEnvironment(
207
+ image_ids=[],
208
+ features={
209
+ 'test_feature': TestingNonSandboxBasedFeature(
210
+ simulate_housekeep_error=ValueError,
211
+ housekeep_interval=0.1
212
+ )
213
+ },
214
+ event_handler=event_handler,
215
+ housekeep_interval=0.2
216
+ )
217
+ with env:
218
+ env.wait_for_housekeeping()
219
+ self.assertFalse(env.is_online)
220
+ self.assertIn(
221
+ '[testing-env/test_feature] feature housekeeping 0 with ValueError',
222
+ event_handler.logs
223
+ )
224
+
225
+
226
+ if __name__ == '__main__':
227
+ unittest.main()
228
+