leaf-common 1.2.41__tar.gz → 1.2.43__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.
Files changed (266) hide show
  1. {leaf_common-1.2.41 → leaf_common-1.2.43}/PKG-INFO +1 -1
  2. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/asyncio/asyncio_executor.py +2 -1
  3. leaf_common-1.2.43/leaf_common/asyncio/asyncio_executor_pool.py +313 -0
  4. leaf_common-1.2.43/leaf_common/asyncio/event_loop_factory.py +78 -0
  5. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common.egg-info/PKG-INFO +1 -1
  6. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common.egg-info/SOURCES.txt +3 -0
  7. leaf_common-1.2.43/tests/asyncio/asyncio_executor_pool_gc_test.py +342 -0
  8. leaf_common-1.2.43/tests/asyncio/event_loop_factory_test.py +94 -0
  9. leaf_common-1.2.41/leaf_common/asyncio/asyncio_executor_pool.py +0 -134
  10. {leaf_common-1.2.41 → leaf_common-1.2.43}/.coveragerc +0 -0
  11. {leaf_common-1.2.41 → leaf_common-1.2.43}/.flake8 +0 -0
  12. {leaf_common-1.2.41 → leaf_common-1.2.43}/.github/workflows/publish.yml +0 -0
  13. {leaf_common-1.2.41 → leaf_common-1.2.43}/.github/workflows/tests.yml +0 -0
  14. {leaf_common-1.2.41 → leaf_common-1.2.43}/.gitignore +0 -0
  15. {leaf_common-1.2.41 → leaf_common-1.2.43}/.pylintrc +0 -0
  16. {leaf_common-1.2.41 → leaf_common-1.2.43}/CODE_OF_CONDUCT.md +0 -0
  17. {leaf_common-1.2.41 → leaf_common-1.2.43}/CONTRIBUTING.md +0 -0
  18. {leaf_common-1.2.41 → leaf_common-1.2.43}/LICENSE.txt +0 -0
  19. {leaf_common-1.2.41 → leaf_common-1.2.43}/Makefile +0 -0
  20. {leaf_common-1.2.41 → leaf_common-1.2.43}/NOTICE.txt +0 -0
  21. {leaf_common-1.2.41 → leaf_common-1.2.43}/README.md +0 -0
  22. {leaf_common-1.2.41 → leaf_common-1.2.43}/SECURITY.md +0 -0
  23. {leaf_common-1.2.41 → leaf_common-1.2.43}/SUPPORT.md +0 -0
  24. {leaf_common-1.2.41 → leaf_common-1.2.43}/build_scripts/copyright_config.json +0 -0
  25. {leaf_common-1.2.41 → leaf_common-1.2.43}/build_scripts/run_flake8.sh +0 -0
  26. {leaf_common-1.2.41 → leaf_common-1.2.43}/build_scripts/run_pylint.sh +0 -0
  27. {leaf_common-1.2.41 → leaf_common-1.2.43}/build_scripts/source_available_copyright.txt +0 -0
  28. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/__init__.py +0 -0
  29. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/asyncio/__init__.py +0 -0
  30. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/asyncio/async_to_sync_generator.py +0 -0
  31. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/asyncio/asyncio_threadpool_executor.py +0 -0
  32. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/asyncio/task_executor.py +0 -0
  33. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/candidates/__init__.py +0 -0
  34. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/candidates/constants.py +0 -0
  35. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/candidates/representation_types.py +0 -0
  36. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/__init__.py +0 -0
  37. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/config_filter.py +0 -0
  38. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/config_filter_chain.py +0 -0
  39. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/config_handler.py +0 -0
  40. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/dictionary_overlay.py +0 -0
  41. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/environment_defaults.py +0 -0
  42. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/file_of_class.py +0 -0
  43. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/resolver.py +0 -0
  44. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/config/resolver_util.py +0 -0
  45. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/evaluation/__init__.py +0 -0
  46. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/evaluation/component_evaluator.py +0 -0
  47. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/evaluation/metrics_merger.py +0 -0
  48. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/__init__.py +0 -0
  49. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/composite_string_filter.py +0 -0
  50. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/inclusionary_replacement_string_filter.py +0 -0
  51. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/replacement_string_filter.py +0 -0
  52. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/sanitizer_util.py +0 -0
  53. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/string_filter.py +0 -0
  54. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/strip_whitespace_string_filter.py +0 -0
  55. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/filters/tensorflow_field_name_filter.py +0 -0
  56. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/__init__.py +0 -0
  57. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/candidate_metrics_provider.py +0 -0
  58. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/cmp_to_key.py +0 -0
  59. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/comparator.py +0 -0
  60. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/fitness_objective.py +0 -0
  61. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/fitness_objectives.py +0 -0
  62. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/fitness_objectives_builder.py +0 -0
  63. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/fitness_objectives_from_config.py +0 -0
  64. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/metrics_based_individual_comparator.py +0 -0
  65. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/metrics_provider.py +0 -0
  66. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/none_comparator.py +0 -0
  67. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/number_comparator.py +0 -0
  68. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/fitness/reversed_comparator.py +0 -0
  69. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/log_utils/__init__.py +0 -0
  70. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/log_utils/message_type.py +0 -0
  71. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/log_utils/structured_message.py +0 -0
  72. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/logging/__init__.py +0 -0
  73. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/logging/example_logging_config.hocon +0 -0
  74. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/logging/logging_setup.py +0 -0
  75. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/logging/stream_to_logger.py +0 -0
  76. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/__init__.py +0 -0
  77. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/boolean_list_parser.py +0 -0
  78. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/boolean_parser.py +0 -0
  79. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/canonical_multi_config_parser.py +0 -0
  80. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/dictionary_extractor.py +0 -0
  81. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/field_extractor.py +0 -0
  82. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/list_parser.py +0 -0
  83. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/parser.py +0 -0
  84. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/parsers/string_parser.py +0 -0
  85. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/__init__.py +0 -0
  86. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/__init__.py +0 -0
  87. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/abstract_easy_persistence.py +0 -0
  88. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/composite_persistor.py +0 -0
  89. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/easy_hocon_persistence.py +0 -0
  90. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/easy_json_persistence.py +0 -0
  91. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/easy_txt_persistence.py +0 -0
  92. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/easy/easy_yaml_persistence.py +0 -0
  93. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/README.md +0 -0
  94. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/__init__.py +0 -0
  95. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/abstract_persistence.py +0 -0
  96. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/hocon_persistence.py +0 -0
  97. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/json_gzip_persistence.py +0 -0
  98. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/json_persistence.py +0 -0
  99. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/null_persistence.py +0 -0
  100. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/override_file_extension_provider.py +0 -0
  101. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/persistence_factory.py +0 -0
  102. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/raw_bytes_persistence.py +0 -0
  103. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/reference_helper.py +0 -0
  104. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/simple_file_persistence.py +0 -0
  105. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/text_persistence.py +0 -0
  106. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/factory/yaml_persistence.py +0 -0
  107. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/interface/__init__.py +0 -0
  108. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/interface/persistence.py +0 -0
  109. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/interface/persistence_mechanism.py +0 -0
  110. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/interface/persistor.py +0 -0
  111. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/interface/restorer.py +0 -0
  112. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/__init__.py +0 -0
  113. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/abstract_persistence_mechanism.py +0 -0
  114. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/local_file_persistence_mechanism.py +0 -0
  115. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/persistence_mechanism_factory.py +0 -0
  116. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/persistence_mechanisms.py +0 -0
  117. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/persistence/mechanism/s3_file_persistence_mechanism.py +0 -0
  118. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/__init__.py +0 -0
  119. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/composite_progress_reporter.py +0 -0
  120. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/iterating_progress_reporter.py +0 -0
  121. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/json_progress_reporter.py +0 -0
  122. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/progress_reporter.py +0 -0
  123. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/status_dict_progress_reporter.py +0 -0
  124. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/progress/worker_progress_reporter.py +0 -0
  125. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/__init__.py +0 -0
  126. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/registry/__init__.py +0 -0
  127. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/registry/representation_file_extension_provider_registry.py +0 -0
  128. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/registry/representation_persistence_registry.py +0 -0
  129. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/registry/representation_serialization_format_registry.py +0 -0
  130. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/registry/self_identifying_restorer.py +0 -0
  131. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/__init__.py +0 -0
  132. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/config/__init__.py +0 -0
  133. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/config/rule_set_config_helper.py +0 -0
  134. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/__init__.py +0 -0
  135. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/condition.py +0 -0
  136. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/features.py +0 -0
  137. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/rule.py +0 -0
  138. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/rule_set.py +0 -0
  139. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/rule_set_binding.py +0 -0
  140. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/data/rules_constants.py +0 -0
  141. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/__init__.py +0 -0
  142. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/condition_evaluator.py +0 -0
  143. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/rule_evaluator.py +0 -0
  144. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/rule_metrics_merger.py +0 -0
  145. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/rule_set_binding_evaluator.py +0 -0
  146. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/rule_set_evaluator.py +0 -0
  147. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/evaluation/rule_set_metrics_merger.py +0 -0
  148. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/persistence/__init__.py +0 -0
  149. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/persistence/rule_set_binding_file_persistence.py +0 -0
  150. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/persistence/rule_set_file_persistence.py +0 -0
  151. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/__init__.py +0 -0
  152. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/condition_dictionary_converter.py +0 -0
  153. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/rule_dictionary_converter.py +0 -0
  154. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/rule_set_binding_dictionary_converter.py +0 -0
  155. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/rule_set_dictionary_converter.py +0 -0
  156. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/representation/rule_based/serialization/rule_set_serialization_format.py +0 -0
  157. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/__init__.py +0 -0
  158. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/__init__.py +0 -0
  159. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/auth0_direct_service_accessor.py +0 -0
  160. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/leaf_service_access.py +0 -0
  161. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/service_accessor.py +0 -0
  162. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/service_accessor_factory.py +0 -0
  163. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/static_token_service_accessor.py +0 -0
  164. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/service/vault_dynamic_token_service_accessor.py +0 -0
  165. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/__init__.py +0 -0
  166. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/github_vault_login.py +0 -0
  167. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/kubernetes_vault_login.py +0 -0
  168. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/token_vault_login.py +0 -0
  169. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/vault_login.py +0 -0
  170. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/security/vault/vault_login_factory.py +0 -0
  171. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/__init__.py +0 -0
  172. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/__init__.py +0 -0
  173. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/buffered_gzip_serialization_format.py +0 -0
  174. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/chained_serialization_format.py +0 -0
  175. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/conversion_policy.py +0 -0
  176. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/gzip_serialization_format.py +0 -0
  177. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/hocon_serialization_format.py +0 -0
  178. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/json_serialization_format.py +0 -0
  179. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/raw_bytes_serialization_format.py +0 -0
  180. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/serialization_formats.py +0 -0
  181. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/text_serialization_format.py +0 -0
  182. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/format/yaml_serialization_format.py +0 -0
  183. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/__init__.py +0 -0
  184. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/deserializer.py +0 -0
  185. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/dictionary_converter.py +0 -0
  186. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/file_extension_provider.py +0 -0
  187. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/reference_pruner.py +0 -0
  188. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/self_identifying_representation_error.py +0 -0
  189. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/serialization_format.py +0 -0
  190. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/interface/serializer.py +0 -0
  191. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/__init__.py +0 -0
  192. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/empty_dictionary_converter.py +0 -0
  193. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/metrics_dictionary_converter.py +0 -0
  194. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/pass_through_dictionary_converter.py +0 -0
  195. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/pass_through_reference_pruner.py +0 -0
  196. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/prep/redactor_dictionary_converter.py +0 -0
  197. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/util/__init__.py +0 -0
  198. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/util/bytes_decoder.py +0 -0
  199. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/serialization/util/text_file_reader.py +0 -0
  200. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/__init__.py +0 -0
  201. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/abstract_service_session.py +0 -0
  202. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/async_abstract_service_session.py +0 -0
  203. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/async_grpc_client_retry.py +0 -0
  204. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/extension_packaging.py +0 -0
  205. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/grpc_channel_security.py +0 -0
  206. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/grpc_client_retry.py +0 -0
  207. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/grpc_metadata_util.py +0 -0
  208. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/population_response_dictionary_converter.py +0 -0
  209. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/population_session.py +0 -0
  210. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/population_session_factory.py +0 -0
  211. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/session/response_candidate_dictionary_converter.py +0 -0
  212. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/time/__init__.py +0 -0
  213. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/time/time_util.py +0 -0
  214. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/time/timeout.py +0 -0
  215. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/time/timeout_reached_exception.py +0 -0
  216. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/utils/__init__.py +0 -0
  217. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/utils/async_atomic_counter.py +0 -0
  218. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common/utils/atomic_counter.py +0 -0
  219. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common.egg-info/dependency_links.txt +0 -0
  220. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common.egg-info/requires.txt +0 -0
  221. {leaf_common-1.2.41 → leaf_common-1.2.43}/leaf_common.egg-info/top_level.txt +0 -0
  222. {leaf_common-1.2.41 → leaf_common-1.2.43}/pyproject.toml +0 -0
  223. {leaf_common-1.2.41 → leaf_common-1.2.43}/requirements-build.txt +0 -0
  224. {leaf_common-1.2.41 → leaf_common-1.2.43}/requirements.txt +0 -0
  225. {leaf_common-1.2.41 → leaf_common-1.2.43}/setup.cfg +0 -0
  226. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/__init__.py +0 -0
  227. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/__init__.py +0 -0
  228. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/async_test_helpers.py +0 -0
  229. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/asyncio_executor_metrics_test.py +0 -0
  230. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/asyncio_executor_not_started_test.py +0 -0
  231. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/asyncio_executor_pool_metrics_test.py +0 -0
  232. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/asyncio_executor_started_test.py +0 -0
  233. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/asyncio_threadpool_executor_metrics_test.py +0 -0
  234. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/callable_class.py +0 -0
  235. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/asyncio/sync_test_helpers.py +0 -0
  236. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/candidates/__init__.py +0 -0
  237. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/candidates/test_representation_types.py +0 -0
  238. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/config/__init__.py +0 -0
  239. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/config/dictionary_overlay_test.py +0 -0
  240. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/config/resolver_util_test.py +0 -0
  241. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/filters/__init__.py +0 -0
  242. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/filters/test_sanitizer_util.py +0 -0
  243. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/filters/test_tensorflow_field_name_filter.py +0 -0
  244. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/fitness/__init__.py +0 -0
  245. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/fitness/fitness_objectives_from_config_test.py +0 -0
  246. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/fixtures/saved_rule_set.rules +0 -0
  247. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/fixtures/saved_rules_model.json +0 -0
  248. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/parsers/__init__.py +0 -0
  249. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/parsers/canonical_multi_config_parser_test.py +0 -0
  250. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/progress/__init__.py +0 -0
  251. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/progress/test_status_dict_progress_reporter.py +0 -0
  252. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/representation/__init__.py +0 -0
  253. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/representation/rule_based/__init__.py +0 -0
  254. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/representation/rule_based/test_condition.py +0 -0
  255. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/representation/rule_based/test_rule.py +0 -0
  256. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/representation/rule_based/test_rule_set.py +0 -0
  257. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/__init__.py +0 -0
  258. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/encoding/__init__.py +0 -0
  259. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/encoding/decode_text_test.py +0 -0
  260. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/encoding/text_file_reader_test.py +0 -0
  261. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/prep/__init__.py +0 -0
  262. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/serialization/prep/redactor_dictionary_converter_test.py +0 -0
  263. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/session/__init__.py +0 -0
  264. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/session/test_extension_packaging.py +0 -0
  265. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/time/__init__.py +0 -0
  266. {leaf_common-1.2.41 → leaf_common-1.2.43}/tests/time/timeout_test.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: leaf-common
