omegaconf 2.4.0.dev3__tar.gz → 2.4.0.dev8__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 (163) hide show
  1. omegaconf-2.4.0.dev8/PKG-INFO +63 -0
  2. omegaconf-2.4.0.dev8/README.md +32 -0
  3. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/build_helpers/build_helpers.py +9 -10
  4. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/_utils.py +127 -16
  5. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/base.py +36 -7
  6. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/basecontainer.py +26 -6
  7. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/gen/OmegaConfGrammarLexer.py +1 -1
  8. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/gen/OmegaConfGrammarParser.py +1 -1
  9. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/gen/OmegaConfGrammarParserListener.py +1 -1
  10. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/gen/OmegaConfGrammarParserVisitor.py +1 -1
  11. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar_parser.py +4 -2
  12. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar_visitor.py +1 -1
  13. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/omegaconf.py +4 -6
  14. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/typing.py +1 -6
  15. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/version.py +4 -4
  16. omegaconf-2.4.0.dev8/omegaconf.egg-info/PKG-INFO +63 -0
  17. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf.egg-info/SOURCES.txt +0 -4
  18. omegaconf-2.4.0.dev8/omegaconf.egg-info/requires.txt +1 -0
  19. omegaconf-2.4.0.dev8/omegaconf.egg-info/top_level.txt +1 -0
  20. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/pyproject.toml +8 -1
  21. omegaconf-2.4.0.dev8/requirements/base.txt +1 -0
  22. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/setup.cfg +7 -2
  23. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/setup.py +8 -8
  24. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/__init__.py +2 -1
  25. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_decode.py +2 -2
  26. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_deprecated.py +1 -1
  27. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/test_custom_resolvers.py +2 -2
  28. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/test_interpolation.py +4 -8
  29. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/data/attr_classes.py +3 -3
  30. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/data/dataclasses.py +3 -3
  31. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/data/dataclasses_pre_311.py +10 -8
  32. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/test_structured_config.py +10 -10
  33. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_basic_ops_dict.py +1 -1
  34. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_basic_ops_list.py +15 -12
  35. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_compare_dictconfig_vs_dict.py +2 -2
  36. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_create.py +71 -24
  37. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_errors.py +4 -6
  38. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_grammar.py +3 -3
  39. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_merge.py +9 -21
  40. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_nodes.py +49 -2
  41. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_select.py +4 -4
  42. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_to_container.py +65 -0
  43. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_to_yaml.py +2 -4
  44. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_utils.py +7 -7
  45. omegaconf-2.4.0.dev3/PKG-INFO +0 -72
  46. omegaconf-2.4.0.dev3/README.md +0 -51
  47. omegaconf-2.4.0.dev3/omegaconf.egg-info/PKG-INFO +0 -72
  48. omegaconf-2.4.0.dev3/omegaconf.egg-info/requires.txt +0 -7
  49. omegaconf-2.4.0.dev3/omegaconf.egg-info/top_level.txt +0 -2
  50. omegaconf-2.4.0.dev3/pydevd_plugins/__init__.py +0 -6
  51. omegaconf-2.4.0.dev3/pydevd_plugins/extensions/__init__.py +0 -6
  52. omegaconf-2.4.0.dev3/pydevd_plugins/extensions/pydevd_plugin_omegaconf.py +0 -126
  53. omegaconf-2.4.0.dev3/requirements/base.txt +0 -5
  54. omegaconf-2.4.0.dev3/tests/test_pydev_resolver_plugin.py +0 -278
  55. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/LICENSE +0 -0
  56. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/MANIFEST.in +0 -0
  57. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/build_helpers/__init__.py +0 -0
  58. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/build_helpers/bin/antlr-4.11.1-complete.jar +0 -0
  59. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/build_helpers/get_vendored.py +0 -0
  60. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/build_helpers/test_helpers.py +0 -0
  61. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/__init__.py +0 -0
  62. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/_impl.py +0 -0
  63. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/dictconfig.py +0 -0
  64. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/errors.py +0 -0
  65. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/OmegaConfGrammarLexer.g4 +0 -0
  66. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/OmegaConfGrammarParser.g4 +0 -0
  67. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/__init__.py +0 -0
  68. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/grammar/gen/__init__.py +0 -0
  69. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/listconfig.py +0 -0
  70. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/nodes.py +0 -0
  71. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/py.typed +0 -0
  72. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/resolvers/__init__.py +0 -0
  73. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/resolvers/oc/__init__.py +0 -0
  74. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/resolvers/oc/dict.py +0 -0
  75. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/__init__.py +0 -0
  76. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/BufferedTokenStream.py +0 -0
  77. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/CommonTokenFactory.py +0 -0
  78. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/CommonTokenStream.py +0 -0
  79. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/FileStream.py +0 -0
  80. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/InputStream.py +0 -0
  81. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/IntervalSet.py +0 -0
  82. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/LL1Analyzer.py +0 -0
  83. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/Lexer.py +0 -0
  84. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/ListTokenSource.py +0 -0
  85. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/Parser.py +0 -0
  86. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/ParserInterpreter.py +0 -0
  87. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/ParserRuleContext.py +0 -0
  88. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/PredictionContext.py +0 -0
  89. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/Recognizer.py +0 -0
  90. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/RuleContext.py +0 -0
  91. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/StdinStream.py +0 -0
  92. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/Token.py +0 -0
  93. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/TokenStreamRewriter.py +0 -0
  94. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/Utils.py +0 -0
  95. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/__init__.py +0 -0
  96. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/_pygrun.py +0 -0
  97. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATN.py +0 -0
  98. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNConfig.py +0 -0
  99. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNConfigSet.py +0 -0
  100. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
  101. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNDeserializer.py +0 -0
  102. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNSimulator.py +0 -0
  103. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNState.py +0 -0
  104. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ATNType.py +0 -0
  105. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
  106. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/LexerAction.py +0 -0
  107. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
  108. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
  109. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/PredictionMode.py +0 -0
  110. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/SemanticContext.py +0 -0
  111. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/Transition.py +0 -0
  112. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/atn/__init__.py +0 -0
  113. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/dfa/DFA.py +0 -0
  114. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/dfa/DFASerializer.py +0 -0
  115. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/dfa/DFAState.py +0 -0
  116. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/dfa/__init__.py +0 -0
  117. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
  118. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/error/ErrorListener.py +0 -0
  119. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/error/ErrorStrategy.py +0 -0
  120. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/error/Errors.py +0 -0
  121. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/error/__init__.py +0 -0
  122. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/Chunk.py +0 -0
  123. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
  124. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/ParseTreePattern.py +0 -0
  125. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
  126. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/RuleTagToken.py +0 -0
  127. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/TokenTagToken.py +0 -0
  128. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/Tree.py +0 -0
  129. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/Trees.py +0 -0
  130. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/tree/__init__.py +0 -0
  131. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/xpath/XPath.py +0 -0
  132. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/xpath/XPathLexer.py +0 -0
  133. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf/vendor/antlr4/xpath/__init__.py +0 -0
  134. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/omegaconf.egg-info/dependency_links.txt +0 -0
  135. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/conftest.py +0 -0
  136. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/data/2.0.6.pickle +0 -0
  137. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/data/2.1.0.rc1.pickle +0 -0
  138. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/data/load.py +0 -0
  139. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/data/save.py +0 -0
  140. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/examples/__init__.py +0 -0
  141. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/examples/dataclass_postponed_annotations.py +0 -0
  142. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/examples/test_dataclass_example.py +0 -0
  143. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/examples/test_postponed_annotations.py +0 -0
  144. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/__init__.py +0 -0
  145. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/__init__.py +0 -0
  146. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_create.py +0 -0
  147. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_dict.py +0 -0
  148. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_env.py +0 -0
  149. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/interpolation/built_in_resolvers/test_oc_select.py +0 -0
  150. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/__init__.py +0 -0
  151. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/data/__init__.py +0 -0
  152. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/structured_conf/test_structured_basic.py +0 -0
  153. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_base_config.py +0 -0
  154. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_config_eq.py +0 -0
  155. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_get_full_key.py +0 -0
  156. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_matrix.py +0 -0
  157. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_nested_containers.py +0 -0
  158. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_omegaconf.py +0 -0
  159. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_readonly.py +0 -0
  160. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_serialization.py +0 -0
  161. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_struct.py +0 -0
  162. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_unions.py +0 -0
  163. {omegaconf-2.4.0.dev3 → omegaconf-2.4.0.dev8}/tests/test_update.py +0 -0
