leaf-common 1.2.37__tar.gz → 1.2.38__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 (257) hide show
  1. leaf_common-1.2.38/.github/workflows/tests.yml +29 -0
  2. {leaf_common-1.2.37/leaf_common.egg-info → leaf_common-1.2.38}/PKG-INFO +1 -1
  3. {leaf_common-1.2.37 → leaf_common-1.2.38}/build_scripts/run_flake8.sh +6 -6
  4. {leaf_common-1.2.37 → leaf_common-1.2.38}/build_scripts/run_pylint.sh +3 -3
  5. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/resolver.py +32 -10
  6. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/resolver_util.py +15 -5
  7. {leaf_common-1.2.37 → leaf_common-1.2.38/leaf_common.egg-info}/PKG-INFO +1 -1
  8. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common.egg-info/SOURCES.txt +1 -0
  9. leaf_common-1.2.38/tests/config/resolver_util_test.py +243 -0
  10. leaf_common-1.2.37/.github/workflows/tests.yml +0 -67
  11. {leaf_common-1.2.37 → leaf_common-1.2.38}/.coveragerc +0 -0
  12. {leaf_common-1.2.37 → leaf_common-1.2.38}/.flake8 +0 -0
  13. {leaf_common-1.2.37 → leaf_common-1.2.38}/.github/workflows/codeql.yml +0 -0
  14. {leaf_common-1.2.37 → leaf_common-1.2.38}/.github/workflows/publish.yml +0 -0
  15. {leaf_common-1.2.37 → leaf_common-1.2.38}/.gitignore +0 -0
  16. {leaf_common-1.2.37 → leaf_common-1.2.38}/.pylintrc +0 -0
  17. {leaf_common-1.2.37 → leaf_common-1.2.38}/CODE_OF_CONDUCT.md +0 -0
  18. {leaf_common-1.2.37 → leaf_common-1.2.38}/CONTRIBUTING.md +0 -0
  19. {leaf_common-1.2.37 → leaf_common-1.2.38}/LICENSE.txt +0 -0
  20. {leaf_common-1.2.37 → leaf_common-1.2.38}/NOTICE.txt +0 -0
  21. {leaf_common-1.2.37 → leaf_common-1.2.38}/README.md +0 -0
  22. {leaf_common-1.2.37 → leaf_common-1.2.38}/SECURITY.md +0 -0
  23. {leaf_common-1.2.37 → leaf_common-1.2.38}/SUPPORT.md +0 -0
  24. {leaf_common-1.2.37 → leaf_common-1.2.38}/build_scripts/copyright_config.json +0 -0
  25. {leaf_common-1.2.37 → leaf_common-1.2.38}/build_scripts/source_available_copyright.txt +0 -0
  26. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/__init__.py +0 -0
  27. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/asyncio/__init__.py +0 -0
  28. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/asyncio/async_to_sync_generator.py +0 -0
  29. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/asyncio/asyncio_executor.py +0 -0
  30. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/asyncio/asyncio_executor_pool.py +0 -0
  31. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/asyncio/task_executor.py +0 -0
  32. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/candidates/__init__.py +0 -0
  33. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/candidates/constants.py +0 -0
  34. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/candidates/representation_types.py +0 -0
  35. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/__init__.py +0 -0
  36. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/config_filter.py +0 -0
  37. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/config_filter_chain.py +0 -0
  38. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/config_handler.py +0 -0
  39. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/dictionary_overlay.py +0 -0
  40. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/environment_defaults.py +0 -0
  41. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/config/file_of_class.py +0 -0
  42. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/evaluation/__init__.py +0 -0
  43. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/evaluation/component_evaluator.py +0 -0
  44. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/evaluation/metrics_merger.py +0 -0
  45. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/__init__.py +0 -0
  46. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/composite_string_filter.py +0 -0
  47. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/inclusionary_replacement_string_filter.py +0 -0
  48. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/replacement_string_filter.py +0 -0
  49. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/sanitizer_util.py +0 -0
  50. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/string_filter.py +0 -0
  51. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/strip_whitespace_string_filter.py +0 -0
  52. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/filters/tensorflow_field_name_filter.py +0 -0
  53. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/__init__.py +0 -0
  54. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/candidate_metrics_provider.py +0 -0
  55. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/cmp_to_key.py +0 -0
  56. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/comparator.py +0 -0
  57. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/fitness_objective.py +0 -0
  58. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/fitness_objectives.py +0 -0
  59. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/fitness_objectives_builder.py +0 -0
  60. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/fitness_objectives_from_config.py +0 -0
  61. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/metrics_based_individual_comparator.py +0 -0
  62. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/metrics_provider.py +0 -0
  63. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/none_comparator.py +0 -0
  64. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/number_comparator.py +0 -0
  65. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/fitness/reversed_comparator.py +0 -0
  66. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/log_utils/__init__.py +0 -0
  67. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/log_utils/message_type.py +0 -0
  68. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/log_utils/structured_message.py +0 -0
  69. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/logging/__init__.py +0 -0
  70. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/logging/example_logging_config.hocon +0 -0
  71. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/logging/logging_setup.py +0 -0
  72. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/logging/stream_to_logger.py +0 -0
  73. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/__init__.py +0 -0
  74. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/boolean_list_parser.py +0 -0
  75. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/boolean_parser.py +0 -0
  76. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/canonical_multi_config_parser.py +0 -0
  77. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/dictionary_extractor.py +0 -0
  78. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/field_extractor.py +0 -0
  79. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/list_parser.py +0 -0
  80. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/parser.py +0 -0
  81. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/parsers/string_parser.py +0 -0
  82. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/__init__.py +0 -0
  83. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/__init__.py +0 -0
  84. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/abstract_easy_persistence.py +0 -0
  85. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/composite_persistor.py +0 -0
  86. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/easy_hocon_persistence.py +0 -0
  87. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/easy_json_persistence.py +0 -0
  88. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/easy_txt_persistence.py +0 -0
  89. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/easy/easy_yaml_persistence.py +0 -0
  90. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/README.md +0 -0
  91. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/__init__.py +0 -0
  92. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/abstract_persistence.py +0 -0
  93. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/hocon_persistence.py +0 -0
  94. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/json_gzip_persistence.py +0 -0
  95. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/json_persistence.py +0 -0
  96. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/null_persistence.py +0 -0
  97. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/override_file_extension_provider.py +0 -0
  98. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/persistence_factory.py +0 -0
  99. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/raw_bytes_persistence.py +0 -0
  100. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/reference_helper.py +0 -0
  101. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/simple_file_persistence.py +0 -0
  102. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/text_persistence.py +0 -0
  103. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/factory/yaml_persistence.py +0 -0
  104. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/interface/__init__.py +0 -0
  105. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/interface/persistence.py +0 -0
  106. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/interface/persistence_mechanism.py +0 -0
  107. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/interface/persistor.py +0 -0
  108. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/interface/restorer.py +0 -0
  109. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/__init__.py +0 -0
  110. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/abstract_persistence_mechanism.py +0 -0
  111. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/local_file_persistence_mechanism.py +0 -0
  112. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/persistence_mechanism_factory.py +0 -0
  113. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/persistence_mechanisms.py +0 -0
  114. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/persistence/mechanism/s3_file_persistence_mechanism.py +0 -0
  115. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/__init__.py +0 -0
  116. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/composite_progress_reporter.py +0 -0
  117. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/iterating_progress_reporter.py +0 -0
  118. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/json_progress_reporter.py +0 -0
  119. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/progress_reporter.py +0 -0
  120. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/status_dict_progress_reporter.py +0 -0
  121. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/progress/worker_progress_reporter.py +0 -0
  122. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/__init__.py +0 -0
  123. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/registry/__init__.py +0 -0
  124. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/registry/representation_file_extension_provider_registry.py +0 -0
  125. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/registry/representation_persistence_registry.py +0 -0
  126. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/registry/representation_serialization_format_registry.py +0 -0
  127. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/registry/self_identifying_restorer.py +0 -0
  128. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/__init__.py +0 -0
  129. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/config/__init__.py +0 -0
  130. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/config/rule_set_config_helper.py +0 -0
  131. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/__init__.py +0 -0
  132. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/condition.py +0 -0
  133. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/features.py +0 -0
  134. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/rule.py +0 -0
  135. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/rule_set.py +0 -0
  136. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/rule_set_binding.py +0 -0
  137. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/data/rules_constants.py +0 -0
  138. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/__init__.py +0 -0
  139. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/condition_evaluator.py +0 -0
  140. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/rule_evaluator.py +0 -0
  141. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/rule_metrics_merger.py +0 -0
  142. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/rule_set_binding_evaluator.py +0 -0
  143. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/rule_set_evaluator.py +0 -0
  144. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/evaluation/rule_set_metrics_merger.py +0 -0
  145. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/persistence/__init__.py +0 -0
  146. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/persistence/rule_set_binding_file_persistence.py +0 -0
  147. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/persistence/rule_set_file_persistence.py +0 -0
  148. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/__init__.py +0 -0
  149. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/condition_dictionary_converter.py +0 -0
  150. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/rule_dictionary_converter.py +0 -0
  151. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/rule_set_binding_dictionary_converter.py +0 -0
  152. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/rule_set_dictionary_converter.py +0 -0
  153. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/representation/rule_based/serialization/rule_set_serialization_format.py +0 -0
  154. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/__init__.py +0 -0
  155. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/__init__.py +0 -0
  156. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/auth0_direct_service_accessor.py +0 -0
  157. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/leaf_service_access.py +0 -0
  158. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/service_accessor.py +0 -0
  159. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/service_accessor_factory.py +0 -0
  160. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/static_token_service_accessor.py +0 -0
  161. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/service/vault_dynamic_token_service_accessor.py +0 -0
  162. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/__init__.py +0 -0
  163. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/github_vault_login.py +0 -0
  164. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/kubernetes_vault_login.py +0 -0
  165. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/token_vault_login.py +0 -0
  166. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/vault_login.py +0 -0
  167. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/security/vault/vault_login_factory.py +0 -0
  168. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/__init__.py +0 -0
  169. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/__init__.py +0 -0
  170. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/buffered_gzip_serialization_format.py +0 -0
  171. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/chained_serialization_format.py +0 -0
  172. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/conversion_policy.py +0 -0
  173. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/gzip_serialization_format.py +0 -0
  174. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/hocon_serialization_format.py +0 -0
  175. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/json_serialization_format.py +0 -0
  176. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/raw_bytes_serialization_format.py +0 -0
  177. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/serialization_formats.py +0 -0
  178. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/text_serialization_format.py +0 -0
  179. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/format/yaml_serialization_format.py +0 -0
  180. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/__init__.py +0 -0
  181. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/deserializer.py +0 -0
  182. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/dictionary_converter.py +0 -0
  183. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/file_extension_provider.py +0 -0
  184. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/reference_pruner.py +0 -0
  185. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/self_identifying_representation_error.py +0 -0
  186. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/serialization_format.py +0 -0
  187. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/interface/serializer.py +0 -0
  188. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/__init__.py +0 -0
  189. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/empty_dictionary_converter.py +0 -0
  190. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/metrics_dictionary_converter.py +0 -0
  191. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/pass_through_dictionary_converter.py +0 -0
  192. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/pass_through_reference_pruner.py +0 -0
  193. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/prep/redactor_dictionary_converter.py +0 -0
  194. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/util/__init__.py +0 -0
  195. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/serialization/util/bytes_decoder.py +0 -0
  196. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/__init__.py +0 -0
  197. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/abstract_service_session.py +0 -0
  198. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/async_abstract_service_session.py +0 -0
  199. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/async_grpc_client_retry.py +0 -0
  200. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/extension_packaging.py +0 -0
  201. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/grpc_channel_security.py +0 -0
  202. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/grpc_client_retry.py +0 -0
  203. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/grpc_metadata_util.py +0 -0
  204. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/population_response_dictionary_converter.py +0 -0
  205. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/population_session.py +0 -0
  206. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/population_session_factory.py +0 -0
  207. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/session/response_candidate_dictionary_converter.py +0 -0
  208. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/time/__init__.py +0 -0
  209. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/time/time_util.py +0 -0
  210. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/time/timeout.py +0 -0
  211. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/time/timeout_reached_exception.py +0 -0
  212. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/utils/__init__.py +0 -0
  213. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/utils/async_atomic_counter.py +0 -0
  214. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common/utils/atomic_counter.py +0 -0
  215. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common.egg-info/dependency_links.txt +0 -0
  216. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common.egg-info/requires.txt +0 -0
  217. {leaf_common-1.2.37 → leaf_common-1.2.38}/leaf_common.egg-info/top_level.txt +0 -0
  218. {leaf_common-1.2.37 → leaf_common-1.2.38}/pyproject.toml +0 -0
  219. {leaf_common-1.2.37 → leaf_common-1.2.38}/requirements-build.txt +0 -0
  220. {leaf_common-1.2.37 → leaf_common-1.2.38}/requirements.txt +0 -0
  221. {leaf_common-1.2.37 → leaf_common-1.2.38}/setup.cfg +0 -0
  222. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/__init__.py +0 -0
  223. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/__init__.py +0 -0
  224. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/async_test_helpers.py +0 -0
  225. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/asyncio_executor_not_started_test.py +0 -0
  226. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/asyncio_executor_started_test.py +0 -0
  227. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/callable_class.py +0 -0
  228. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/asyncio/sync_test_helpers.py +0 -0
  229. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/candidates/__init__.py +0 -0
  230. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/candidates/test_representation_types.py +0 -0
  231. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/config/__init__.py +0 -0
  232. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/config/dictionary_overlay_test.py +0 -0
  233. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/filters/__init__.py +0 -0
  234. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/filters/test_sanitizer_util.py +0 -0
  235. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/filters/test_tensorflow_field_name_filter.py +0 -0
  236. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/fitness/__init__.py +0 -0
  237. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/fitness/fitness_objectives_from_config_test.py +0 -0
  238. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/fixtures/saved_rule_set.rules +0 -0
  239. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/fixtures/saved_rules_model.json +0 -0
  240. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/parsers/__init__.py +0 -0
  241. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/parsers/canonical_multi_config_parser_test.py +0 -0
  242. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/progress/__init__.py +0 -0
  243. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/progress/test_status_dict_progress_reporter.py +0 -0
  244. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/representation/__init__.py +0 -0
  245. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/representation/rule_based/__init__.py +0 -0
  246. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/representation/rule_based/test_condition.py +0 -0
  247. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/representation/rule_based/test_rule.py +0 -0
  248. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/representation/rule_based/test_rule_set.py +0 -0
  249. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/serialization/__init__.py +0 -0
  250. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/serialization/encoding/__init__.py +0 -0
  251. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/serialization/encoding/decode_text_test.py +0 -0
  252. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/serialization/prep/__init__.py +0 -0
  253. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/serialization/prep/redactor_dictionary_converter_test.py +0 -0
  254. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/session/__init__.py +0 -0
  255. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/session/test_extension_packaging.py +0 -0
  256. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/time/__init__.py +0 -0
  257. {leaf_common-1.2.37 → leaf_common-1.2.38}/tests/time/timeout_test.py +0 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: Test