3
- Version: 1.2.41
3
+ Version: 1.2.43
4
4
  Summary: LEAF team common code library
5
5
  Author-email: Dan Fink <Daniel.Fink@cognizant.com>
6
6
  License-Expression: Apache-2.0
@@ -36,6 +36,7 @@ from asyncio import Future
36
36
  from asyncio import Task
37
37
  from concurrent import futures
38
38
 
39
+ from leaf_common.asyncio.event_loop_factory import EventLoopFactory
39
40
  from leaf_common.asyncio.task_executor import TaskExecutor
40
41
  from leaf_common.asyncio.asyncio_threadpool_executor import AsyncioThreadPoolExecutor
41
42
 
@@ -60,7 +61,7 @@ class AsyncioExecutor(TaskExecutor):
60
61
  # We are going to start new thread for this Executor,
61
62
  # so we need a new event loop bound to this particular thread:
62
63
  self._threadpool_executor: AsyncioThreadPoolExecutor = AsyncioThreadPoolExecutor()
63
- self._loop: AbstractEventLoop = asyncio.new_event_loop()
64
+ self._loop: AbstractEventLoop = EventLoopFactory.new_event_loop()
64
65
  self._loop.set_exception_handler(AsyncioExecutor.loop_exception_handler)
65
66
  self._loop.set_default_executor(self._threadpool_executor)
