omegaconf 2.4.0.dev13__tar.gz → 2.4.0.dev14__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 (166) hide show
  1. {omegaconf-2.4.0.dev13/omegaconf.egg-info → omegaconf-2.4.0.dev14}/PKG-INFO +2 -5
  2. omegaconf-2.4.0.dev14/build_helpers/__init__.py +5 -0
  3. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/build_helpers/get_vendored.py +46 -30
  4. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/__init__.py +1 -2
  5. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/_utils.py +273 -31
  6. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/base.py +134 -21
  7. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/basecontainer.py +105 -29
  8. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/dictconfig.py +11 -2
  9. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar_visitor.py +2 -1
  10. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/listconfig.py +27 -18
  11. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/nodes.py +42 -0
  12. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/omegaconf.py +414 -54
  13. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/version.py +1 -1
  14. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14/omegaconf.egg-info}/PKG-INFO +2 -5
  15. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf.egg-info/SOURCES.txt +2 -0
  16. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf.egg-info/requires.txt +1 -4
  17. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/pyproject.toml +21 -27
  18. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/requirements/dev.txt +1 -4
  19. omegaconf-2.4.0.dev14/setup.cfg +7 -0
  20. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/__init__.py +0 -5
  21. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/test_custom_resolvers.py +3 -1
  22. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/test_interpolation.py +10 -5
  23. omegaconf-2.4.0.dev14/tests/interpolation/test_resolver_annotation_validation.py +522 -0
  24. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/data/attr_classes.py +21 -1
  25. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/data/dataclasses.py +23 -1
  26. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/data/dataclasses_pre_311.py +23 -1
  27. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/test_structured_basic.py +375 -3
  28. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/test_structured_config.py +41 -4
  29. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_basic_ops_dict.py +12 -0
  30. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_basic_ops_list.py +69 -2
  31. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_container_unions.py +17 -20
  32. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_create.py +12 -6
  33. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_errors.py +3 -14
  34. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_merge.py +144 -50
  35. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_nodes.py +6 -0
  36. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_omegaconf.py +5 -0
  37. omegaconf-2.4.0.dev14/tests/test_structured_config_unions.py +614 -0
  38. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_tuple_merge.py +1 -16
  39. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_unions.py +73 -2
  40. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_update.py +283 -2
  41. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_utils.py +39 -3
  42. omegaconf-2.4.0.dev13/build_helpers/__init__.py +0 -3
  43. omegaconf-2.4.0.dev13/setup.cfg +0 -12
  44. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/LICENSE +0 -0
  45. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/MANIFEST.in +0 -0
  46. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/README.md +0 -0
  47. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/build_helpers/bin/antlr-4.11.1-complete.jar +0 -0
  48. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/build_helpers/build_helpers.py +0 -0
  49. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/build_helpers/test_helpers.py +0 -0
  50. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/_impl.py +0 -0
  51. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/_yaml.py +0 -0
  52. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/errors.py +0 -0
  53. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/OmegaConfGrammarLexer.g4 +0 -0
  54. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/OmegaConfGrammarParser.g4 +0 -0
  55. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/__init__.py +0 -0
  56. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/gen/OmegaConfGrammarLexer.py +0 -0
  57. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/gen/OmegaConfGrammarParser.py +0 -0
  58. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/gen/OmegaConfGrammarParserListener.py +0 -0
  59. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/gen/OmegaConfGrammarParserVisitor.py +0 -0
  60. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar/gen/__init__.py +0 -0
  61. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/grammar_parser.py +0 -0
  62. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/py.typed +0 -0
  63. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/resolvers/__init__.py +0 -0
  64. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/resolvers/oc/__init__.py +0 -0
  65. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/resolvers/oc/dict.py +0 -0
  66. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/tupleconfig.py +0 -0
  67. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/typing.py +0 -0
  68. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/__init__.py +0 -0
  69. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/BufferedTokenStream.py +0 -0
  70. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/CommonTokenFactory.py +0 -0
  71. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/CommonTokenStream.py +0 -0
  72. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/FileStream.py +0 -0
  73. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/InputStream.py +0 -0
  74. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/IntervalSet.py +0 -0
  75. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/LL1Analyzer.py +0 -0
  76. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/Lexer.py +0 -0
  77. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/ListTokenSource.py +0 -0
  78. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/Parser.py +0 -0
  79. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/ParserInterpreter.py +0 -0
  80. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/ParserRuleContext.py +0 -0
  81. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/PredictionContext.py +0 -0
  82. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/Recognizer.py +0 -0
  83. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/RuleContext.py +0 -0
  84. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/StdinStream.py +0 -0
  85. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/Token.py +0 -0
  86. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/TokenStreamRewriter.py +0 -0
  87. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/Utils.py +0 -0
  88. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/__init__.py +0 -0
  89. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/_pygrun.py +0 -0
  90. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATN.py +0 -0
  91. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNConfig.py +0 -0
  92. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNConfigSet.py +0 -0
  93. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
  94. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNDeserializer.py +0 -0
  95. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNSimulator.py +0 -0
  96. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNState.py +0 -0
  97. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ATNType.py +0 -0
  98. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
  99. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/LexerAction.py +0 -0
  100. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
  101. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
  102. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/PredictionMode.py +0 -0
  103. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/SemanticContext.py +0 -0
  104. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/Transition.py +0 -0
  105. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/atn/__init__.py +0 -0
  106. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/dfa/DFA.py +0 -0
  107. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/dfa/DFASerializer.py +0 -0
  108. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/dfa/DFAState.py +0 -0
  109. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/dfa/__init__.py +0 -0
  110. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
  111. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/error/ErrorListener.py +0 -0
  112. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/error/ErrorStrategy.py +0 -0
  113. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/error/Errors.py +0 -0
  114. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/error/__init__.py +0 -0
  115. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/Chunk.py +0 -0
  116. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
  117. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/ParseTreePattern.py +0 -0
  118. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
  119. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/RuleTagToken.py +0 -0
  120. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/TokenTagToken.py +0 -0
  121. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/Tree.py +0 -0
  122. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/Trees.py +0 -0
  123. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/tree/__init__.py +0 -0
  124. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/xpath/XPath.py +0 -0
  125. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/xpath/XPathLexer.py +0 -0
  126. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf/vendor/antlr4/xpath/__init__.py +0 -0
  127. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf.egg-info/dependency_links.txt +0 -0
  128. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/omegaconf.egg-info/top_level.txt +0 -0
  129. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/requirements/base.txt +0 -0
  130. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/requirements/docs.txt +0 -0
  131. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/setup.py +0 -0
  132. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/conftest.py +0 -0
  133. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/data/2.0.6.pickle +0 -0
  134. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/data/2.1.0.rc1.pickle +0 -0
  135. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/data/load.py +0 -0
  136. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/data/save.py +0 -0
  137. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/examples/__init__.py +0 -0
  138. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/examples/dataclass_postponed_annotations.py +0 -0
  139. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/examples/test_dataclass_example.py +0 -0
  140. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/examples/test_postponed_annotations.py +0 -0
  141. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/__init__.py +0 -0
  142. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/__init__.py +0 -0
  143. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_create.py +0 -0
  144. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_decode.py +0 -0
  145. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_deprecated.py +0 -0
  146. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_dict.py +0 -0
  147. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_env.py +0 -0
  148. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/interpolation/built_in_resolvers/test_oc_select.py +0 -0
  149. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/__init__.py +0 -0
  150. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/structured_conf/data/__init__.py +0 -0
  151. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_base_config.py +0 -0
  152. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_basic_ops_tuple.py +0 -0
  153. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_compare_dictconfig_vs_dict.py +0 -0
  154. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_config_eq.py +0 -0
  155. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_get_full_key.py +0 -0
  156. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_grammar.py +0 -0
  157. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_matrix.py +0 -0
  158. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_nested_containers.py +0 -0
  159. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_readonly.py +0 -0
  160. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_select.py +0 -0
  161. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_serialization.py +0 -0
  162. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_struct.py +0 -0
  163. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_to_container.py +0 -0
  164. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_to_yaml.py +0 -0
  165. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_tuple_integration.py +0 -0
  166. {omegaconf-2.4.0.dev13 → omegaconf-2.4.0.dev14}/tests/test_tuple_structured.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omegaconf