3
+
4
+ "on":
5
+ push:
6
+ branches:
7
+ - main
8
+ pull_request:
9
+
10
+ permissions:
11
+ contents: read
12
+
13
+ jobs:
14
+ test:
15
+ # 455db3c = build-common 1.0.0
16
+ # https://github.com/cognizant-ai-lab/build-common/releases
17
+ uses: cognizant-ai-lab/build-common/.github/workflows/_python-quality-gate.yml@455db3cc40e9ed6a0fb9bbaf8188888c1ae315c2
18
+ with:
19
+ lint-toolchain: "legacy"
20
+ python-version: "3.12"
21
+ pylint-command: "build_scripts/run_pylint.sh"
22
+ run-shellcheck: true
23
+ pytest-markers: "not integration"
24
+ pytest-extra-args: "--timer-top-n 10 --cov-config=.coveragerc --cov"
25
+ check-readme-pypi: true
26
+ secrets:
27
+ SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
28
+
29
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: leaf-common
3
- Version: 1.2.37
3
+ Version: 1.2.38
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
@@ -2,11 +2,11 @@
2
2
 
3
3
  # set -euo pipefail
4
4
 
5
- UP_TO_SNUFF_DIRS=`ls -1`
5
+ UP_TO_SNUFF_DIRS=$(ls -1)
6
6
  IGNORE="build_scripts"