66
67
  self._loop_ready = threading.Event()
@@ -0,0 +1,313 @@
1
+
2
+ # Copyright © 2019-2026 Cognizant Technology Solutions Corp, www.cognizant.com.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ # END COPYRIGHT
17
+ """
18
+ See class comments
19
+ """
20
+ from collections.abc import Sequence
21
+ from typing import Any
22
+ from typing import Dict
23
+ from typing import List
24
+ from typing import Optional
25
+
26
+ import copy
27
+ import logging
28
+ import threading
29
+ import time
30
+
31
+ from leaf_common.asyncio.asyncio_executor import AsyncioExecutor
32
+
33
+
34
+ class AsyncioExecutorPool:
35
+ # pylint: disable=too-many-instance-attributes
36
+ """
37
+ Class maintaining a dynamic set of reusable AsyncioExecutor instances.
38
+
39
+ Garbage collection policy
40
+ -------------------------
41
+ When reuse_mode is True, executors are returned to pool_available rather
42
+ than shut down. Each time an executor is returned, its return timestamp
43
+ is recorded.
44
+
45
+ Garbage collection is performed by a dedicated daemon GC thread that
46
+ runs on its own schedule, independent of any get_executor() /
47
+ return_executor() activity. Every gc_sweep_interval_seconds the thread
48
+ wakes, identifies any executor whose idle period exceeds
49
+ idle_timeout_seconds, removes it from pool_available, and shuts it
50
+ down. This keeps the caller of get_executor / return_executor off both
51
+ the sweep and the shutdown latency paths entirely.
52
+
53
+ Both the idle threshold and the sweep interval are internally
54
+ configurable via constructor parameters, with defaults given by
55
+ DEFAULT_IDLE_TIMEOUT_SECONDS and DEFAULT_GC_SWEEP_INTERVAL_SECONDS.
56
+
57
+ Lifecycle: callers should invoke shutdown() to stop the GC thread when
58
+ the pool is no longer needed. The GC thread holds a strong reference to
59
+ the pool, so omitting shutdown() will keep the pool (and its state) alive
60
+ for the lifetime of the process. The GC thread is a daemon so it will not
61
+ block process exit, but explicit shutdown is cleaner for tests and managed
62
+ long-running services.
63
+ """
64
+
65
+ # Internal configuration: default idle time (seconds) after which an
66
+ # executor sitting in pool_available is shut down and removed.
67
+ DEFAULT_IDLE_TIMEOUT_SECONDS: float = 3 * 60.0
68
+
69
+ # Internal configuration: default interval (seconds) at which the GC
70
+ # thread wakes to look for stale executors.
71
+ DEFAULT_GC_SWEEP_INTERVAL_SECONDS: float = 30.0
72
+
73
+ def __init__(self, reuse_mode: bool = True, *,
74
+ idle_timeout_seconds: float = DEFAULT_IDLE_TIMEOUT_SECONDS,
75
+ gc_sweep_interval_seconds: float = DEFAULT_GC_SWEEP_INTERVAL_SECONDS):
76
+ """
77
+ Constructor.
78
+ :param reuse_mode: True, if requested executor instances
79
+ are taken from pool of available ones (pool mode);
80
+ False, if requested executor instances are created new
81
+ and shutdown on return (backward compatible mode)
82
+ Note that 2 parameters below are keyword-only, to make it more explicit when configuring the GC policy.
83
+ :param idle_timeout_seconds: Idle time threshold in seconds. An
84
+ AsyncioExecutor sitting in pool_available
85
+ longer than this is shut down and removed
86
+ by the background GC thread. Only applies
87
+ when reuse_mode is True.
88
+ :param gc_sweep_interval_seconds: Interval at which the GC thread
89
+ checks pool_available for stale executors.
90
+ Stale executors linger at most one sweep
91
+ interval beyond the idle timeout before
92
+ being collected. Only applies when
93
+ reuse_mode is True.
94
+ """
95
+ self.reuse_mode: bool = reuse_mode
96
+ self.idle_timeout_seconds: float = idle_timeout_seconds
97
+ self.gc_sweep_interval_seconds: float = gc_sweep_interval_seconds
98
+ if self.reuse_mode:
99
+ if self.gc_sweep_interval_seconds <= 0:
100
+ raise ValueError("gc_sweep_interval_seconds must be > 0 when reuse_mode=True")
101
+ if self.idle_timeout_seconds < 0:
102
+ raise ValueError("idle_timeout_seconds must be >= 0 when reuse_mode=True")
103
+ # List of available (not currently used) AsyncioExecutor instances in the pool.
104
+ self.pool_available: List[AsyncioExecutor] = []
105
+
106
+ # List of currently used AsyncioExecutor instances in the pool.
107
+ self.pool_used: List[AsyncioExecutor] = []
108
+
109
+ # Maps id(executor) -> monotonic timestamp when the executor was
110
+ # returned to pool_available. Only contains entries for executors
111
+ # currently in pool_available; pruned on get_executor() and on sweep.
112
+ self._returned_at: Dict[int, float] = {}
113
+
114
+ self.lock = threading.Lock()
115
+ self.logger = logging.getLogger(self.__class__.__name__)
116
+
117
+ # Active GC: a daemon thread runs _gc_loop, sweeping on its own
118
+ # schedule. The stop event lets shutdown() interrupt the sweep
119
+ # interval wait promptly.
120
+ self._gc_stop_event: threading.Event = threading.Event()
121
+ self._gc_thread: Optional[threading.Thread] = None
122
+ if self.reuse_mode:
123
+ self._gc_thread = threading.Thread(
124
+ target=self._gc_loop,
125
+ name=f"AsyncioExecutorPool-GC-{id(self)}",
126
+ daemon=True,
127
+ )
128
+ self._gc_thread.start()
129
+
130
+ self.logger.debug(
131
+ "AsyncioExecutorPool created: %s reuse: %s idle_timeout: %.1fs sweep_interval: %.1fs",
132
+ id(self), str(self.reuse_mode), self.idle_timeout_seconds, self.gc_sweep_interval_seconds)
133
+
134
+ def get_executor(self) -> AsyncioExecutor:
135
+ """
136
+ Get active (running) executor from the pool. Does not sweep;
137
+ sweeping is the GC thread's responsibility. If a stale executor
138
+ happens to sit at the head of pool_available before the next
139
+ sweep, get_executor() will reuse it -- which is fine, since
140
+ "stale" only means "would otherwise be collected as idle".
141
+ :return: AsyncioExecutor instance
142
+ """
143
+ if self.reuse_mode:
144
+ with self.lock:
145
+ if len(self.pool_available) > 0:
146
+ result = self.pool_available.pop(0)
147
+ self._returned_at.pop(id(result), None)
148
+ self.logger.debug("Reusing AsyncioExecutor %s", id(result))
149
+ self.pool_used.append(result)
150
+ return result
151
+ # Create AsyncioExecutor outside of lock
152
+ # to avoid potentially longer locked periods
153
+ result = AsyncioExecutor()
154
+ result.start()
155
+ self.logger.debug("Creating AsyncioExecutor %s", id(result))
156
+ with self.lock:
157
+ self.pool_used.append(result)
158
+ return result
159
+
160
+ def return_executor(self, executor: AsyncioExecutor):
161
+ """
162
+ Return AsyncioExecutor instance back to the pool of available instances.
163
+ Does not sweep; the GC thread handles stale collection on its own
164
+ schedule.
165
+ :param executor: AsyncioExecutor to return.
166
+ """
167
+ with self.lock:
168
+ if executor not in self.pool_used:
169
+ raise ValueError(f"Returned executor {id(executor)} is not in the pool of used executors")
170
+ self.pool_used.remove(executor)
171
+
172
+ # Executor clean up: cancel current tasks and shutdown if not in reuse mode.
173
+ if self.reuse_mode:
174
+ executor.cancel_current_tasks()
175
+ else:
176
+ self.logger.debug("Shutting down: AsyncioExecutor %s", id(executor))
177
+ executor.shutdown()
178
+
179
+ if self.reuse_mode:
180
+ with self.lock:
181
+ self.pool_available.append(executor)
182
+ self._returned_at[id(executor)] = time.monotonic()
183
+ self.logger.debug("Returned to pool: AsyncioExecutor %s pool size: %d",
184
+ id(executor), len(self.pool_available))
185
+
186
+ def shutdown(self, wait: bool = True) -> None:
187
+ """
188
+ Stop the background GC thread. After this returns, the pool no
189
+ longer reaps idle executors. Idempotent. Executors still held in
190
+ pool_used or pool_available are NOT shut down here; callers that
191
+ want those gone should handle them explicitly.
192
+ """
193
+ with self.lock:
194
+ gc_thread = self._gc_thread
195
+ if gc_thread is None:
196
+ return
197
+ # Clear first to make concurrent shutdown() calls safe.
198
+ self._gc_thread = None
199
+
200
+ self._gc_stop_event.set()
201
+ if wait and threading.current_thread() is not gc_thread:
202
+ gc_thread.join()
203
+
204
+ def _gc_loop(self) -> None:
205
+ """
206
+ Active GC loop: sweep, then wait for either the configured
207
+ interval or the stop event, whichever comes first. Exits cleanly
208
+ when shutdown() sets the stop event.
209
+
210
+ Exceptions from _sweep_once are logged but do not stop the loop,
211
+ so a single bad executor cannot leak the rest of the pool.
212
+ """
213
+ while not self._gc_stop_event.is_set():
214
+ try:
215
+ self._sweep_once()
216
+ except Exception as exc: # pylint: disable=broad-exception-caught
217
+ self.logger.warning("GC: sweep raised %s; continuing", exc, exc_info=True)
218
+ # Sleep until the next sweep tick or until shutdown is signaled.
219
+ self._gc_stop_event.wait(timeout=self.gc_sweep_interval_seconds)
220
+
221
+ def _collect_executors(self, executors: Sequence[AsyncioExecutor]) -> None:
222
+ for executor in executors:
223
+ try:
224
+ self.logger.debug("GC: shutting down idle AsyncioExecutor %s", id(executor))
225
+ executor.shutdown(wait=True)
226
+ except Exception as exc: # pylint: disable=broad-exception-caught
227
+ self.logger.warning(
228
+ "GC: shutdown failed for AsyncioExecutor %s: %s", id(executor), exc, exc_info=True)
229
+
230
+ def _sweep_once(self, now: Optional[float] = None) -> None:
231
+ """
232
+ Identify any executors in pool_available whose idle time exceeds
233
+ idle_timeout_seconds, remove them from the pool, and shut them
234
+ down. No-op when not in reuse mode.
235
+
236
+ pool_available is FIFO-ordered by return time (oldest at the head),
237
+ so the scan stops at the first non-stale entry.
238
+
239
+ Run by the GC thread on its periodic schedule; tests may call it
240
+ directly with a synthetic `now` for deterministic verification.
241
+
242
+ :param now: Optional override for the current time, in the same
243
+ units as time.monotonic(). Intended for tests; production
244
+ callers should omit this and let it default to
245
+ time.monotonic().
246
+ """
247
+ if not self.reuse_mode:
248
+ return
249
+ if now is None:
250
+ now = time.monotonic()
251
+ to_collect: List[AsyncioExecutor] = []
252
+ with self.lock:
253
+ keep_from: int = 0
254
+ prev_returned_at: float = 0.0
255
+ for executor in self.pool_available:
256
+ if id(executor) not in self._returned_at:
257
+ # This should never happen, but log this and record executor
258
+ # as returned at the same time as the previous one - to keep sequence invariant still valid.
259
+ self.logger.warning(
260
+ "GC: executor %s in pool_available but missing return timestamp; fixing",
261
+ id(executor))
262
+ self._returned_at[id(executor)] = prev_returned_at
263
+ returned_at: float = self._returned_at.get(id(executor), prev_returned_at)
264
+ if now - returned_at > self.idle_timeout_seconds:
265
+ to_collect.append(executor)
266
+ keep_from += 1
267
+ prev_returned_at = returned_at
268
+ else:
269
+ break
270
+ if keep_from > 0:
271
+ for executor in self.pool_available[:keep_from]:
272
+ self._returned_at.pop(id(executor), None)
273
+ del self.pool_available[:keep_from]
274
+ # Shutdown outside the lock to keep the critical section short.
275
+ self._collect_executors(to_collect)
276
+
277
+ def get_threads_metrics(self) -> Dict[str, Any]:
278
+ """
279
+ Get metrics related to threads in the pool of executors:
280
+ for both collections of used and available executors,
281
+ get the total number of executors in this collection "executors",
282
+ the total number of work threads across all executors in this collection "work_threads",
283
+ and the total number of currently running work threads
284
+ across all executors in this collection "threads_running".
285
+ """
286
+ with self.lock:
287
+ available_copy = copy.copy(self.pool_available)
288
+ used_copy = copy.copy(self.pool_used)
289
+ used_threads: int = 0
290
+ used_running: int = 0
291
+ available_threads: int = 0
292
+ available_running: int = 0
293
+ for executor in used_copy:
294
+ threads, running = executor.get_threads_metrics()
295
+ used_threads += threads
296
+ used_running += running
297
+ for executor in available_copy:
298
+ threads, running = executor.get_threads_metrics()
299
+ available_threads += threads
300
+ available_running += running
301
+ result_dict = {
302
+ "used": {
303
+ "executors": len(used_copy),
304
+ "work_threads": used_threads,
305
+ "threads_running": used_running
306
+ },
307
+ "available": {
308
+ "executors": len(available_copy),
309
+ "work_threads": available_threads,
310
+ "threads_running": available_running
311
+ }
312
+ }
313
+ return result_dict
@@ -0,0 +1,78 @@
1
+
2
+ # Copyright © 2019-2026 Cognizant Technology Solutions Corp, www.cognizant.com.
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ #
16
+ # END COPYRIGHT
17
+ """
18
+ See class comment for details.
19
+ """
20
+ from typing import Callable
21
+ from typing import Optional
22
+
23
+ import asyncio
24
+ import sys
25
+
26
+ from asyncio import AbstractEventLoop
27
+
28
+
29
+ class EventLoopFactory:
30
+ """
31
+ Factory for creating asyncio event loops with a platform-appropriate type.
32
+
33
+ On Windows the factory produces SelectorEventLoop instances (note: this loop
34
+ does not support asyncio subprocess APIs); on other platforms it defers to
35
+ asyncio's platform default. This is the forward-compatible replacement for the
36
+ asyncio.set_event_loop_policy() pattern (using asyncio.WindowsSelectorEventLoopPolicy
37
+ on Windows), which is deprecated in Python 3.14 and slated for removal.
38
+
39
+ Two entry points are provided so callers can pick what matches their
40
+ existing call site:
41
+ - loop_factory(): returns a callable suitable for the loop_factory=
42
+ keyword on asyncio.run() (Python 3.12+) and asyncio.Runner()
43
+ (Python 3.11+). Returns None on platforms where asyncio's
44
+ default is appropriate; None is the documented sentinel for
45
+ "use default", so the result can be passed through unchanged
46
+ on any platform.
47
+ - new_event_loop(): drop-in replacement for asyncio.new_event_loop()
48
+ at sites that construct a loop directly. Combine with
49
+ asyncio.set_event_loop() to install the loop for the current
50
+ thread before frameworks (e.g. Tornado) wrap it.
51
+ """
52
+
53
+ @staticmethod
54
+ def loop_factory() -> Optional[Callable[[], AbstractEventLoop]]:
55
+ """
56
+ :return: A callable that constructs a platform-appropriate event
57
+ loop, or None if asyncio's default is appropriate. None is
58
+ the documented value asyncio.run() and asyncio.Runner()
59
+ accept to mean "use the default factory", so the return
60
+ value of this method can be forwarded unchanged on any
61
+ platform.
62
+ """
63
+ if sys.platform == "win32":
64
+ return asyncio.SelectorEventLoop
65
+ return None
66
+
67
+ @staticmethod
68
+ def new_event_loop() -> AbstractEventLoop:
69
+ """
70
+ :return: A newly-constructed event loop appropriate for the current
71
+ platform. On Windows this is a SelectorEventLoop; on other
72
+ platforms it is the loop type returned by
73
+ asyncio.new_event_loop().
74
+ """
75
+ factory: Optional[Callable[[], AbstractEventLoop]] = EventLoopFactory.loop_factory()
76
+ if factory is not None:
77
+ return factory()
78
+ return asyncio.new_event_loop()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: leaf-common
3
- Version: 1.2.41
3
+ Version: 1.2.43
4
4
  Summary: LEAF team common code library