3
- Version: 2.4.0.dev13
3
+ Version: 2.4.0.dev14
4
4
  Summary: A flexible configuration library
5
5
  Home-page: https://github.com/omry/omegaconf
6
6
  Author: Omry Yadan
@@ -22,19 +22,16 @@ Requires-Dist: PyYAML>=5.1.0; extra == "dev"
22
22
  Requires-Dist: sphinx; extra == "dev"
23
23
  Requires-Dist: sphinx-tabs; extra == "dev"
24
24
  Requires-Dist: attrs; extra == "dev"
25
- Requires-Dist: black[jupyter]==26.3.1; extra == "dev"
26
25
  Requires-Dist: build; extra == "dev"
27
26
  Requires-Dist: bump-my-version; extra == "dev"
28
27
  Requires-Dist: coveralls; extra == "dev"
29
- Requires-Dist: flake8==7.3.0; extra == "dev"
30
- Requires-Dist: isort==5.13.2; extra == "dev"
31
28
  Requires-Dist: nox[pbs]; extra == "dev"
32
29
  Requires-Dist: pre-commit; extra == "dev"
33
- Requires-Dist: pyflakes; extra == "dev"
34
30
  Requires-Dist: pyrefly==0.62.0; extra == "dev"
35
31
  Requires-Dist: pytest; extra == "dev"