7
7
 
8
8
  dirs=$1
9
- if [ "x${dirs}" == "x" ]
9
+ if [ "${dirs}" == "" ]
10
10
  then
11
11
  dirs=${UP_TO_SNUFF_DIRS}
12
12
  fi
@@ -26,18 +26,18 @@ do
26
26
  # See if it's a directory we specifically want to ignore
27
27
  for ignore_dir in ${IGNORE}
28
28
  do
29
- if [ "x${ignore_dir}" == $"x${dir}" ]
29
+ if [ "${ignore_dir}" == "${dir}" ]
30
30
  then
31
31
  ignore_this=${dir}
32
32
  fi
33
33
  done
34
34
 
35
35
  # If we do not need to ignore...
36
- if [ "x" == "x${ignore_this}" ]
36
+ if [ "" == "${ignore_this}" ]
37
37
  then
38
38
  echo "Running flake8 on directory '${dir}':"
39
- find "${dir}" -iname "*.py" | \
40
- xargs flake8 -j 0
39
+ find "${dir}" -iname "*.py" -print0 | \
40
+ xargs --null flake8 --jobs 0
41
41
  current_retval=$?
42
42
  if [ ${current_retval} -ne 0 ]
43
43
  then
@@ -8,7 +8,7 @@ DEPENDENCY_DIRS=""
8
8
  IGNORE="${DEPENDENCY_DIRS} build_scripts"