5
5
  Author-email: Dan Fink <Daniel.Fink@cognizant.com>
6
6
  License-Expression: Apache-2.0
@@ -30,6 +30,7 @@ leaf_common/asyncio/async_to_sync_generator.py
30
30
  leaf_common/asyncio/asyncio_executor.py
31
31
  leaf_common/asyncio/asyncio_executor_pool.py
32
32
  leaf_common/asyncio/asyncio_threadpool_executor.py
33
+ leaf_common/asyncio/event_loop_factory.py
33
34
  leaf_common/asyncio/task_executor.py
34
35
  leaf_common/candidates/__init__.py
35
36
  leaf_common/candidates/constants.py
@@ -222,10 +223,12 @@ tests/asyncio/__init__.py
222
223
  tests/asyncio/async_test_helpers.py
223
224
  tests/asyncio/asyncio_executor_metrics_test.py
224
225
  tests/asyncio/asyncio_executor_not_started_test.py
226
+ tests/asyncio/asyncio_executor_pool_gc_test.py
225
227
  tests/asyncio/asyncio_executor_pool_metrics_test.py
226
228
  tests/asyncio/asyncio_executor_started_test.py
227
229
  tests/asyncio/asyncio_threadpool_executor_metrics_test.py
228
230
  tests/asyncio/callable_class.py
231
+ tests/asyncio/event_loop_factory_test.py
229
232
  tests/asyncio/sync_test_helpers.py
230
233
  tests/candidates/__init__.py
231
234
  tests/candidates/test_representation_types.py