36
32
  Requires-Dist: pytest-benchmark; extra == "dev"
37
33
  Requires-Dist: pytest-mock; extra == "dev"
34
+ Requires-Dist: ruff==0.15.22; extra == "dev"
38
35
  Requires-Dist: towncrier; extra == "dev"
39
36
  Requires-Dist: twine; extra == "dev"
40
37
  Requires-Dist: pydevd; extra == "dev"
@@ -0,0 +1,5 @@
1
+ # ruff: noqa: F401, I001
2
+
3
+ # Order of imports is important (see warning otherwise when running tests)
4
+ import setuptools
5
+ import distutils
@@ -6,10 +6,12 @@ from itertools import chain
6
6
  from pathlib import Path
7
7
  from typing import Callable, FrozenSet, Generator, List, Set, Tuple, Union
8
8
 
9
- WHITELIST = {'README.txt', '__init__.py', 'vendor.txt'}
9
+ WHITELIST = {"README.txt", "__init__.py", "vendor.txt"}
10
10
 
11
11
 
12
- def delete_all(*paths: Path, whitelist: Union[Set[str], FrozenSet[str]] = frozenset()) -> None:
12
+ def delete_all(
13
+ *paths: Path, whitelist: Union[Set[str], FrozenSet[str]] = frozenset()
14
+ ) -> None:
13
15
  """Clear all the items in each of the indicated paths, except for elements listed
14
16
  in the whitelist"""
15
17
  for item in paths:
@@ -34,13 +36,15 @@ def iter_subtree(path: Path, depth: int = 0) -> Generator[Tuple[Path, int], None
34
36
 
35
37
  def patch_vendor_imports(file: Path, replacements: List[Callable[[str], str]]) -> None:
36
38
  """Apply a list of replacements/patches to a given file"""
37
- text = file.read_text('utf8')
39
+ text = file.read_text("utf8")
38
40
  for replacement in replacements:
39
41
  text = replacement(text)
40
- file.write_text(text, 'utf8')
42
+ file.write_text(text, "utf8")
41
43
 
42
44
 
43
- def find_vendored_libs(vendor_dir: Path, whitelist: Set[str]) -> Tuple[List[str], List[Path]]:
45
+ def find_vendored_libs(
46
+ vendor_dir: Path, whitelist: Set[str]
47
+ ) -> Tuple[List[str], List[Path]]:
44
48
  vendored_libs = []
45
49
  paths = []
46
50
  for item in vendor_dir.iterdir():
@@ -56,23 +60,31 @@ def find_vendored_libs(vendor_dir: Path, whitelist: Set[str]) -> Tuple[List[str]
56
60
 
57
61
  def vendor(vendor_dir: Path, relative_imports: bool = False) -> None:
58
62
  # target package is <parent>.<vendor_dir>; foo/vendor -> foo.vendor
59
- pkgname = f'{vendor_dir.parent.name}.{vendor_dir.name}'
63
+ pkgname = f"{vendor_dir.parent.name}.{vendor_dir.name}"
60
64
 
61
65
  # remove everything
62
66
  delete_all(*vendor_dir.iterdir(), whitelist=WHITELIST)
63
67
 
64
68
  # install with pip
65
- subprocess.run([
66
- 'pip', 'install', '-t', str(vendor_dir),
67
- '-r', str(vendor_dir / 'vendor.txt'),
68
- '--no-compile', '--no-deps'
69
- ])
69
+ subprocess.run(
70
+ [
71
+ "pip",
72
+ "install",
73
+ "-t",
74
+ str(vendor_dir),
75
+ "-r",
76
+ str(vendor_dir / "vendor.txt"),
77
+ "--no-compile",
78
+ "--no-deps",
79
+ ]
80
+ )
70
81
 
71
82
  # delete stuff that's not needed
72
83
  delete_all(
73
- *vendor_dir.glob('*.dist-info'),
74
- *vendor_dir.glob('*.egg-info'),
75
- vendor_dir / 'bin')
84
+ *vendor_dir.glob("*.dist-info"),
85
+ *vendor_dir.glob("*.egg-info"),
86
+ vendor_dir / "bin",
87
+ )
76
88
 
77
89
  vendored_libs, paths = find_vendored_libs(vendor_dir, WHITELIST)
78
90
 
@@ -81,41 +93,45 @@ def vendor(vendor_dir: Path, relative_imports: bool = False) -> None:
81
93
  for lib in vendored_libs:
82
94
  replacements += (
83
95
  partial( # import bar -> import foo.vendor.bar
84
- re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
85
- r'\1from {} import {}\n'.format(pkgname, lib)
96
+ re.compile(r"(^\s*)import {}\n".format(lib), flags=re.M).sub,
97
+ r"\1from {} import {}\n".format(pkgname, lib),
86
98
  ),
87
99
  partial( # from bar -> from foo.vendor.bar
88
- re.compile(r'(^\s*)from {}(\.|\s+)'.format(lib), flags=re.M).sub,
89
- r'\1from {}.{}\2'.format(pkgname, lib)
100
+ re.compile(r"(^\s*)from {}(\.|\s+)".format(lib), flags=re.M).sub,
101
+ r"\1from {}.{}\2".format(pkgname, lib),
90
102
  ),
91
103
  )
92
104
 
93
105
  for file, depth in chain.from_iterable(map(iter_subtree, paths)):
94
106
  if relative_imports:
95
- pkgname = '.' * (depth - 1)
107
+ pkgname = "." * (depth - 1)
96
108
  replacements = []
97
109
  for lib in vendored_libs:
98
110
  replacements += (
99
111
  partial(
100
- re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
101
- r'\1from {} import {}\n'.format(pkgname, "")
112
+ re.compile(r"(^\s*)import {}\n".format(lib), flags=re.M).sub,
113
+ r"\1from {} import {}\n".format(pkgname, ""),
102
114
  ),
103
115
  partial(
104
- re.compile(r'^from {}(\s+)'.format(lib), flags=re.M).sub,
105
- r'from .{}\1'.format(pkgname)
116
+ re.compile(r"^from {}(\s+)".format(lib), flags=re.M).sub,
117
+ r"from .{}\1".format(pkgname),
106
118
  ),
107
119
  partial(
108
- re.compile(r'(^\s*)from {}(\.+)'.format(lib), flags=re.M).sub,
109
- r'\1from {}\2'.format(pkgname)
120
+ re.compile(r"(^\s*)from {}(\.+)".format(lib), flags=re.M).sub,
121
+ r"\1from {}\2".format(pkgname),
110
122
  ),
111
123
  )
112
124
  patch_vendor_imports(file, replacements)
113
125
 
114
126
 
115
- if __name__ == '__main__':
127
+ if __name__ == "__main__":
116
128
  # this assumes this is a script in `build_helpers`
117
- here = Path('__file__').resolve().parent
118
- vendor_dir = here / 'omegaconf' / 'vendor'
119
- assert (vendor_dir / 'vendor.txt').exists(), 'omegaconf/vendor/vendor.txt file not found'
120
- assert (vendor_dir / '__init__.py').exists(), 'omegaconf/vendor/__init__.py file not found'
129
+ repo_root = Path(__file__).resolve().parent.parent
130
+ vendor_dir = repo_root / "omegaconf" / "vendor"
131
+ assert (vendor_dir / "vendor.txt").exists(), (
132
+ "omegaconf/vendor/vendor.txt file not found"
133
+ )
134
+ assert (vendor_dir / "__init__.py").exists(), (
135
+ "omegaconf/vendor/__init__.py file not found"
136
+ )
121
137
  vendor(vendor_dir, relative_imports=True)
@@ -1,4 +1,4 @@
1
- from .base import Container, DictKeyType, ListMergeMode, Node, SCMode, UnionNode
1
+ from .base import Container, DictKeyType, Node, SCMode, UnionNode
2
2
  from .dictconfig import DictConfig
3
3
  from .errors import (
4
4
  KeyValidationError,
@@ -50,7 +50,6 @@ __all__ = [
50
50
  "OmegaConf",
51
51
  "Resolver",
52
52
  "SCMode",
53
- "ListMergeMode",
54
53
  "flag_override",
55
54
  "read_write",
56
55
  "open_dict",
@@ -52,6 +52,8 @@ if TYPE_CHECKING:
52
52
  import attr
53
53
  from attr import Attribute as AttrAttribute
54
54
 
55
+ from .basecontainer import BaseContainer
56
+
55
57
 
56
58
  NoneType: Type[None] = type(None)
57
59
 
@@ -230,26 +232,130 @@ def is_union_annotation(type_: Any) -> bool:
230
232
  return getattr(type_, "__origin__", None) is Union
231
233
 
232
234
 
233
- def _resolve_optional(type_: Any) -> Tuple[bool, Any]:
234
- """Check whether `type_` is equivalent to `typing.Optional[T]` for some T."""
235
+ def _resolve_type_alias(type_: Any) -> Any:
235
236
  if sys.version_info >= (3, 12): # pragma: no cover
236
237
  import typing # lgtm [py/import-and-import-from]
237
238
 
238
- if isinstance(type_, typing.TypeAliasType):
239
- type_ = type_.__value__
239
+ seen = set()
240
+ while id(type_) not in seen:
241
+ seen.add(id(type_))
242
+ if isinstance(type_, typing.TypeAliasType):
243
+ bare_alias = True
244
+ origin = type_
245
+ arguments: Tuple[Any, ...] = ()
246
+ else:
247
+ bare_alias = False
248
+ origin = typing.get_origin(type_)
249
+ if not isinstance(origin, typing.TypeAliasType):
250
+ break
251
+ arguments = typing.get_args(type_)
252
+
253
+ parameters = origin.__type_params__
254
+ variadic = [
255
+ parameter
256
+ for parameter in parameters
257
+ if isinstance(parameter, typing.TypeVarTuple)
258
+ ]
259
+ if len(variadic) > 1:
260
+ break
261
+
262
+ if bare_alias:
263
+ substitutions = {}
264
+ elif variadic:
265
+ variadic_index = parameters.index(variadic[0])
266
+ trailing = len(parameters) - variadic_index - 1
267
+ if len(arguments) < len(parameters) - 1:
268
+ break
269
+ substitutions = dict(zip(parameters[:variadic_index], arguments))
270
+ substitutions[variadic[0]] = arguments[
271
+ variadic_index : len(arguments) - trailing if trailing else None
272
+ ]
273
+ if trailing:
274
+ substitutions.update(
275
+ zip(parameters[-trailing:], arguments[-trailing:])
276
+ )
277
+ else:
278
+ if len(arguments) > len(parameters):
279
+ break
280
+ substitutions = dict(zip(parameters, arguments))
281
+
282
+ def substitution_args(value: Any) -> Tuple[Any, ...]:
283
+ result = []
284
+ for parameter in getattr(value, "__parameters__", ()):
285
+ argument = substitutions[parameter]
286
+ if isinstance(parameter, typing.TypeVarTuple):
287
+ result.extend(argument)
288
+ else:
289
+ result.append(argument)
290
+ return tuple(result)
291
+
292
+ no_default = getattr(typing, "NoDefault", None)
293
+ for parameter in parameters:
294
+ if parameter in substitutions:
295
+ continue
296
+ default: Any = getattr(parameter, "__default__", no_default)
297
+ if default is no_default:
298
+ break
299
+
300
+ for known_parameter, argument in substitutions.items():
301
+ if default is known_parameter:
302
+ default = argument
303
+ break
304
+ else:
305
+ default_parameters = getattr(default, "__parameters__", ())
306
+ if default_parameters:
307
+ try:
308
+ default_arguments = substitution_args(default)
309
+ except KeyError:
310
+ break
311
+ default = default[default_arguments]
312
+ if isinstance(parameter, typing.TypeVarTuple):
313
+ default_args = typing.get_args(default)
314
+ if typing.get_origin(default) is typing.Unpack and default_args:
315
+ default = typing.get_args(default_args[0])
316
+ substitutions[parameter] = default
317
+ else:
318
+ value = origin.__value__
319
+ for parameter, argument in substitutions.items():
320
+ if value is parameter:
321
+ value = argument
322
+ break
323
+ else:
324
+ value_parameters = getattr(value, "__parameters__", ())
325
+ if value_parameters:
326
+ try:
327
+ value_arguments = substitution_args(value)
328
+ except KeyError:
329
+ break
330
+ value = value[value_arguments]
331
+ type_ = value
332
+ continue
333
+ break
334
+ return type_
335
+
336
+
337
+ def _resolve_optional(type_: Any) -> Tuple[bool, Any]:
338
+ """Normalize aliases and check whether `type_` accepts None."""
339
+ type_ = _resolve_type_alias(type_)
240
340
  if is_union_annotation(type_):
241
- args = type_.__args__
242
- if NoneType in args:
243
- optional = True
244
- args = tuple(a for a in args if a is not NoneType)
245
- else:
246
- optional = False
341
+ optional = False
342
+ args = []
343
+ for arg in type_.__args__:
344
+ arg_optional, arg_type = _resolve_optional(arg)
345
+ if arg_type is Any:
346
+ return True, Any
347
+ optional = optional or arg_optional
348
+ if arg_type is NoneType:
349
+ continue
350
+ args.append(arg_type)
351
+
247
352
  if len(args) == 1:
248
353
  return optional, args[0]
249
354
  elif len(args) >= 2:
250
- return optional, Union[args]
355
+ return optional, Union[tuple(args)] # pyrefly: ignore[not-a-type]
251
356
  else:
252
- assert False
357
+ # Only distinct PEP 695 aliases that all resolve to None can reach this.
358
+ return True, NoneType # pragma: no cover
253
359
 
254
360
  if type_ is Any:
255
361
  return True, Any
@@ -271,30 +377,111 @@ def _is_optional(obj: Any, key: Optional[Union[int, str]] = None) -> bool:
271
377
  return obj._is_optional()
272
378
 
273
379
 
274
- def _resolve_forward(type_: Type[Any], module: str) -> Type[Any]:
380
+ def _resolve_forward(
381
+ type_: Type[Any],
382
+ module: str,
383
+ preserve_container_origin: bool = False,
384
+ type_alias_guard: Optional[set[int]] = None,
385
+ ) -> Type[Any]:
275
386
  import typing # lgtm [py/import-and-import-from]
276
387
 
388
+ if sys.version_info >= (3, 12): # pragma: no cover
389
+ type_alias = (
390
+ type_
391
+ if isinstance(type_, typing.TypeAliasType)
392
+ else typing.get_origin(type_)
393
+ )
394
+ if isinstance(type_alias, typing.TypeAliasType):
395
+ if type_alias_guard is None:
396
+ type_alias_guard = set()
397
+ if id(type_alias) in type_alias_guard:
398
+ raise ValidationError(
399
+ f"Recursive type alias '{type_alias.__name__}' is not supported"
400
+ )
401
+ type_alias_guard = type_alias_guard | {id(type_alias)}
402
+
403
+ type_ = _resolve_type_alias(type_)
277
404
  forward = typing.ForwardRef if hasattr(typing, "ForwardRef") else typing._ForwardRef # type: ignore
278
405
  if type(type_) is forward:
279
406
  return _get_class(module, type_.__forward_arg__)
280
407
  elif isinstance(type_, str):
281
408
  return _get_class(module, type_)
409
+ elif is_union_annotation(type_):
410
+ is_optional, type_ = _resolve_optional(type_)
411
+ if type_ is Any:
412
+ return Any
413
+ if is_union_annotation(type_):
414
+ args = tuple(
415
+ _resolve_forward(
416
+ arg,
417
+ module=module,
418
+ preserve_container_origin=True,
419
+ type_alias_guard=type_alias_guard,
420
+ )
421
+ for arg in type_.__args__
422
+ )
423
+ type_ = Union[args] # pyrefly: ignore[not-a-type]
424
+ else:
425
+ type_ = _resolve_forward(
426
+ type_,
427
+ module=module,
428
+ preserve_container_origin=True,
429
+ type_alias_guard=type_alias_guard,
430
+ )
431
+ return Optional[type_] if is_optional else type_
282
432
  else:
283
433
  if is_dict_annotation(type_):
434
+ if preserve_container_origin and (
435
+ type_ in (dict, Dict) or getattr(type_, "__origin__", None) is None
436
+ ):
437
+ return type_
284
438
  kt, vt = get_dict_key_value_types(type_)
285
439
  if kt is not None:
286
- kt = _resolve_forward(kt, module=module)
440
+ kt = _resolve_forward(
441
+ kt,
442
+ module=module,
443
+ preserve_container_origin=preserve_container_origin,
444
+ type_alias_guard=type_alias_guard,
445
+ )
287
446
  if vt is not None:
288
- vt = _resolve_forward(vt, module=module)
447
+ vt = _resolve_forward(
448
+ vt,
449
+ module=module,
450
+ preserve_container_origin=preserve_container_origin,
451
+ type_alias_guard=type_alias_guard,
452
+ )
453
+ if preserve_container_origin and isinstance(type_, types.GenericAlias):
454
+ return dict[kt, vt] # type: ignore
289
455
  return Dict[kt, vt] # type: ignore
290
456
  if is_list_annotation(type_):
457
+ if preserve_container_origin and type_ in (list, List):
458
+ return type_
291
459
  et = get_list_element_type(type_)
292
460
  if et is not None:
293
- et = _resolve_forward(et, module=module)
461
+ et = _resolve_forward(
462
+ et,
463
+ module=module,
464
+ preserve_container_origin=preserve_container_origin,
465
+ type_alias_guard=type_alias_guard,
466
+ )
467
+ if preserve_container_origin and isinstance(type_, types.GenericAlias):
468
+ return list[et] # type: ignore
294
469
  return List[et] # type: ignore
295
470
  if is_tuple_annotation(type_):
471
+ if preserve_container_origin and type_ in (tuple, Tuple):
472
+ return type_
296
473
  its = get_tuple_item_types(type_)
297
- its = tuple(_resolve_forward(it, module=module) for it in its)
474
+ its = tuple(
475
+ _resolve_forward(
476
+ it,
477
+ module=module,
478
+ preserve_container_origin=preserve_container_origin,
479
+ type_alias_guard=type_alias_guard,
480
+ )
481
+ for it in its
482
+ )
483
+ if preserve_container_origin and isinstance(type_, types.GenericAlias):
484
+ return tuple[its] # type: ignore
298
485
  return Tuple[its] # type: ignore
299
486
 
300
487
  return type_
@@ -350,7 +537,11 @@ def get_attr_class_fields(obj: Any) -> List["AttrAttribute[Any]"]:
350
537
  return [f for f in fields if f.metadata.get("omegaconf_ignore") is not True]
351
538
 
352
539
 
353
- def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, Any]:
540
+ def get_attr_data(
541
+ obj: Any,
542
+ allow_objects: Optional[bool] = None,
543
+ parent: Optional["BaseContainer"] = None,
544
+ ) -> Dict[str, Any]:
354
545
  from omegaconf.base import Node
355
546
  from omegaconf.omegaconf import OmegaConf, _maybe_wrap
356
547
 
@@ -363,6 +554,9 @@ def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, A
363
554
  obj_type = obj if is_type else type(obj)
364
555
  dummy_parent = OmegaConf.create({}, flags=flags)
365
556
  dummy_parent._metadata.object_type = obj_type
557
+ if parent is not None:
558
+ dummy_parent._set_key(parent._key())
559
+ dummy_parent._set_parent(parent._get_parent())
366
560
  resolved_hints = get_type_hints(obj_type)
367
561
 
368
562
  for attrib in get_attr_class_fields(obj):
@@ -393,7 +587,7 @@ def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, A
393
587
  value = copy.deepcopy(value)
394
588
  if is_union_annotation(type_) and not is_supported_union_annotation(type_):
395
589
  e = ConfigValueError(
396
- f"Unions of containers are not supported:\n{name}: {type_str(type_)}" # noqa: E231
590
+ f"Unsupported type annotation in Union:\n{name}: {type_str(type_)}" # noqa: E231
397
591
  )
398
592
  format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e))
399
593
 
@@ -422,7 +616,9 @@ def get_dataclass_fields(obj: Any) -> List["dataclasses.Field[Any]"]:
422
616
 
423
617
 
424
618
  def get_dataclass_data(
425
- obj: Any, allow_objects: Optional[bool] = None
619
+ obj: Any,
620
+ allow_objects: Optional[bool] = None,
621
+ parent: Optional["BaseContainer"] = None,
426
622
  ) -> Dict[str, Any]:
427
623
  from omegaconf.base import Node
428
624
  from omegaconf.omegaconf import MISSING, OmegaConf, _maybe_wrap
@@ -433,6 +629,9 @@ def get_dataclass_data(
433
629
  obj_type = get_type_of(obj)
434
630
  dummy_parent = OmegaConf.create({}, flags=flags)
435
631
  dummy_parent._metadata.object_type = obj_type
632
+ if parent is not None:
633
+ dummy_parent._set_key(parent._key())
634
+ dummy_parent._set_parent(parent._get_parent())
436
635
  resolved_hints = get_type_hints(obj_type)
437
636
  for field in get_dataclass_fields(obj):
438
637
  name = field.name
@@ -455,7 +654,7 @@ def get_dataclass_data(
455
654
 
456
655
  if is_union_annotation(type_) and not is_supported_union_annotation(type_):
457
656
  e = ConfigValueError(
458
- f"Unions of containers are not supported:\n{name}: {type_str(type_)}" # noqa: E231
657
+ f"Unsupported type annotation in Union:\n{name}: {type_str(type_)}" # noqa: E231
459
658
  )
460
659
  format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e))
461
660
  try:
@@ -552,12 +751,14 @@ def get_structured_config_init_field_aliases(obj: Any) -> Dict[str, str]:
552
751
 
553
752
 
554
753
  def get_structured_config_data(
555
- obj: Any, allow_objects: Optional[bool] = None
754
+ obj: Any,
755
+ allow_objects: Optional[bool] = None,
756
+ parent: Optional["BaseContainer"] = None,
556
757
  ) -> Dict[str, Any]:
557
758
  if is_dataclass(obj):
558
- return get_dataclass_data(obj, allow_objects=allow_objects)
759
+ return get_dataclass_data(obj, allow_objects=allow_objects, parent=parent)
559
760
  elif is_attr_class(obj):
560
- return get_attr_data(obj, allow_objects=allow_objects)
761
+ return get_attr_data(obj, allow_objects=allow_objects, parent=parent)
561
762
  else:
562
763
  raise ValueError(f"Unsupported type: {type(obj).__name__}")
563
764
 
@@ -715,21 +916,58 @@ def is_tuple_annotation(type_: Any) -> bool:
715
916
 
716
917
 
717
918
  def is_supported_union_annotation(obj: Any) -> bool:
718
- """Primitives and typed List/Dict containers are supported in Unions."""
919
+ """Supported value annotations can be used as members of Unions."""
920
+ obj = _resolve_type_alias(obj)
719
921
  if not is_union_annotation(obj):
720
922
  return False
721
- args = obj.__args__
923
+ _, obj = _resolve_optional(obj)
924
+ if obj is Any:
925
+ return True
926
+ args = obj.__args__ if is_union_annotation(obj) else (obj,)
722
927
  return all(
723
- is_primitive_type_annotation(arg) or is_container_annotation(arg)
928
+ is_primitive_type_annotation(arg)
929
+ or is_literal_annotation(arg)
930
+ or is_structured_config(arg)
931
+ or is_container_annotation(arg)
724
932
  for arg in args
725
933
  )
726
934
 
727
935
 
936
+ def select_structured_config_union_member(
937
+ value: Any, candidates: List[Any]
938
+ ) -> Optional[Any]:
939
+ from omegaconf.dictconfig import DictConfig
940
+
941
+ if isinstance(value, DictConfig):
942
+ value_type = value._metadata.object_type
943
+ elif is_structured_config(value):
944
+ value_type = get_type_of(value)
945
+ else:
946
+ return None
947
+
948
+ if not is_structured_config(value_type):
949
+ return None
950
+
951
+ candidate_set = set(candidates)
952
+ return next(
953
+ (base for base in value_type.__mro__ if base in candidate_set),
954
+ None,
955
+ )
956
+
957
+
728
958
  def is_literal_annotation(type_: Any) -> bool:
729
959
  origin = getattr(type_, "__origin__", None)
730
960
  return origin is Literal
731
961
 
732
962
 
963
+ def type_hint_contains_none_literal(type_: Any) -> bool:
964
+ if is_literal_annotation(type_):
965
+ return any(arg is None for arg in type_.__args__)
966
+ return is_union_annotation(type_) and any(
967
+ type_hint_contains_none_literal(arg) for arg in type_.__args__
968
+ )
969
+
970
+
733
971
  def is_dict_subclass(type_: Any) -> bool:
734
972
  return type_ is not None and isinstance(type_, type) and issubclass(type_, Dict)
735
973
 
@@ -744,8 +982,10 @@ def is_primitive_container(obj: Any) -> bool:
744
982
 
745
983
  def get_list_element_type(ref_type: Optional[Type[Any]]) -> Any:
746
984
  args = getattr(ref_type, "__args__", None)
747
- if ref_type is not List and args is not None and args[0]:
985
+ if ref_type is not List and args is not None:
748
986
  element_type = args[0]
987
+ if element_type is None:
988
+ element_type = NoneType
749
989
  else:
750
990
  element_type = Any
751
991
  return element_type
@@ -811,6 +1051,8 @@ def get_dict_key_value_types(ref_type: Any) -> Tuple[Any, Any]:
811
1051
  if args is not None and len(args) == 2:
812
1052
  key_type = args[0]
813
1053
  element_type = args[1]
1054
+ if element_type is None:
1055
+ element_type = NoneType
814
1056
  else:
815
1057
  key_type = Any
816
1058
  element_type = Any
@@ -1083,14 +1325,14 @@ def type_str(t: Any, include_module_name: bool = False) -> str:
1083
1325
  return ret
1084
1326
 
1085
1327
 
1086
- def _ensure_container(target: Any, flags: Optional[Dict[str, bool]] = None) -> Any:
1328
+ def _ensure_container(target: Any) -> Any:
1087
1329
  from omegaconf import OmegaConf
1088
1330
 
1089
1331
  if is_primitive_container(target):
1090
1332
  assert isinstance(target, (list, tuple, dict))
1091
- target = OmegaConf.create(target, flags=flags)
1333
+ target = OmegaConf.create(target)
1092
1334
  elif is_structured_config(target):
1093
- target = OmegaConf.structured(target, flags=flags)
1335
+ target = OmegaConf.structured(target)
1094
1336
  elif not OmegaConf.is_config(target):
1095
1337
  raise ValueError(
1096
1338
  "Invalid input. Supports one of "