9
9
 
10
10
  dirs=$1
11
- if [ "x${dirs}" == "x" ]
11
+ if [ "${dirs}" == "" ]
12
12
  then
13
13
  dirs=${UP_TO_SNUFF_DIRS}
14
14
  fi
@@ -30,14 +30,14 @@ do
30
30
  # See if it's a directory we specifically want to ignore
31
31
  for ignore_dir in ${IGNORE}
32
32
  do
33
- if [ "x${ignore_dir}" == $"x${dir}" ]
33
+ if [ "${ignore_dir}" == "${dir}" ]
34
34
  then
35
35
  ignore_this=${dir}
36
36
  fi
37
37
  done
38
38
 
39
39
  # If we do not need to ignore...
40
- if [ "x" == "x${ignore_this}" ]
40
+ if [ "" == "${ignore_this}" ]
41
41
  then
42
42
  use_dirs="${use_dirs} ${dir}"
43
43
  fi
@@ -75,16 +75,7 @@ class Resolver():
75
75
  if verbose:
76
76
  logger.info("Attempting to resolve module %s", use_module_name)
77
77
 
78
- if self.packages is not None:
79
- for package in self.packages:
80
- fully_qualified_module: str = f"{package}.{use_module_name}"
81
- found_module: Any = self.try_to_import_module(fully_qualified_module,
82
- messages, install_if_missing)
83
- if found_module is not None:
84
- break
85
- else:
86
- found_module: Any = self.try_to_import_module(use_module_name, messages,
87
- install_if_missing)
78
+ found_module: Any = self._find_module(use_module_name, messages, install_if_missing)
88
79
 