@@ -0,0 +1,63 @@
1
+ Metadata-Version: 2.4
2
+ Name: omegaconf
3
+ Version: 2.4.0.dev8
4
+ Summary: A flexible configuration library
5
+ Home-page: https://github.com/omry/omegaconf
6
+ Author: Omry Yadan
7
+ Author-email: omry@yadan.net
8
+ Keywords: yaml configuration config
9
+ Classifier: Programming Language :: Python :: 3.10
10
+ Classifier: Programming Language :: Python :: 3.11
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Classifier: Programming Language :: Python :: 3.13
13
+ Classifier: Programming Language :: Python :: 3.14
14
+ Classifier: License :: OSI Approved :: BSD License
15
+ Classifier: Operating System :: OS Independent
16
+ Requires-Python: >=3.10
17
+ Description-Content-Type: text/markdown
18
+ License-File: LICENSE
19
+ Requires-Dist: PyYAML>=5.1.0
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license-file
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ # OmegaConf
33
+ | | Description |
34
+ | --- | --- |
35
+ | Project | [![PyPI version](https://badge.fury.io/py/omegaconf.svg)](https://badge.fury.io/py/omegaconf)[![Downloads](https://pepy.tech/badge/omegaconf/month)](https://pepy.tech/project/omegaconf)![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue) |
36
+ | Code quality| [![CircleCI](https://img.shields.io/circleci/build/github/omry/omegaconf?logo=s&token=5de2f8dc2a0dd78438520575431aa533150806e3)](https://circleci.com/gh/omry/omegaconf)[![Coverage Status](https://coveralls.io/repos/github/omry/omegaconf/badge.svg)](https://coveralls.io/github/omry/omegaconf)|
37
+ | Docs and support |[![Documentation Status](https://readthedocs.org/projects/omegaconf/badge/?version=2.0_branch)](https://omegaconf.readthedocs.io/en/2.1_branch/)[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)|
38
+
39
+
40
+ OmegaConf is a hierarchical configuration system, with support for merging configurations from multiple sources (YAML config files, dataclasses/objects and CLI arguments)
41
+ providing a consistent API regardless of how the configuration was created.
42
+
43
+ ## Optional subprojects
44
+
45
+ - [`omegaconf-pydevd`](./subprojects/omegaconf-pydevd/README.md): optional `pydevd` debugger plugin for inspecting OmegaConf objects in supported debuggers.
46
+
47
+ ## Releases
48
+
49
+ ### Upcoming (2.4.0.dev)
50
+ OmegaConf 2.4.0.dev is the upcoming development version.
51
+ * [Documentation](https://omegaconf.readthedocs.io/en/latest/)
52
+ * [Source code](https://github.com/omry/omegaconf/tree/master)
53
+
54
+ ### Stable (2.3)
55
+ OmegaConf 2.3 is the current stable version.
56
+ * [What's new](https://github.com/omry/omegaconf/releases/tag/v2.3.0)
57
+ * [Documentation](https://omegaconf.readthedocs.io/en/2.3_branch/)
58
+ * [Source code](https://github.com/omry/omegaconf/tree/2.3_branch)
59
+
60
+ Install with `pip install --upgrade omegaconf`
61
+
62
+ ## Live tutorial
63
+ Run the live tutorial: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)
@@ -0,0 +1,32 @@
1
+ # OmegaConf
2
+ | | Description |
3
+ | --- | --- |
4
+ | Project | [![PyPI version](https://badge.fury.io/py/omegaconf.svg)](https://badge.fury.io/py/omegaconf)[![Downloads](https://pepy.tech/badge/omegaconf/month)](https://pepy.tech/project/omegaconf)![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12%20%7C%203.13%20%7C%203.14-blue) |
5
+ | Code quality| [![CircleCI](https://img.shields.io/circleci/build/github/omry/omegaconf?logo=s&token=5de2f8dc2a0dd78438520575431aa533150806e3)](https://circleci.com/gh/omry/omegaconf)[![Coverage Status](https://coveralls.io/repos/github/omry/omegaconf/badge.svg)](https://coveralls.io/github/omry/omegaconf)|
6
+ | Docs and support |[![Documentation Status](https://readthedocs.org/projects/omegaconf/badge/?version=2.0_branch)](https://omegaconf.readthedocs.io/en/2.1_branch/)[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)|
7
+
8
+
9
+ OmegaConf is a hierarchical configuration system, with support for merging configurations from multiple sources (YAML config files, dataclasses/objects and CLI arguments)
10
+ providing a consistent API regardless of how the configuration was created.
11
+
12
+ ## Optional subprojects
13
+
14
+ - [`omegaconf-pydevd`](./subprojects/omegaconf-pydevd/README.md): optional `pydevd` debugger plugin for inspecting OmegaConf objects in supported debuggers.
15
+
16
+ ## Releases
17
+
18
+ ### Upcoming (2.4.0.dev)
19
+ OmegaConf 2.4.0.dev is the upcoming development version.
20
+ * [Documentation](https://omegaconf.readthedocs.io/en/latest/)
21
+ * [Source code](https://github.com/omry/omegaconf/tree/master)
22
+
23
+ ### Stable (2.3)
24
+ OmegaConf 2.3 is the current stable version.
25
+ * [What's new](https://github.com/omry/omegaconf/releases/tag/v2.3.0)
26
+ * [Documentation](https://omegaconf.readthedocs.io/en/2.3_branch/)
27
+ * [Source code](https://github.com/omry/omegaconf/tree/2.3_branch)
28
+
29
+ Install with `pip install --upgrade omegaconf`
30
+
31
+ ## Live tutorial
32
+ Run the live tutorial: [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)
@@ -1,4 +1,3 @@
1
- import codecs
2
1
  import distutils.log
3
2
  import errno
4
3
  import os
@@ -64,26 +63,26 @@ class ANTLRCommand(Command): # type: ignore # pragma: no cover
64
63
  build_dir = Path(__file__).parent.absolute()
65
64
  project_root = build_dir.parent
66
65
  lib = "antlr4"
67
- pkgname = 'omegaconf.vendor'
66
+ pkgname = "omegaconf.vendor"
68
67
 
69
68
  replacements = [
70
69
  partial( # import antlr4 -> import omegaconf.vendor.antlr4
71
- re.compile(r'(^\s*)import {}\n'.format(lib), flags=re.M).sub,
72
- r'\1from {} import {}\n'.format(pkgname, lib)
70
+ re.compile(r"(^\s*)import {}\n".format(lib), flags=re.M).sub,
71
+ r"\1from {} import {}\n".format(pkgname, lib),
73
72
  ),
74
73
  partial( # from antlr4 -> from fomegaconf.vendor.antlr4
75
- re.compile(r'(^\s*)from {}(\.|\s+)'.format(lib), flags=re.M).sub,
76
- r'\1from {}.{}\2'.format(pkgname, lib)
74
+ re.compile(r"(^\s*)from {}(\.|\s+)".format(lib), flags=re.M).sub,
75
+ r"\1from {}.{}\2".format(pkgname, lib),
77
76
  ),
78
77
  ]
79
78
 
80
79
  path = project_root / "omegaconf" / "grammar" / "gen"
81
80
  for item in path.iterdir():
82
81
  if item.is_file() and item.name.endswith(".py"):
83
- text = item.read_text('utf8')
82
+ text = item.read_text("utf8")
84
83
  for replacement in replacements:
85
84
  text = replacement(text)
86
- item.write_text(text, 'utf8')
85
+ item.write_text(text, "utf8")
87
86
 
88
87
 
89
88
  class BuildPyCommand(build_py.build_py): # pragma: no cover
@@ -148,7 +147,7 @@ class DevelopCommand(develop.develop): # pragma: no cover
148
147
 
149
148
  class SDistCommand(sdist.sdist): # pragma: no cover
150
149
  def run(self) -> None:
151
- if not self.dry_run: # type: ignore
150
+ if not self.dry_run: # type: ignore[attr-defined]
152
151
  self.run_command("clean")
153
152
  run_antlr(self)
154
153
  sdist.sdist.run(self)
@@ -196,7 +195,7 @@ def find(
196
195
 
197
196
  def find_version(*file_paths: str) -> str:
198
197
  root = Path(__file__).parent.parent.absolute()
199
- with codecs.open(root / Path(*file_paths), "r") as fp: # type: ignore
198
+ with open(root / Path(*file_paths), "r", encoding="utf-8") as fp:
200
199
  version_file = fp.read()
201
200
  version_match = re.search(r"^__version__ = ['\"]([^'\"]*)['\"]", version_file, re.M)
202
201
  if version_match:
@@ -114,6 +114,7 @@ _DEFAULT_MARKER_: Any = Marker("_DEFAULT_MARKER_")
114
114
 
115
115
  class OmegaConfDumper(BaseDumper): # type: ignore
116
116
  str_representer_added = False
117
+ pathlib_representers_added = False
117
118
 
118
119
  @staticmethod
119
120
  def str_representer(dumper: yaml.Dumper, data: str) -> yaml.ScalarNode:
@@ -124,11 +125,51 @@ class OmegaConfDumper(BaseDumper): # type: ignore
124
125
  style=("'" if with_quotes else None),
125
126
  )
126
127
 
128
+ @staticmethod
129
+ def pathlib_path_representer(dumper: yaml.Dumper, data: Any) -> yaml.Node:
130
+ # Use old pathlib.Path tag for cross-version compatibility
131
+ # Extract constructor args from __reduce__ and use sequence representation
132
+ return dumper.represent_sequence( # pragma: no cover
133
+ "tag:yaml.org,2002:python/object/apply:pathlib.Path",
134
+ [str(data)],
135
+ )
136
+
137
+ @staticmethod
138
+ def pathlib_posix_path_representer(dumper: yaml.Dumper, data: Any) -> yaml.Node:
139
+ # Use old pathlib.PosixPath tag for cross-version compatibility
140
+ return dumper.represent_sequence(
141
+ "tag:yaml.org,2002:python/object/apply:pathlib.PosixPath",
142
+ [str(data)],
143
+ )
144
+
145
+ @staticmethod
146
+ def pathlib_windows_path_representer(dumper: yaml.Dumper, data: Any) -> yaml.Node:
147
+ # Use old pathlib.WindowsPath tag for cross-version compatibility
148
+ return dumper.represent_sequence( # pragma: no cover
149
+ "tag:yaml.org,2002:python/object/apply:pathlib.WindowsPath",
150
+ [str(data)],
151
+ )
152
+
127
153
 
128
154
  def get_omega_conf_dumper() -> Type[OmegaConfDumper]:
129
155
  if not OmegaConfDumper.str_representer_added:
130
156
  OmegaConfDumper.add_representer(str, OmegaConfDumper.str_representer)
131
157
  OmegaConfDumper.str_representer_added = True
158
+
159
+ # Add representers for pathlib types to ensure cross-version compatibility.
160
+ # Python 3.13+ uses pathlib._local.* internally, so we normalize to old pathlib.* tags
161
+ if not OmegaConfDumper.pathlib_representers_added:
162
+ from pathlib import Path, PosixPath, WindowsPath
163
+
164
+ OmegaConfDumper.add_representer(Path, OmegaConfDumper.pathlib_path_representer)
165
+ OmegaConfDumper.add_representer(
166
+ PosixPath, OmegaConfDumper.pathlib_posix_path_representer
167
+ )
168
+ OmegaConfDumper.add_representer(
169
+ WindowsPath, OmegaConfDumper.pathlib_windows_path_representer
170
+ )
171
+ OmegaConfDumper.pathlib_representers_added = True
172
+
132
173
  return OmegaConfDumper
133
174
 
134
175
 
@@ -138,19 +179,72 @@ def yaml_is_bool(b: str) -> bool:
138
179
 
139
180
  def get_yaml_loader() -> Any:
140
181
  class OmegaConfLoader(BaseLoader): # type: ignore
141
- def construct_mapping(self, node: yaml.Node, deep: bool = False) -> Any:
142
- keys = set()
143
- for key_node, value_node in node.value:
182
+ def flatten_mapping(self, node: yaml.Node) -> Any:
183
+ merge_tag = "tag:yaml.org,2002:merge"
184
+ explicit_keys = set()
185
+ for key_node, _ in node.value:
186
+ if key_node.tag == merge_tag:
187
+ continue
144
188
  if key_node.tag != yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG:
145
189
  continue
146
- if key_node.value in keys:
190
+ if key_node.value in explicit_keys:
147
191
  raise yaml.constructor.ConstructorError(
148
192
  "while constructing a mapping",
149
193
  node.start_mark,
150
194
  f"found duplicate key {key_node.value}",
151
195
  key_node.start_mark,
152
196
  )
153
- keys.add(key_node.value)
197
+ explicit_keys.add(key_node.value)
198
+
199
+ merge = []
200
+ index = 0
201
+ while index < len(node.value):
202
+ key_node, value_node = node.value[index]
203
+ if key_node.tag == merge_tag:
204
+ del node.value[index]
205
+ if isinstance(value_node, yaml.MappingNode):
206
+ self.flatten_mapping(value_node)
207
+ merge.extend(value_node.value)
208
+ elif isinstance(value_node, yaml.SequenceNode):
209
+ submerge = []
210
+ for subnode in value_node.value:
211
+ if not isinstance(subnode, yaml.MappingNode):
212
+ raise yaml.constructor.ConstructorError(
213
+ "while constructing a mapping",
214
+ node.start_mark,
215
+ "expected a mapping for merging, but found "
216
+ f"{subnode.id}",
217
+ subnode.start_mark,
218
+ )
219
+ self.flatten_mapping(subnode)
220
+ submerge.append(subnode.value)
221
+ submerge.reverse()
222
+ for value in submerge:
223
+ merge.extend(value)
224
+ else:
225
+ raise yaml.constructor.ConstructorError(
226
+ "while constructing a mapping",
227
+ node.start_mark,
228
+ "expected a mapping or list of mappings for merging, "
229
+ f"but found {value_node.id}",
230
+ value_node.start_mark,
231
+ )
232
+ elif key_node.tag == "tag:yaml.org,2002:value":
233
+ key_node.tag = yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG
234
+ index += 1
235
+ else:
236
+ index += 1
237
+
238
+ if merge:
239
+ merge = [
240
+ (key_node, value_node)
241
+ for key_node, value_node in merge
242
+ if key_node.tag != yaml.resolver.BaseResolver.DEFAULT_SCALAR_TAG
243
+ or key_node.value not in explicit_keys
244
+ ]
245
+ node.value = merge + node.value
246
+
247
+ def construct_mapping(self, node: yaml.Node, deep: bool = False) -> Any:
154
248
  return super().construct_mapping(node, deep=deep)
155
249
 
156
250
  loader = OmegaConfLoader
@@ -190,6 +284,20 @@ def get_yaml_loader() -> Any:
190
284
  lambda loader, node: pathlib.WindowsPath(*loader.construct_sequence(node)),
191
285
  )
192
286
 
287
+ # Python 3.13+ uses internal pathlib._local module
288
+ loader.add_constructor(
289
+ "tag:yaml.org,2002:python/object/apply:pathlib._local.Path",
290
+ lambda loader, node: pathlib.Path(*loader.construct_sequence(node)),
291
+ )
292
+ loader.add_constructor(
293
+ "tag:yaml.org,2002:python/object/apply:pathlib._local.PosixPath",
294
+ lambda loader, node: pathlib.PosixPath(*loader.construct_sequence(node)),
295
+ )
296
+ loader.add_constructor(
297
+ "tag:yaml.org,2002:python/object/apply:pathlib._local.WindowsPath",
298
+ lambda loader, node: pathlib.WindowsPath(*loader.construct_sequence(node)),
299
+ )
300
+
193
301
  return loader
194
302
 
195
303
 
@@ -350,7 +458,7 @@ def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, A
350
458
  value = MISSING
351
459
  if is_union_annotation(type_) and not is_supported_union_annotation(type_):
352
460
  e = ConfigValueError(
353
- f"Unions of containers are not supported:\n{name}: {type_str(type_)}"
461
+ f"Unions of containers are not supported:\n{name}: {type_str(type_)}" # noqa: E231
354
462
  )
355
463
  format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e))
356
464
 
@@ -409,7 +517,7 @@ def get_dataclass_data(
409
517
 
410
518
  if is_union_annotation(type_) and not is_supported_union_annotation(type_):
411
519
  e = ConfigValueError(
412
- f"Unions of containers are not supported:\n{name}: {type_str(type_)}"
520
+ f"Unions of containers are not supported:\n{name}: {type_str(type_)}" # noqa: E231
413
521
  )
414
522
  format_and_raise(node=None, key=None, value=value, cause=e, msg=str(e))
415
523
  try:
@@ -864,20 +972,16 @@ def format_and_raise(
864
972
  )
865
973
 
866
974
  if ref_type not in (None, Any):
867
- template = dedent(
868
- """\
975
+ template = dedent("""\
869
976
  $MSG
870
977
  full_key: $FULL_KEY
871
978
  reference_type=$REF_TYPE
872
- object_type=$OBJECT_TYPE"""
873
- )
979
+ object_type=$OBJECT_TYPE""")
874
980
  else:
875
- template = dedent(
876
- """\
981
+ template = dedent("""\
877
982
  $MSG
878
983
  full_key: $FULL_KEY
879
- object_type=$OBJECT_TYPE"""
880
- )
984
+ object_type=$OBJECT_TYPE""")
881
985
  s = string.Template(template=template)
882
986
 
883
987
  message = s.substitute(
@@ -941,7 +1045,14 @@ def type_str(t: Any, include_module_name: bool = False) -> str:
941
1045
  and t.__module__ != "typing"
942
1046
  and not t.__module__.startswith("omegaconf.")
943
1047
  ):
944
- module_prefix = str(t.__module__) + "."
1048
+ module_name = str(t.__module__)
1049
+ if isinstance(t, type) and issubclass(t, pathlib.PurePath):
1050
+ # Python 3.13+ uses pathlib._local internally, normalize to pathlib for # Path types
1051
+ # Normalize pathlib._local to pathlib for cross-version compatibility
1052
+ if module_name == "pathlib._local": # pragma: no cover
1053
+ module_name = "pathlib"
1054
+
1055
+ module_prefix = module_name + "."
945
1056
  else:
946
1057
  module_prefix = ""
947
1058
  ret = module_prefix + ret
@@ -248,16 +248,19 @@ class Node(ABC):
248
248
  self,
249
249
  throw_on_resolution_failure: bool = False,
250
250
  memo: Optional[Set[int]] = None,
251
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
251
252
  ) -> Optional["Node"]:
252
253
  return self._dereference_node_impl(
253
254
  throw_on_resolution_failure=throw_on_resolution_failure,
254
255
  memo=memo,
256
+ resolved_node_cache=resolved_node_cache,
255
257
  )
256
258
 
257
259
  def _dereference_node_impl(
258
260
  self,
259
261
  throw_on_resolution_failure: bool,
260
262
  memo: Optional[Set[int]] = None,
263
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
261
264
  ) -> Optional["Node"]:
262
265
  if not self._is_interpolation():
263
266
  return self
@@ -278,6 +281,7 @@ class Node(ABC):
278
281
  parse_tree=parse(_get_value(self)),
279
282
  throw_on_resolution_failure=throw_on_resolution_failure,
280
283
  memo=memo,
284
+ resolved_node_cache=resolved_node_cache,
281
285
  )
282
286
 
283
287
  def _get_root(self) -> "Container":
@@ -456,6 +460,7 @@ class Container(Box):
456
460
  throw_on_missing: bool,
457
461
  throw_on_resolution_failure: bool,
458
462
  memo: Optional[Set[int]] = None,
463
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
459
464
  ) -> Tuple[Optional["Container"], Optional[str], Optional[Node]]:
460
465
  """
461
466
  Select a value using dot separated key sequence
@@ -482,6 +487,7 @@ class Container(Box):
482
487
  ret = ret._maybe_dereference_node(
483
488
  throw_on_resolution_failure=throw_on_resolution_failure,
484
489
  memo=memo,
490
+ resolved_node_cache=resolved_node_cache,
485
491
  )
486
492
 
487
493
  if ret is not None and not isinstance(ret, Container):
@@ -506,12 +512,15 @@ class Container(Box):
506
512
  if value is None:
507
513
  return root, last_key, None
508
514
 
515
+ value_id = id(value)
516
+ if resolved_node_cache is not None and value_id in resolved_node_cache:
517
+ return root, last_key, resolved_node_cache[value_id]
518
+
509
519
  if memo is not None:
510
- vid = id(value)
511
- if vid in memo:
520
+ if value_id in memo:
512
521
  raise InterpolationResolutionError("Recursive interpolation detected")
513
522
  # push to memo "stack"
514
- memo.add(vid)
523
+ memo.add(value_id)
515
524
 
516
525
  try:
517
526
  value = root._maybe_resolve_interpolation(
@@ -520,11 +529,15 @@ class Container(Box):
520
529
  value=value,
521
530
  throw_on_resolution_failure=throw_on_resolution_failure,
522
531
  memo=memo,
532
+ resolved_node_cache=resolved_node_cache,
523
533
  )
524
534
  finally:
525
535
  if memo is not None:
526
536
  # pop from memo "stack"
527
- memo.remove(vid)
537
+ memo.remove(value_id)
538
+
539
+ if resolved_node_cache is not None and value is not None:
540
+ resolved_node_cache[value_id] = value
528
541
 
529
542
  return root, last_key, value
530
543
 
@@ -536,6 +549,7 @@ class Container(Box):
536
549
  parse_tree: OmegaConfGrammarParser.ConfigValueContext,
537
550
  throw_on_resolution_failure: bool,
538
551
  memo: Optional[Set[int]],
552
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
539
553
  ) -> Optional["Node"]:
540
554
  """
541
555
  Resolve an interpolation.
@@ -568,7 +582,11 @@ class Container(Box):
568
582
 
569
583
  try:
570
584
  resolved = self.resolve_parse_tree(
571
- parse_tree=parse_tree, node=value, key=key, memo=memo
585
+ parse_tree=parse_tree,
586
+ node=value,
587
+ key=key,
588
+ memo=memo,
589
+ resolved_node_cache=resolved_node_cache,
572
590
  )
573
591
  except InterpolationResolutionError:
574
592
  if throw_on_resolution_failure:
@@ -636,7 +654,10 @@ class Container(Box):
636
654
  parent = parent._get_parent()
637
655
 
638
656
  def _resolve_node_interpolation(
639
- self, inter_key: str, memo: Optional[Set[int]]
657
+ self,
658
+ inter_key: str,
659
+ memo: Optional[Set[int]],
660
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
640
661
  ) -> "Node":
641
662
  """A node interpolation is of the form `${foo.bar}`"""
642
663
  try:
@@ -652,6 +673,7 @@ class Container(Box):
652
673
  throw_on_missing=True,
653
674
  throw_on_resolution_failure=True,
654
675
  memo=memo,
676
+ resolved_node_cache=resolved_node_cache,
655
677
  )
656
678
  except MissingMandatoryValue as exc:
657
679
  raise InterpolationToMissingValueError(
@@ -696,6 +718,7 @@ class Container(Box):
696
718
  value: Node,
697
719
  throw_on_resolution_failure: bool,
698
720
  memo: Optional[Set[int]] = None,
721
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
699
722
  ) -> Optional[Node]:
700
723
  value_kind = get_value_kind(value)
701
724
  if value_kind != ValueKind.INTERPOLATION:
@@ -709,6 +732,7 @@ class Container(Box):
709
732
  parse_tree=parse_tree,
710
733
  throw_on_resolution_failure=throw_on_resolution_failure,
711
734
  memo=memo if memo is not None else set(),
735
+ resolved_node_cache=resolved_node_cache,
712
736
  )
713
737
 
714
738
  def resolve_parse_tree(
@@ -717,6 +741,7 @@ class Container(Box):
717
741
  node: Node,
718
742
  memo: Optional[Set[int]] = None,
719
743
  key: Optional[Any] = None,
744
+ resolved_node_cache: Optional[Dict[int, "Node"]] = None,
720
745
  ) -> Any:
721
746
  """
722
747
  Resolve a given parse tree into its value.
@@ -728,7 +753,11 @@ class Container(Box):
728
753
  def node_interpolation_callback(
729
754
  inter_key: str, memo: Optional[Set[int]]
730
755
  ) -> Optional["Node"]:
731
- return self._resolve_node_interpolation(inter_key=inter_key, memo=memo)
756
+ return self._resolve_node_interpolation(
757
+ inter_key=inter_key,
758
+ memo=memo,
759
+ resolved_node_cache=resolved_node_cache,
760
+ )
732
761
 
733
762
  def resolver_interpolation_callback(
734
763
  name: str, args: Tuple[Any, ...], args_str: Tuple[str, ...]
@@ -214,9 +214,13 @@ class BaseContainer(Container, ABC):
214
214
  throw_on_missing: bool,
215
215
  enum_to_str: bool = False,
216
216
  structured_config_mode: SCMode = SCMode.DICT,
217
+ resolved_node_cache: Optional[Dict[int, Node]] = None,
217
218
  ) -> Union[None, Any, str, Dict[DictKeyType, Any], List[Any]]:
218
219
  from omegaconf import MISSING, DictConfig, ListConfig
219
220
 
221
+ if resolve and resolved_node_cache is None:
222
+ resolved_node_cache = {}
223
+
220
224
  def convert(val: Node) -> Any:
221
225
  value = val._value()
222
226
  if enum_to_str and isinstance(value, Enum):
@@ -230,11 +234,26 @@ class BaseContainer(Container, ABC):
230
234
  except MissingMandatoryValue as e:
231
235
  conf._format_and_raise(key=key, value=None, cause=e)
232
236
  assert isinstance(node, Node)
237
+ node_id = id(node)
233
238
  if resolve:
234
- try:
235
- node = node._dereference_node()
236
- except InterpolationResolutionError as e:
237
- conf._format_and_raise(key=key, value=None, cause=e)
239
+ cached = (
240
+ resolved_node_cache.get(node_id)
241
+ if resolved_node_cache is not None
242
+ else None
243
+ )
244
+ if cached is not None:
245
+ node = cached
246
+ else:
247
+ try:
248
+ node = node._maybe_dereference_node(
249
+ throw_on_resolution_failure=True,
250
+ resolved_node_cache=resolved_node_cache,
251
+ )
252
+ except InterpolationResolutionError as e:
253
+ conf._format_and_raise(key=key, value=None, cause=e)
254
+ assert node is not None
255
+ if resolved_node_cache is not None:
256
+ resolved_node_cache[node_id] = node
238
257
 
239
258
  if isinstance(node, Container):
240
259
  value = BaseContainer._to_content(
@@ -243,6 +262,7 @@ class BaseContainer(Container, ABC):
243
262
  throw_on_missing=throw_on_missing,
244
263
  enum_to_str=enum_to_str,
245
264
  structured_config_mode=structured_config_mode,
265
+ resolved_node_cache=resolved_node_cache,
246
266
  )
247
267
  else:
248
268
  value = convert(node)
@@ -738,9 +758,9 @@ class BaseContainer(Container, ABC):
738
758
 
739
759
  def _slice_to_str(x: slice) -> str:
740
760
  if x.step is not None:
741
- return f"{x.start}:{x.stop}:{x.step}"
761
+ return f"{x.start}:{x.stop}:{x.step}" # noqa: E231
742
762
  else:
743
- return f"{x.start}:{x.stop}"
763
+ return f"{x.start}:{x.stop}" # noqa: E231
744
764
 
745
765
  def prepand(
746
766
  full_key: str,
@@ -1,4 +1,4 @@
1
- # Generated from /home/homestar/dev/omegaconf/omegaconf/grammar/OmegaConfGrammarLexer.g4 by ANTLR 4.11.1
1
+ # Generated from /home/runner/work/omegaconf/omegaconf/omegaconf/grammar/OmegaConfGrammarLexer.g4 by ANTLR 4.11.1
2
2
  from omegaconf.vendor.antlr4 import *
3
3
  from io import StringIO
4
4
  import sys
@@ -1,4 +1,4 @@
1
- # Generated from /home/homestar/dev/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
1
+ # Generated from /home/runner/work/omegaconf/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
2
2
  # encoding: utf-8
3
3
  from omegaconf.vendor.antlr4 import *
4
4
  from io import StringIO
@@ -1,4 +1,4 @@
1
- # Generated from /home/homestar/dev/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
1
+ # Generated from /home/runner/work/omegaconf/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
2
2
  from omegaconf.vendor.antlr4 import *
3
3
  if __name__ is not None and "." in __name__:
4
4
  from .OmegaConfGrammarParser import OmegaConfGrammarParser
@@ -1,4 +1,4 @@
1
- # Generated from /home/homestar/dev/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
1
+ # Generated from /home/runner/work/omegaconf/omegaconf/omegaconf/grammar/OmegaConfGrammarParser.g4 by ANTLR 4.11.1
2
2
  from omegaconf.vendor.antlr4 import *
3
3
  if __name__ is not None and "." in __name__:
4
4
  from .OmegaConfGrammarParser import OmegaConfGrammarParser
@@ -28,8 +28,10 @@ _node_inter = f"\\${{\\s*{_node_path}\\s*}}" # node interpolation ${foo.bar}
28
28
  _id = "[a-zA-Z_][\\w\\-]*" # foo, foo_bar, foo-bar, abc123
29
29
  _resolver_name = f"({_id}(\\.{_id})*)?" # foo, ns.bar3, ns_1.ns_2.b0z
30
30
  _arg = r"[a-zA-Z_0-9/\-\+.$%*@?|]+" # string representing a resolver argument
31
- _args = f"{_arg}(\\s*,\\s*{_arg})*" # list of resolver arguments
32
- _resolver_inter = f"\\${{\\s*{_resolver_name}\\s*:\\s*{_args}?\\s*}}" # ${foo:bar}
31
+ _args = f"{_arg}(\\s*,\\s*{_arg})*" # list of resolver arguments # noqa: E231
32
+ _resolver_inter = (
33
+ f"\\${{\\s*{_resolver_name}\\s*:\\s*{_args}?\\s*}}" # ${foo:bar} # noqa: E231
34
+ )
33
35
  _inter = f"({_node_inter}|{_resolver_inter})" # any kind of interpolation
34
36
  _outer = "([^$]|\\$(?!{))+" # any character except $ (unless not followed by {)
35
37
  SIMPLE_INTERPOLATION_PATTERN = re.compile(
@@ -251,7 +251,7 @@ class GrammarVisitor(OmegaConfGrammarParserVisitor):
251
251
  warnings.warn(
252
252
  f"In the sequence `{txt}` some elements are missing: please replace "
253
253
  f"them with empty quoted strings. "
254
- f"See https://github.com/omry/omegaconf/issues/572 for details.",
254
+ f"See https://github.com/omry/omegaconf/issues/572 for details.", # noqa: E231
255
255
  category=UserWarning,
256
256
  )
257
257