omegaconf 2.4.0.dev11__tar.gz → 2.4.0.dev13__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.
- {omegaconf-2.4.0.dev11/omegaconf.egg-info → omegaconf-2.4.0.dev13}/PKG-INFO +2 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/README.md +1 -1
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/__init__.py +2 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/_impl.py +20 -4
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/_utils.py +75 -11
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/base.py +112 -38
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/basecontainer.py +134 -32
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/dictconfig.py +0 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/listconfig.py +31 -5
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/omegaconf.py +224 -102
- omegaconf-2.4.0.dev13/omegaconf/tupleconfig.py +522 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/version.py +2 -1
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13/omegaconf.egg-info}/PKG-INFO +2 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/SOURCES.txt +5 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/pyproject.toml +14 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/setup.cfg +1 -1
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/conftest.py +2 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_create.py +2 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_dict.py +3 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/test_custom_resolvers.py +51 -48
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/test_interpolation.py +3 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/test_structured_config.py +69 -9
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_base_config.py +1 -1
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_basic_ops_list.py +56 -4
- omegaconf-2.4.0.dev13/tests/test_basic_ops_tuple.py +411 -0
- omegaconf-2.4.0.dev13/tests/test_config_eq.py +429 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_container_unions.py +86 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_create.py +46 -2
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_errors.py +134 -6
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_grammar.py +6 -6
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_matrix.py +11 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_merge.py +113 -8
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_nested_containers.py +20 -8
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_omegaconf.py +6 -6
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_select.py +11 -3
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_to_container.py +1 -1
- omegaconf-2.4.0.dev13/tests/test_tuple_integration.py +186 -0
- omegaconf-2.4.0.dev13/tests/test_tuple_merge.py +178 -0
- omegaconf-2.4.0.dev13/tests/test_tuple_structured.py +205 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_utils.py +4 -1
- omegaconf-2.4.0.dev11/tests/test_config_eq.py +0 -171
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/LICENSE +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/MANIFEST.in +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/build_helpers/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/build_helpers/bin/antlr-4.11.1-complete.jar +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/build_helpers/build_helpers.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/build_helpers/get_vendored.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/build_helpers/test_helpers.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/_yaml.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/errors.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/OmegaConfGrammarLexer.g4 +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/OmegaConfGrammarParser.g4 +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarLexer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParser.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParserListener.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParserVisitor.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar_parser.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/grammar_visitor.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/nodes.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/py.typed +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/oc/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/oc/dict.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/typing.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/BufferedTokenStream.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/CommonTokenFactory.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/CommonTokenStream.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/FileStream.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/InputStream.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/IntervalSet.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/LL1Analyzer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Lexer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ListTokenSource.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Parser.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ParserInterpreter.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ParserRuleContext.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/PredictionContext.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Recognizer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/RuleContext.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/StdinStream.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Token.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/TokenStreamRewriter.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Utils.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/_pygrun.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATN.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNConfig.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNConfigSet.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNDeserializer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNState.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNType.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerAction.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/PredictionMode.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/SemanticContext.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/Transition.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFA.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFASerializer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFAState.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/ErrorListener.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/ErrorStrategy.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/Errors.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Chunk.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreePattern.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/RuleTagToken.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/TokenTagToken.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Tree.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Trees.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/XPath.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/XPathLexer.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/dependency_links.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/requires.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/top_level.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/requirements/base.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/requirements/dev.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/requirements/docs.txt +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/setup.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/data/2.0.6.pickle +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/data/2.1.0.rc1.pickle +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/data/load.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/data/save.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/examples/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/examples/dataclass_postponed_annotations.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/examples/test_dataclass_example.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/examples/test_postponed_annotations.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_decode.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_deprecated.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_env.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_select.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/__init__.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/attr_classes.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/dataclasses.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/dataclasses_pre_311.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/structured_conf/test_structured_basic.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_basic_ops_dict.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_compare_dictconfig_vs_dict.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_get_full_key.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_nodes.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_readonly.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_serialization.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_struct.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_to_yaml.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_unions.py +0 -0
- {omegaconf-2.4.0.dev11 → omegaconf-2.4.0.dev13}/tests/test_update.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omegaconf
|
|
3
|
-
Version: 2.4.0.
|
|
3
|
+
Version: 2.4.0.dev13
|
|
4
4
|
Summary: A flexible configuration library
|
|
5
5
|
Home-page: https://github.com/omry/omegaconf
|
|
6
6
|
Author: Omry Yadan
|
|
@@ -57,7 +57,7 @@ Dynamic: summary
|
|
|
57
57
|
| Project | [](https://badge.fury.io/py/omegaconf)[](https://pepy.tech/project/omegaconf) |
|
|
58
58
|
| Code quality| [](https://dl.circleci.com/status-badge/redirect/gh/omry/omegaconf/tree/main)[](https://coveralls.io/github/omry/omegaconf)|
|
|
59
59
|
| Docs and support |[](https://omegaconf.readthedocs.io/en/2.3_branch/)[](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)[](https://hydra-framework.zulipchat.com/)|
|
|
60
|
-
| Backlog | [
|
|
60
|
+
| Backlog | [](https://omry.github.io/backlog-atlas/) |
|
|
61
61
|
|
|
62
62
|
|
|
63
63
|
OmegaConf is a hierarchical configuration system, with support for merging configurations from multiple sources (YAML config files, dataclasses/objects and CLI arguments)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
| Project | [](https://badge.fury.io/py/omegaconf)[](https://pepy.tech/project/omegaconf) |
|
|
5
5
|
| Code quality| [](https://dl.circleci.com/status-badge/redirect/gh/omry/omegaconf/tree/main)[](https://coveralls.io/github/omry/omegaconf)|
|
|
6
6
|
| Docs and support |[](https://omegaconf.readthedocs.io/en/2.3_branch/)[](https://mybinder.org/v2/gh/omry/omegaconf/master?filepath=docs%2Fnotebook%2FTutorial.ipynb)[](https://hydra-framework.zulipchat.com/)|
|
|
7
|
-
| Backlog | [
|
|
7
|
+
| Backlog | [](https://omry.github.io/backlog-atlas/) |
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
OmegaConf is a hierarchical configuration system, with support for merging configurations from multiple sources (YAML config files, dataclasses/objects and CLI arguments)
|
|
@@ -30,6 +30,7 @@ from .omegaconf import (
|
|
|
30
30
|
open_dict,
|
|
31
31
|
read_write,
|
|
32
32
|
)
|
|
33
|
+
from .tupleconfig import TupleConfig
|
|
33
34
|
from .typing import Antlr4ParserRuleContext
|
|
34
35
|
from .version import __version__
|
|
35
36
|
|
|
@@ -43,6 +44,7 @@ __all__ = [
|
|
|
43
44
|
"Container",
|
|
44
45
|
"UnionNode",
|
|
45
46
|
"ListConfig",
|
|
47
|
+
"TupleConfig",
|
|
46
48
|
"DictConfig",
|
|
47
49
|
"DictKeyType",
|
|
48
50
|
"OmegaConf",
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
from typing import Any
|
|
2
2
|
|
|
3
|
-
from omegaconf import Container, DictConfig, ListConfig, Node, ValueNode
|
|
4
|
-
from omegaconf.errors import
|
|
3
|
+
from omegaconf import Container, DictConfig, ListConfig, Node, TupleConfig, ValueNode
|
|
4
|
+
from omegaconf.errors import (
|
|
5
|
+
ConfigKeyError,
|
|
6
|
+
ConfigTypeError,
|
|
7
|
+
InterpolationToMissingValueError,
|
|
8
|
+
)
|
|
5
9
|
from omegaconf.nodes import InterpolationResultNode
|
|
6
10
|
|
|
7
11
|
from ._utils import (
|
|
8
12
|
_DEFAULT_MARKER_,
|
|
9
13
|
_ensure_container,
|
|
10
14
|
_get_value,
|
|
15
|
+
_is_missing_literal,
|
|
11
16
|
is_primitive_container,
|
|
12
17
|
is_structured_config,
|
|
13
18
|
)
|
|
@@ -26,8 +31,19 @@ def _resolve_container_value(cfg: Container, key: Any) -> None:
|
|
|
26
31
|
resolved_value
|
|
27
32
|
):
|
|
28
33
|
resolved = _ensure_container(resolved_value)
|
|
34
|
+
if isinstance(cfg, TupleConfig) and _is_missing_literal(_get_value(resolved)):
|
|
35
|
+
cfg._format_and_raise(
|
|
36
|
+
key=key,
|
|
37
|
+
value=_get_value(resolved),
|
|
38
|
+
cause=InterpolationToMissingValueError(
|
|
39
|
+
"TupleConfig interpolation resolved to a missing value"
|
|
40
|
+
),
|
|
41
|
+
)
|
|
29
42
|
if isinstance(resolved, Container) and isinstance(node, ValueNode):
|
|
30
|
-
cfg
|
|
43
|
+
if isinstance(cfg, TupleConfig):
|
|
44
|
+
cfg._set_item_for_resolution(key, resolved)
|
|
45
|
+
else:
|
|
46
|
+
cfg[key] = resolved
|
|
31
47
|
else:
|
|
32
48
|
node._set_value(_get_value(resolved))
|
|
33
49
|
else:
|
|
@@ -44,7 +60,7 @@ def _resolve(cfg: Node) -> Node:
|
|
|
44
60
|
for k in list(cfg.keys()):
|
|
45
61
|
_resolve_container_value(cfg, k)
|
|
46
62
|
|
|
47
|
-
elif isinstance(cfg, ListConfig):
|
|
63
|
+
elif isinstance(cfg, (ListConfig, TupleConfig)):
|
|
48
64
|
for i in range(len(cfg)):
|
|
49
65
|
_resolve_container_value(cfg, i)
|
|
50
66
|
|
|
@@ -131,6 +131,7 @@ _DEFAULT_MARKER_: Any = Marker("_DEFAULT_MARKER_")
|
|
|
131
131
|
class OmegaConfDumper(BaseDumper): # type: ignore
|
|
132
132
|
str_representer_added = False
|
|
133
133
|
pathlib_representers_added = False
|
|
134
|
+
tuple_representer_added = False
|
|
134
135
|
|
|
135
136
|
@staticmethod
|
|
136
137
|
def str_representer(dumper: yaml.Dumper, data: str) -> yaml.ScalarNode:
|
|
@@ -166,6 +167,12 @@ class OmegaConfDumper(BaseDumper): # type: ignore
|
|
|
166
167
|
[str(data)],
|
|
167
168
|
)
|
|
168
169
|
|
|
170
|
+
@staticmethod
|
|
171
|
+
def tuple_representer(dumper: yaml.Dumper, data: Any) -> yaml.Node:
|
|
172
|
+
return dumper.represent_sequence(
|
|
173
|
+
yaml.resolver.BaseResolver.DEFAULT_SEQUENCE_TAG, data
|
|
174
|
+
)
|
|
175
|
+
|
|
169
176
|
|
|
170
177
|
def get_omega_conf_dumper() -> Type[OmegaConfDumper]:
|
|
171
178
|
if not OmegaConfDumper.str_representer_added:
|
|
@@ -186,6 +193,10 @@ def get_omega_conf_dumper() -> Type[OmegaConfDumper]:
|
|
|
186
193
|
)
|
|
187
194
|
OmegaConfDumper.pathlib_representers_added = True
|
|
188
195
|
|
|
196
|
+
if not OmegaConfDumper.tuple_representer_added:
|
|
197
|
+
OmegaConfDumper.add_representer(tuple, OmegaConfDumper.tuple_representer)
|
|
198
|
+
OmegaConfDumper.tuple_representer_added = True
|
|
199
|
+
|
|
189
200
|
return OmegaConfDumper
|
|
190
201
|
|
|
191
202
|
|
|
@@ -340,6 +351,7 @@ def get_attr_class_fields(obj: Any) -> List["AttrAttribute[Any]"]:
|
|
|
340
351
|
|
|
341
352
|
|
|
342
353
|
def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, Any]:
|
|
354
|
+
from omegaconf.base import Node
|
|
343
355
|
from omegaconf.omegaconf import OmegaConf, _maybe_wrap
|
|
344
356
|
|
|
345
357
|
flags = {"allow_objects": allow_objects} if allow_objects is not None else {}
|
|
@@ -377,6 +389,8 @@ def get_attr_data(obj: Any, allow_objects: Optional[bool] = None) -> Dict[str, A
|
|
|
377
389
|
value = default.factory()
|
|
378
390
|
else:
|
|
379
391
|
value = default
|
|
392
|
+
if isinstance(value, Node):
|
|
393
|
+
value = copy.deepcopy(value)
|
|
380
394
|
if is_union_annotation(type_) and not is_supported_union_annotation(type_):
|
|
381
395
|
e = ConfigValueError(
|
|
382
396
|
f"Unions of containers are not supported:\n{name}: {type_str(type_)}" # noqa: E231
|
|
@@ -410,6 +424,7 @@ def get_dataclass_fields(obj: Any) -> List["dataclasses.Field[Any]"]:
|
|
|
410
424
|
def get_dataclass_data(
|
|
411
425
|
obj: Any, allow_objects: Optional[bool] = None
|
|
412
426
|
) -> Dict[str, Any]:
|
|
427
|
+
from omegaconf.base import Node
|
|
413
428
|
from omegaconf.omegaconf import MISSING, OmegaConf, _maybe_wrap
|
|
414
429
|
|
|
415
430
|
flags = {"allow_objects": allow_objects} if allow_objects is not None else {}
|
|
@@ -435,6 +450,8 @@ def get_dataclass_data(
|
|
|
435
450
|
value = field.default_factory() # type: ignore
|
|
436
451
|
else:
|
|
437
452
|
value = MISSING
|
|
453
|
+
if isinstance(value, Node):
|
|
454
|
+
value = copy.deepcopy(value)
|
|
438
455
|
|
|
439
456
|
if is_union_annotation(type_) and not is_supported_union_annotation(type_):
|
|
440
457
|
e = ConfigValueError(
|
|
@@ -736,18 +753,54 @@ def get_list_element_type(ref_type: Optional[Type[Any]]) -> Any:
|
|
|
736
753
|
|
|
737
754
|
def get_tuple_item_types(ref_type: Type[Any]) -> Tuple[Any, ...]:
|
|
738
755
|
args = getattr(ref_type, "__args__", None)
|
|
739
|
-
if
|
|
756
|
+
if ref_type in (tuple, Tuple) or args is None:
|
|
740
757
|
args = (Any, ...)
|
|
758
|
+
elif args == ((),):
|
|
759
|
+
args = ()
|
|
741
760
|
assert isinstance(args, tuple)
|
|
742
761
|
return args
|
|
743
762
|
|
|
744
763
|
|
|
764
|
+
def normalize_tuple_annotation(ref_type: Any) -> Any:
|
|
765
|
+
if not is_tuple_annotation(ref_type):
|
|
766
|
+
raise ValidationError(f"Unsupported tuple type: '{type_str(ref_type)}'")
|
|
767
|
+
|
|
768
|
+
if ref_type in (tuple, Tuple):
|
|
769
|
+
return Tuple[Any, ...]
|
|
770
|
+
|
|
771
|
+
args = get_tuple_item_types(ref_type)
|
|
772
|
+
if Ellipsis in args and not (len(args) == 2 and args[1] is Ellipsis):
|
|
773
|
+
raise ValidationError(f"Unsupported tuple type: '{type_str(ref_type)}'")
|
|
774
|
+
|
|
775
|
+
for item_type in args:
|
|
776
|
+
origin = getattr(item_type, "__origin__", None)
|
|
777
|
+
if (
|
|
778
|
+
getattr(origin, "__module__", None),
|
|
779
|
+
getattr(origin, "__qualname__", None),
|
|
780
|
+
) in (("typing", "Unpack"), ("typing_extensions", "Unpack")):
|
|
781
|
+
raise ValidationError(f"Unsupported tuple type: '{type_str(ref_type)}'")
|
|
782
|
+
|
|
783
|
+
return ref_type
|
|
784
|
+
|
|
785
|
+
|
|
786
|
+
def is_variadic_tuple_annotation(ref_type: Any) -> bool:
|
|
787
|
+
args = get_tuple_item_types(normalize_tuple_annotation(ref_type))
|
|
788
|
+
return len(args) == 2 and args[1] is Ellipsis
|
|
789
|
+
|
|
790
|
+
|
|
791
|
+
def make_tuple_annotation(item_types: Tuple[Any, ...]) -> Any:
|
|
792
|
+
return Tuple[item_types] # type: ignore[valid-type]
|
|
793
|
+
|
|
794
|
+
|
|
745
795
|
def get_dict_key_value_types(ref_type: Any) -> Tuple[Any, Any]:
|
|
746
796
|
args = getattr(ref_type, "__args__", None)
|
|
747
797
|
if args is None:
|
|
748
798
|
bases = getattr(ref_type, "__orig_bases__", None)
|
|
749
799
|
if bases is not None and len(bases) > 0:
|
|
750
|
-
|
|
800
|
+
for base in bases:
|
|
801
|
+
if is_dict_annotation(base):
|
|
802
|
+
args = getattr(base, "__args__", None)
|
|
803
|
+
break
|
|
751
804
|
|
|
752
805
|
key_type: Any
|
|
753
806
|
element_type: Any
|
|
@@ -755,7 +808,7 @@ def get_dict_key_value_types(ref_type: Any) -> Tuple[Any, Any]:
|
|
|
755
808
|
key_type = Any
|
|
756
809
|
element_type = Any
|
|
757
810
|
else:
|
|
758
|
-
if args is not None:
|
|
811
|
+
if args is not None and len(args) == 2:
|
|
759
812
|
key_type = args[0]
|
|
760
813
|
element_type = args[1]
|
|
761
814
|
else:
|
|
@@ -829,12 +882,16 @@ def get_type_hint(obj: Any, key: Any = None) -> Optional[Type[Any]]:
|
|
|
829
882
|
return Any # type: ignore
|
|
830
883
|
|
|
831
884
|
|
|
832
|
-
def
|
|
885
|
+
def _is_full_backtrace_enabled() -> bool:
|
|
833
886
|
# Set the environment variable OC_CAUSE=1 to get a stacktrace that includes the
|
|
834
887
|
# causing exception.
|
|
835
888
|
env_var = os.environ["OC_CAUSE"] if "OC_CAUSE" in os.environ else None
|
|
836
889
|
debugging = sys.gettrace() is not None
|
|
837
|
-
|
|
890
|
+
return (debugging and not env_var == "0") or (env_var == "1")
|
|
891
|
+
|
|
892
|
+
|
|
893
|
+
def _raise(ex: Exception, cause: Exception) -> None:
|
|
894
|
+
full_backtrace = _is_full_backtrace_enabled()
|
|
838
895
|
if full_backtrace:
|
|
839
896
|
# In the alias case (`cause is ex`), assigning `ex.__cause__ = cause`
|
|
840
897
|
# would create a self-reference cycle that pins `ex` (and transitively
|
|
@@ -845,9 +902,12 @@ def _raise(ex: Exception, cause: Exception) -> None:
|
|
|
845
902
|
else:
|
|
846
903
|
ex.__cause__ = None
|
|
847
904
|
try:
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
905
|
+
# Set env var OC_CAUSE=1 for full trace. In normal mode, avoid
|
|
906
|
+
# preserving the active traceback because it can retain caller frame
|
|
907
|
+
# locals until cyclic GC runs.
|
|
908
|
+
if full_backtrace:
|
|
909
|
+
raise ex.with_traceback(sys.exc_info()[2])
|
|
910
|
+
raise ex.with_traceback(None) from None
|
|
851
911
|
finally:
|
|
852
912
|
# Follow https://peps.python.org/pep-3110/ to break
|
|
853
913
|
# the exception reference cycle.
|
|
@@ -1027,14 +1087,14 @@ def _ensure_container(target: Any, flags: Optional[Dict[str, bool]] = None) -> A
|
|
|
1027
1087
|
from omegaconf import OmegaConf
|
|
1028
1088
|
|
|
1029
1089
|
if is_primitive_container(target):
|
|
1030
|
-
assert isinstance(target, (list, dict))
|
|
1090
|
+
assert isinstance(target, (list, tuple, dict))
|
|
1031
1091
|
target = OmegaConf.create(target, flags=flags)
|
|
1032
1092
|
elif is_structured_config(target):
|
|
1033
1093
|
target = OmegaConf.structured(target, flags=flags)
|
|
1034
1094
|
elif not OmegaConf.is_config(target):
|
|
1035
1095
|
raise ValueError(
|
|
1036
1096
|
"Invalid input. Supports one of "
|
|
1037
|
-
+ "[dict,list,DictConfig,ListConfig,dataclass,dataclass instance,attr class,attr class instance]"
|
|
1097
|
+
+ "[dict,list,tuple,DictConfig,ListConfig,TupleConfig,dataclass,dataclass instance,attr class,attr class instance]"
|
|
1038
1098
|
)
|
|
1039
1099
|
|
|
1040
1100
|
return target
|
|
@@ -1067,7 +1127,11 @@ def is_generic_dict(type_: Any) -> bool:
|
|
|
1067
1127
|
|
|
1068
1128
|
|
|
1069
1129
|
def is_container_annotation(type_: Any) -> bool:
|
|
1070
|
-
return
|
|
1130
|
+
return (
|
|
1131
|
+
is_list_annotation(type_)
|
|
1132
|
+
or is_tuple_annotation(type_)
|
|
1133
|
+
or is_dict_annotation(type_)
|
|
1134
|
+
)
|
|
1071
1135
|
|
|
1072
1136
|
|
|
1073
1137
|
# Characters recognised after a backslash in key paths and dotlist entries.
|
|
@@ -22,6 +22,7 @@ from ._utils import (
|
|
|
22
22
|
NoneType,
|
|
23
23
|
ValueKind,
|
|
24
24
|
_get_value,
|
|
25
|
+
_is_full_backtrace_enabled,
|
|
25
26
|
_is_interpolation,
|
|
26
27
|
_is_missing_value,
|
|
27
28
|
_is_special,
|
|
@@ -29,6 +30,7 @@ from ._utils import (
|
|
|
29
30
|
get_value_kind,
|
|
30
31
|
is_dict_annotation,
|
|
31
32
|
is_list_annotation,
|
|
33
|
+
is_tuple_annotation,
|
|
32
34
|
is_union_annotation,
|
|
33
35
|
is_valid_value_annotation,
|
|
34
36
|
split_key,
|
|
@@ -42,6 +44,7 @@ from .errors import (
|
|
|
42
44
|
InterpolationToMissingValueError,
|
|
43
45
|
InterpolationValidationError,
|
|
44
46
|
MissingMandatoryValue,
|
|
47
|
+
OmegaConfBaseException,
|
|
45
48
|
UnsupportedInterpolationType,
|
|
46
49
|
ValidationError,
|
|
47
50
|
)
|
|
@@ -238,6 +241,17 @@ class Node(ABC):
|
|
|
238
241
|
msg: Optional[str] = None,
|
|
239
242
|
type_override: Any = None,
|
|
240
243
|
) -> NoReturn:
|
|
244
|
+
if (
|
|
245
|
+
type_override is None
|
|
246
|
+
and isinstance(cause, OmegaConfBaseException)
|
|
247
|
+
and cause._initialized
|
|
248
|
+
):
|
|
249
|
+
if not _is_full_backtrace_enabled():
|
|
250
|
+
cause.__traceback__ = None
|
|
251
|
+
cause.__context__ = None
|
|
252
|
+
# Keep the re-raise in this common wrapper so callers do not need
|
|
253
|
+
# to remember traceback cleanup before forwarding OmegaConf errors.
|
|
254
|
+
raise
|
|
241
255
|
format_and_raise(
|
|
242
256
|
node=self,
|
|
243
257
|
key=key,
|
|
@@ -364,7 +378,7 @@ class Node(ABC):
|
|
|
364
378
|
class Box(Node):
|
|
365
379
|
"""
|
|
366
380
|
Base class for nodes that can contain other nodes.
|
|
367
|
-
Concrete subclasses include DictConfig, ListConfig, and UnionNode.
|
|
381
|
+
Concrete subclasses include DictConfig, ListConfig, TupleConfig, and UnionNode.
|
|
368
382
|
"""
|
|
369
383
|
|
|
370
384
|
_content: Any
|
|
@@ -380,6 +394,7 @@ class Box(Node):
|
|
|
380
394
|
def _re_parent(self) -> None:
|
|
381
395
|
from .dictconfig import DictConfig
|
|
382
396
|
from .listconfig import ListConfig
|
|
397
|
+
from .tupleconfig import TupleConfig
|
|
383
398
|
|
|
384
399
|
# update parents of first level Config nodes to self
|
|
385
400
|
|
|
@@ -391,7 +406,7 @@ class Box(Node):
|
|
|
391
406
|
value._set_parent(self)
|
|
392
407
|
if isinstance(value, Box):
|
|
393
408
|
value._re_parent()
|
|
394
|
-
elif isinstance(self, ListConfig):
|
|
409
|
+
elif isinstance(self, (ListConfig, TupleConfig)):
|
|
395
410
|
content = self.__dict__["_content"]
|
|
396
411
|
if isinstance(content, list):
|
|
397
412
|
for item in self.__dict__["_content"]:
|
|
@@ -690,6 +705,7 @@ class Container(Box):
|
|
|
690
705
|
resolved_node_cache: Optional[Dict[int, "Node"]] = None,
|
|
691
706
|
) -> "Node":
|
|
692
707
|
"""A node interpolation is of the form `${foo.bar}`"""
|
|
708
|
+
original_inter_key = inter_key
|
|
693
709
|
try:
|
|
694
710
|
root_node, inter_key = self._resolve_key_and_root(inter_key)
|
|
695
711
|
except ConfigKeyError as exc:
|
|
@@ -711,7 +727,19 @@ class Container(Box):
|
|
|
711
727
|
).with_traceback(sys.exc_info()[2])
|
|
712
728
|
|
|
713
729
|
if parent is None or value is None:
|
|
714
|
-
|
|
730
|
+
msg = f"Interpolation key '{inter_key}' not found"
|
|
731
|
+
if original_inter_key != inter_key:
|
|
732
|
+
try:
|
|
733
|
+
resolved_inter_key = root_node._get_full_key(inter_key)
|
|
734
|
+
except Exception as exc:
|
|
735
|
+
resolved_inter_key = (
|
|
736
|
+
f"<unresolvable due to {type(exc).__name__}: {exc}>"
|
|
737
|
+
)
|
|
738
|
+
msg = (
|
|
739
|
+
f"Interpolation key '{original_inter_key}' not found"
|
|
740
|
+
f" (resolved to '{resolved_inter_key}')"
|
|
741
|
+
)
|
|
742
|
+
raise InterpolationKeyError(msg)
|
|
715
743
|
else:
|
|
716
744
|
self._validate_not_dereferencing_to_parent(node=self, target=value)
|
|
717
745
|
return value
|
|
@@ -818,6 +846,7 @@ class Container(Box):
|
|
|
818
846
|
def _invalidate_flags_cache(self) -> None:
|
|
819
847
|
from .dictconfig import DictConfig
|
|
820
848
|
from .listconfig import ListConfig
|
|
849
|
+
from .tupleconfig import TupleConfig
|
|
821
850
|
|
|
822
851
|
# invalidate subtree cache only if the cache is initialized in this node.
|
|
823
852
|
|
|
@@ -828,7 +857,7 @@ class Container(Box):
|
|
|
828
857
|
if isinstance(content, dict):
|
|
829
858
|
for value in self.__dict__["_content"].values():
|
|
830
859
|
value._invalidate_flags_cache()
|
|
831
|
-
elif isinstance(self, ListConfig):
|
|
860
|
+
elif isinstance(self, (ListConfig, TupleConfig)):
|
|
832
861
|
content = self.__dict__["_content"]
|
|
833
862
|
if isinstance(content, list):
|
|
834
863
|
for item in self.__dict__["_content"]:
|
|
@@ -943,6 +972,7 @@ class UnionNode(Box):
|
|
|
943
972
|
) -> None:
|
|
944
973
|
from omegaconf.listconfig import ListConfig
|
|
945
974
|
from omegaconf.omegaconf import _node_wrap
|
|
975
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
946
976
|
|
|
947
977
|
ref_type = self._metadata.ref_type
|
|
948
978
|
type_hint = self._metadata.type_hint
|
|
@@ -956,12 +986,25 @@ class UnionNode(Box):
|
|
|
956
986
|
f"Value '$VALUE' is incompatible with type hint '{type_str(type_hint)}'"
|
|
957
987
|
)
|
|
958
988
|
self.__dict__["_content"] = value
|
|
959
|
-
elif isinstance(value, (list, tuple, ListConfig)):
|
|
960
|
-
|
|
961
|
-
|
|
989
|
+
elif isinstance(value, (list, tuple, ListConfig, TupleConfig)):
|
|
990
|
+
sequence_candidates = [
|
|
991
|
+
t
|
|
992
|
+
for t in ref_type.__args__
|
|
993
|
+
if is_list_annotation(t) or is_tuple_annotation(t)
|
|
994
|
+
]
|
|
995
|
+
input_is_tuple = isinstance(value, (tuple, TupleConfig))
|
|
996
|
+
preferred_candidates = [
|
|
997
|
+
t
|
|
998
|
+
for t in sequence_candidates
|
|
999
|
+
if is_tuple_annotation(t) == input_is_tuple
|
|
1000
|
+
]
|
|
1001
|
+
fallback_candidates = [
|
|
1002
|
+
t for t in sequence_candidates if t not in preferred_candidates
|
|
1003
|
+
]
|
|
962
1004
|
self._set_container_value(
|
|
963
1005
|
value=value,
|
|
964
|
-
candidates=
|
|
1006
|
+
candidates=preferred_candidates,
|
|
1007
|
+
fallback_candidates=fallback_candidates,
|
|
965
1008
|
type_hint=type_hint,
|
|
966
1009
|
)
|
|
967
1010
|
elif isinstance(value, (dict, Container)):
|
|
@@ -994,48 +1037,79 @@ class UnionNode(Box):
|
|
|
994
1037
|
value: Any,
|
|
995
1038
|
candidates: List[Any],
|
|
996
1039
|
type_hint: Any,
|
|
1040
|
+
fallback_candidates: Optional[List[Any]] = None,
|
|
997
1041
|
) -> None:
|
|
998
1042
|
from omegaconf.listconfig import ListConfig
|
|
999
1043
|
from omegaconf.omegaconf import _node_wrap
|
|
1044
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
1000
1045
|
|
|
1001
|
-
from ._utils import
|
|
1046
|
+
from ._utils import (
|
|
1047
|
+
get_dict_key_value_types,
|
|
1048
|
+
get_list_element_type,
|
|
1049
|
+
get_tuple_item_types,
|
|
1050
|
+
normalize_tuple_annotation,
|
|
1051
|
+
)
|
|
1002
1052
|
|
|
1003
1053
|
# For an already-typed container (from typed_list/typed_dict or another
|
|
1004
1054
|
# structured config), use its element-type metadata to narrow candidates
|
|
1005
1055
|
# before falling back to content-based validation. This makes empty
|
|
1006
1056
|
# containers unambiguous when the metadata uniquely identifies one branch.
|
|
1007
|
-
|
|
1008
|
-
meta_candidates = [
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
if meta_candidates:
|
|
1014
|
-
candidates = meta_candidates
|
|
1015
|
-
elif isinstance(value, Container) and not isinstance(value, ListConfig):
|
|
1016
|
-
vkey, vval = get_dict_key_value_types(value._metadata.ref_type)
|
|
1017
|
-
if vkey is not Any or vval is not Any:
|
|
1057
|
+
def narrow_by_metadata(candidate_types: List[Any]) -> List[Any]:
|
|
1058
|
+
meta_candidates: List[Any] = []
|
|
1059
|
+
if (
|
|
1060
|
+
isinstance(value, ListConfig)
|
|
1061
|
+
and value._metadata.element_type is not Any
|
|
1062
|
+
):
|
|
1018
1063
|
meta_candidates = [
|
|
1019
|
-
t
|
|
1064
|
+
t
|
|
1065
|
+
for t in candidate_types
|
|
1066
|
+
if is_list_annotation(t)
|
|
1067
|
+
and get_list_element_type(t) == value._metadata.element_type
|
|
1020
1068
|
]
|
|
1021
|
-
|
|
1022
|
-
|
|
1069
|
+
elif isinstance(value, TupleConfig):
|
|
1070
|
+
value_type = normalize_tuple_annotation(value._metadata.ref_type)
|
|
1071
|
+
value_item_types = get_tuple_item_types(value_type)
|
|
1072
|
+
meta_candidates = [
|
|
1073
|
+
t
|
|
1074
|
+
for t in candidate_types
|
|
1075
|
+
if is_tuple_annotation(t)
|
|
1076
|
+
and get_tuple_item_types(normalize_tuple_annotation(t))
|
|
1077
|
+
== value_item_types
|
|
1078
|
+
]
|
|
1079
|
+
elif isinstance(value, Container) and not isinstance(
|
|
1080
|
+
value, (ListConfig, TupleConfig)
|
|
1081
|
+
):
|
|
1082
|
+
vkey, vval = get_dict_key_value_types(value._metadata.ref_type)
|
|
1083
|
+
if vkey is not Any or vval is not Any:
|
|
1084
|
+
meta_candidates = [
|
|
1085
|
+
t
|
|
1086
|
+
for t in candidate_types
|
|
1087
|
+
if is_dict_annotation(t)
|
|
1088
|
+
and get_dict_key_value_types(t) == (vkey, vval)
|
|
1089
|
+
]
|
|
1090
|
+
return meta_candidates or candidate_types
|
|
1091
|
+
|
|
1092
|
+
def find_matches(candidate_types: List[Any]) -> List[Node]:
|
|
1093
|
+
matches: List[Node] = []
|
|
1094
|
+
for candidate_ref_type in narrow_by_metadata(candidate_types):
|
|
1095
|
+
try:
|
|
1096
|
+
node = _node_wrap(
|
|
1097
|
+
value=value,
|
|
1098
|
+
ref_type=candidate_ref_type,
|
|
1099
|
+
is_optional=False,
|
|
1100
|
+
key=None,
|
|
1101
|
+
parent=self,
|
|
1102
|
+
)
|
|
1103
|
+
matches.append(node)
|
|
1104
|
+
if len(matches) > 1:
|
|
1105
|
+
break
|
|
1106
|
+
except Exception:
|
|
1107
|
+
continue
|
|
1108
|
+
return matches
|
|
1023
1109
|
|
|
1024
|
-
matches
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
node = _node_wrap(
|
|
1028
|
-
value=value,
|
|
1029
|
-
ref_type=candidate_ref_type,
|
|
1030
|
-
is_optional=False,
|
|
1031
|
-
key=None,
|
|
1032
|
-
parent=self,
|
|
1033
|
-
)
|
|
1034
|
-
matches.append(node)
|
|
1035
|
-
if len(matches) > 1:
|
|
1036
|
-
break # ambiguous — stop constructing further candidates
|
|
1037
|
-
except Exception:
|
|
1038
|
-
continue
|
|
1110
|
+
matches = find_matches(candidates)
|
|
1111
|
+
if len(matches) == 0 and fallback_candidates:
|
|
1112
|
+
matches = find_matches(fallback_candidates)
|
|
1039
1113
|
|
|
1040
1114
|
if len(matches) == 0:
|
|
1041
1115
|
raise ValidationError(
|