89
80
  if found_module is None:
90
81
  message: str = f"Could not find code for {use_module_name}"
@@ -108,6 +99,37 @@ class Resolver():
108
99
 
109
100
  return my_class
110
101
 
102
+ def _find_module(self, use_module_name: str, messages: List[str],
103
+ install_if_missing: str = None) -> Any:
104
+ """
105
+ Searches for a module by name, trying each configured package in turn.
106
+ Also tries the module name as a top-level module if packages are configured.
107
+
108
+ :param use_module_name: The module name to search for
109
+ :param messages: a list of messages where logs of failed attempts can go
110
+ :param install_if_missing: Optional name of a package to install if the module is missing.
111
+ :return: The python module if found. None if not found.
112
+ """
113
+
114
+ if self.packages is None:
115
+ return self.try_to_import_module(use_module_name, messages, install_if_missing)
116
+
117
+ for package in self.packages:
118
+ fully_qualified_module: str = f"{package}.{use_module_name}"
119
+ found_module: Any = self.try_to_import_module(fully_qualified_module,
120
+ messages, install_if_missing)
121
+
122
+ if found_module is not None:
123
+ return found_module
124
+
125
+ # check main package
126
+ check_main_package: Any = self.try_to_import_module(package,
127
+ messages, install_if_missing)
128
+ if check_main_package is not None:
129
+ return check_main_package
130
+
131
+ return None
132
+
111
133
  def try_to_import_module(self, module: str, messages: List[str],
112
134
  install_if_missing: str = None) -> Any:
113
135
  """
@@ -80,20 +80,30 @@ class ResolverUtil:
80
80
  if class_name is None or len(class_name) == 0:
81
81
  return None
82
82
 
83
+ value_error_message: str = (f"Value from {class_name_source} '{class_name}' "
84
+ "must be of the form <package_name>.<module_name>.<ClassName> "
85
+ "or <package_name>.<ClassName> ")
83
86
  # Split the single string into package, module, and class name
84
87
  class_split: List[str] = class_name.split(".")
85
- if len(class_split) <= 2:
86
- raise ValueError(f"Value from {class_name_source} '{class_name}' "
87
- "must be of the form <package_name>.<module_name>.<ClassName>")
88
+ if len(class_split) < 2:
89
+ raise ValueError(value_error_message)
88
90
 
89
- packages: List[str] = [".".join(class_split[:-2])]
91
+ # Extract module and class details
92
+ if len(class_split) <= 2:
93
+ # handles <package_name>.<ClassName>
94
+ packages: List[str] = [class_split[0]]
95
+ module_name = class_split[0]
96
+ else:
97
+ # handles <package_name>.<module_name>.<ClassName>
98
+ packages: List[str] = [".".join(class_split[:-2])]
99
+ module_name = class_split[-2]
90
100
  class_name: str = class_split[-1]
91
101
  resolver = Resolver(packages)
92
102
 
93
103
  # Resolve the class name
94
104
  class_reference: Type[Any] = None
95
105
  try:
96
- class_reference = resolver.resolve_class_in_module(class_name, module_name=class_split[-2])
106
+ class_reference = resolver.resolve_class_in_module(class_name, module_name=module_name)
97
107
  except AttributeError as exception:
98
108
  raise ValueError(f"Class '{class_name}' from {class_name_source} "
99
109
  "not found in PYTHONPATH") from exception
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: leaf-common
3
- Version: 1.2.37
3
+ Version: 1.2.38
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
@@ -226,6 +226,7 @@ tests/candidates/__init__.py
226
226
  tests/candidates/test_representation_types.py
227
227
  tests/config/__init__.py
228
228
  tests/config/dictionary_overlay_test.py
229
+ tests/config/resolver_util_test.py
229
230
  tests/filters/__init__.py
230
231
  tests/filters/test_sanitizer_util.py
231
232
  tests/filters/test_tensorflow_field_name_filter.py
@@ -0,0 +1,243 @@
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
+
21
+ from unittest import TestCase
22
+ from unittest.mock import MagicMock
23
+ from unittest.mock import patch
24
+
25
+
26
+ from leaf_common.config.resolver_util import ResolverUtil
27
+
28
+ RESOLVER_PATH = "leaf_common.config.resolver.Resolver.resolve_class_in_module"
29
+
30
+
31
+ class ResolverUtilTest(TestCase):
32
+ """
33
+ Tests for ResolverUtil.
34
+ """
35
+
36
+ # -------------------------------------------------------------------------
37
+ # create_class tests
38
+ # -------------------------------------------------------------------------
39
+
40
+ def test_create_class_none_returns_none(self):
41
+ """
42
+ None class_name should return None without raising.
43
+ """
44
+ result = ResolverUtil.create_class(None, "test_source", object)
45
+ self.assertIsNone(result)
46
+
47
+ def test_create_class_empty_string_returns_none(self):
48
+ """
49
+ Empty class_name should return None without raising.
50
+ """
51
+ result = ResolverUtil.create_class("", "test_source", object)
52
+ self.assertIsNone(result)
53
+
54
+ def test_create_class_too_few_parts_raises(self):
55
+ """
56
+ A class_name with no dots (single part) should raise ValueError.
57
+ """
58
+ with self.assertRaises(ValueError):
59
+ ResolverUtil.create_class("ClassName", "test_source", object)
60
+
61
+ def test_create_class_two_part_name(self):
62
+ """
63
+ <package_name>.<ClassName> form should resolve correctly.
64
+ """
65
+ mock_class = MagicMock()
66
+ mock_class.__mro__ = [mock_class, object]
67
+
68
+ with patch(RESOLVER_PATH, return_value=mock_class) as mock_resolve, \
69
+ patch("builtins.issubclass", return_value=True):
70
+ result = ResolverUtil.create_class("my_package.MyClass", "test_source", object)
71
+
72
+ mock_resolve.assert_called_once_with("MyClass", module_name="my_package")
73
+ self.assertIs(result, mock_class)
74
+
75
+ def test_create_class_three_part_name(self):
76
+ """
77
+ <package_name>.<module_name>.<ClassName> form should resolve correctly.
78
+ """
79
+ mock_class = MagicMock()
80
+
81
+ with patch(RESOLVER_PATH, return_value=mock_class) as mock_resolve, \
82
+ patch("builtins.issubclass", return_value=True):
83
+ result = ResolverUtil.create_class(
84
+ "my_package.my_module.MyClass", "test_source", object
85
+ )
86
+
87
+ mock_resolve.assert_called_once_with("MyClass", module_name="my_module")
88
+ self.assertIs(result, mock_class)
89
+
90
+ def test_create_class_multi_part_package(self):
91
+ """
92
+ <pkg>.<sub>.<module>.<ClassName> — everything before the last two parts
93
+ is treated as the package path.
94
+ """
95
+ mock_class = MagicMock()
96
+
97
+ with patch(RESOLVER_PATH, return_value=mock_class) as mock_resolve, \
98
+ patch("builtins.issubclass", return_value=True):
99
+ result = ResolverUtil.create_class(
100
+ "my_pkg.sub_pkg.my_module.MyClass", "test_source", object
101
+ )
102
+
103
+ mock_resolve.assert_called_once_with("MyClass", module_name="my_module")
104
+ self.assertIs(result, mock_class)
105
+
106
+ def test_create_class_attribute_error_raises_value_error(self):
107
+ """
108
+ If resolve_class_in_module raises AttributeError, a ValueError is raised.
109
+ """
110
+ with patch(RESOLVER_PATH, side_effect=AttributeError("not found")):
111
+ with self.assertRaises(ValueError):
112
+ ResolverUtil.create_class(
113
+ "my_package.my_module.MyClass", "test_source", object
114
+ )
115
+
116
+ def test_create_class_wrong_type_raises_value_error(self):
117
+ """
118
+ If the resolved class is not a subclass of type_of_class, a ValueError is raised.
119
+ """
120
+ class Base:
121
+ """Base class for type-check testing."""
122
+
123
+ class NotBase:
124
+ """Class that does not inherit from Base."""
125
+
126
+ with patch(RESOLVER_PATH, return_value=NotBase):
127
+ with self.assertRaises(ValueError):
128
+ ResolverUtil.create_class(
129
+ "my_package.my_module.NotBase", "test_source", Base
130
+ )
131
+
132
+ # -------------------------------------------------------------------------
133
+ # create_instance tests
134
+ # -------------------------------------------------------------------------
135
+
136
+ def test_create_instance_none_returns_none(self):
137
+ """
138
+ None class_name propagates through create_class and returns None.
139
+ """
140
+ result = ResolverUtil.create_instance(None, "test_source", object)
141
+ self.assertIsNone(result)
142
+
143
+ def test_create_instance_creates_object(self):
144
+ """
145
+ A valid class_name should produce an instance of the resolved class.
146
+ """
147
+ mock_instance = MagicMock()
148
+ mock_class = MagicMock(return_value=mock_instance)
149
+
150
+ with patch(RESOLVER_PATH, return_value=mock_class), \
151
+ patch("builtins.issubclass", return_value=True):
152
+ result = ResolverUtil.create_instance(
153
+ "my_package.my_module.MyClass", "test_source", object
154
+ )
155
+
156
+ mock_class.assert_called_once_with()
157
+ self.assertIs(result, mock_instance)
158
+
159
+ def test_create_instance_no_args_constructor_raises_value_error(self):
160
+ """
161
+ If the class cannot be instantiated without args, a ValueError is raised.
162
+ """
163
+ mock_class = MagicMock(side_effect=TypeError("__init__ requires args"))
164
+
165
+ with patch(RESOLVER_PATH, return_value=mock_class), \
166
+ patch("builtins.issubclass", return_value=True):
167
+ with self.assertRaises(ValueError):
168
+ ResolverUtil.create_instance(
169
+ "my_package.my_module.MyClass", "test_source", object
170
+ )
171
+
172
+ # -------------------------------------------------------------------------
173
+ # create_type tests
174
+ # -------------------------------------------------------------------------
175
+
176
+ def test_create_type_none_returns_none(self):
177
+ """
178
+ None or empty fully_qualified_name should return None.
179
+ """
180
+ self.assertIsNone(ResolverUtil.create_type(None))
181
+ self.assertIsNone(ResolverUtil.create_type(""))
182
+
183
+ def test_create_type_resolves_class(self):
184
+ """
185
+ A fully qualified name should resolve to the correct class via Resolver.
186
+ """
187
+ mock_class = MagicMock()
188
+
189
+ with patch(RESOLVER_PATH, return_value=mock_class) as mock_resolve:
190
+ result = ResolverUtil.create_type("my_package.my_module.MyClass")
191
+
192
+ mock_resolve.assert_called_once_with(
193
+ "MyClass",
194
+ module_name="my_package.my_module",
195
+ raise_if_not_found=True,
196
+ install_if_missing=None,
197
+ )
198
+ self.assertIs(result, mock_class)
199
+
200
+ # -------------------------------------------------------------------------
201
+ # create_type_tuple tests
202
+ # -------------------------------------------------------------------------
203
+
204
+ def test_create_type_tuple_empty_list(self):
205
+ """
206
+ An empty or None type_list should return an empty tuple.
207
+ """
208
+ self.assertEqual(ResolverUtil.create_type_tuple([]), ())
209
+ self.assertEqual(ResolverUtil.create_type_tuple(None), ())
210
+
211
+ def test_create_type_tuple_resolves_types(self):
212
+ """
213
+ Each resolvable name in the list should appear in the returned tuple.
214
+ """
215
+ mock_class_a = MagicMock()
216
+ mock_class_b = MagicMock()
217
+
218
+ side_effects = [mock_class_a, mock_class_b]
219
+
220
+ with patch(RESOLVER_PATH, side_effect=side_effects):
221
+ result = ResolverUtil.create_type_tuple([
222
+ "pkg.mod.ClassA",
223
+ "pkg.mod.ClassB",
224
+ ])
225
+
226
+ self.assertEqual(len(result), 2)
227
+ self.assertIn(mock_class_a, result)
228
+ self.assertIn(mock_class_b, result)
229
+
230
+ def test_create_type_tuple_skips_unresolvable(self):
231
+ """
232
+ Types that cannot be resolved (raise_if_not_found=False) are skipped.
233
+ """
234
+ mock_class_a = MagicMock()
235
+
236
+ with patch(RESOLVER_PATH, side_effect=[mock_class_a, None]):
237
+ result = ResolverUtil.create_type_tuple([
238
+ "pkg.mod.ClassA",
239
+ "pkg.mod.Missing",
240
+ ])
241
+
242
+ self.assertEqual(len(result), 1)
243
+ self.assertIn(mock_class_a, result)
@@ -1,67 +0,0 @@
1
- name: Test
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- - "**"
8
- pull_request:
9
-
10
- permissions:
11
- contents: read
12
-
13
- jobs:
14
- test:
15
- runs-on: ubuntu-latest
16
- container:
17
- image: python:3.12-slim
18
-
19
- steps:
20
- - name: Checkout
21
- uses: actions/checkout@v4
22
-
23
- - name: Install dependencies
24
- run: |
25
- pip install -r requirements-build.txt
26
- pip install -r requirements.txt
27
-
28
- - name: Show installed packages
29
- run: pip freeze
30
-
31
- - name: Run pylint
32
- run: build_scripts/run_pylint.sh
33
-
34
- - name: Run flake8
35
- run: flake8
36
-
37
- - name: Run pytest (excluding integration tests)
38
- run: pytest --verbose -m "not integration" --timer-top-n 10 --cov-config=.coveragerc --cov
39
-
40
- - name: Check README renders correctly on PyPI
41
- run: |
42
- pip install readme_renderer readme_renderer[md]
43
- python -m readme_renderer README.md
44
-
45
- - name: Notify Slack on success
46
- if: success()
47
- uses: slackapi/slack-github-action@v1.24.0
48
- with:
49
- payload: |
50
- {
51
- "text": "✅ *Tests Passed* for `${{ github.repository }}` on `${{ github.ref_name }}`"
52
- }
53
- env:
54
- SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
55
-
56
- - name: Notify Slack on failure
57
- if: failure()
58
- uses: slackapi/slack-github-action@v1.24.0
59
- with:
60
- payload: |
61
- {
62
- "text": "❌ *Tests Failed* for `${{ github.repository }}` on `${{ github.ref_name }}`"
63
- }
64
- env:
65
- SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
66
-
67
-
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes