omegaconf 2.4.0.dev12__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.dev12/omegaconf.egg-info → omegaconf-2.4.0.dev13}/PKG-INFO +1 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/__init__.py +2 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/_impl.py +20 -4
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/_utils.py +52 -4
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/base.py +85 -37
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/basecontainer.py +78 -13
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/listconfig.py +23 -5
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/omegaconf.py +117 -30
- omegaconf-2.4.0.dev13/omegaconf/tupleconfig.py +522 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/version.py +2 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13/omegaconf.egg-info}/PKG-INFO +1 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/SOURCES.txt +5 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/pyproject.toml +4 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/test_structured_config.py +35 -8
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_base_config.py +1 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_basic_ops_list.py +36 -4
- omegaconf-2.4.0.dev13/tests/test_basic_ops_tuple.py +411 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_container_unions.py +86 -2
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_create.py +10 -2
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_errors.py +8 -2
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_merge.py +4 -5
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_nested_containers.py +20 -8
- 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.dev12 → omegaconf-2.4.0.dev13}/tests/test_utils.py +4 -1
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/LICENSE +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/MANIFEST.in +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/README.md +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/build_helpers/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/build_helpers/bin/antlr-4.11.1-complete.jar +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/build_helpers/build_helpers.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/build_helpers/get_vendored.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/build_helpers/test_helpers.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/_yaml.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/dictconfig.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/errors.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/OmegaConfGrammarLexer.g4 +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/OmegaConfGrammarParser.g4 +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarLexer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParser.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParserListener.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/OmegaConfGrammarParserVisitor.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar/gen/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar_parser.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/grammar_visitor.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/nodes.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/py.typed +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/oc/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/resolvers/oc/dict.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/typing.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/BufferedTokenStream.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/CommonTokenFactory.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/CommonTokenStream.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/FileStream.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/InputStream.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/IntervalSet.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/LL1Analyzer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Lexer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ListTokenSource.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Parser.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ParserInterpreter.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/ParserRuleContext.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/PredictionContext.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Recognizer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/RuleContext.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/StdinStream.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Token.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/TokenStreamRewriter.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/Utils.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/_pygrun.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATN.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNConfig.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNConfigSet.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNDeserializationOptions.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNDeserializer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNState.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ATNType.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerAction.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/LexerActionExecutor.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/ParserATNSimulator.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/PredictionMode.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/SemanticContext.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/Transition.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/atn/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFA.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFASerializer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/DFAState.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/dfa/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/DiagnosticErrorListener.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/ErrorListener.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/ErrorStrategy.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/Errors.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/error/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Chunk.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreeMatch.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreePattern.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/ParseTreePatternMatcher.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/RuleTagToken.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/TokenTagToken.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Tree.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/Trees.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/tree/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/XPath.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/XPathLexer.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf/vendor/antlr4/xpath/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/dependency_links.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/requires.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/omegaconf.egg-info/top_level.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/requirements/base.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/requirements/dev.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/requirements/docs.txt +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/setup.cfg +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/setup.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/conftest.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/data/2.0.6.pickle +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/data/2.1.0.rc1.pickle +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/data/load.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/data/save.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/examples/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/examples/dataclass_postponed_annotations.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/examples/test_dataclass_example.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/examples/test_postponed_annotations.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_create.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_decode.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_deprecated.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_dict.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_env.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/built_in_resolvers/test_oc_select.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/test_custom_resolvers.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/interpolation/test_interpolation.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/__init__.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/attr_classes.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/dataclasses.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/data/dataclasses_pre_311.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/structured_conf/test_structured_basic.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_basic_ops_dict.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_compare_dictconfig_vs_dict.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_config_eq.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_get_full_key.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_grammar.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_matrix.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_nodes.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_omegaconf.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_readonly.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_select.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_serialization.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_struct.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_to_container.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_to_yaml.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_unions.py +0 -0
- {omegaconf-2.4.0.dev12 → omegaconf-2.4.0.dev13}/tests/test_update.py +0 -0
|
@@ -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
|
|
|
@@ -742,12 +753,45 @@ def get_list_element_type(ref_type: Optional[Type[Any]]) -> Any:
|
|
|
742
753
|
|
|
743
754
|
def get_tuple_item_types(ref_type: Type[Any]) -> Tuple[Any, ...]:
|
|
744
755
|
args = getattr(ref_type, "__args__", None)
|
|
745
|
-
if
|
|
756
|
+
if ref_type in (tuple, Tuple) or args is None:
|
|
746
757
|
args = (Any, ...)
|
|
758
|
+
elif args == ((),):
|
|
759
|
+
args = ()
|
|
747
760
|
assert isinstance(args, tuple)
|
|
748
761
|
return args
|
|
749
762
|
|
|
750
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
|
+
|
|
751
795
|
def get_dict_key_value_types(ref_type: Any) -> Tuple[Any, Any]:
|
|
752
796
|
args = getattr(ref_type, "__args__", None)
|
|
753
797
|
if args is None:
|
|
@@ -1043,14 +1087,14 @@ def _ensure_container(target: Any, flags: Optional[Dict[str, bool]] = None) -> A
|
|
|
1043
1087
|
from omegaconf import OmegaConf
|
|
1044
1088
|
|
|
1045
1089
|
if is_primitive_container(target):
|
|
1046
|
-
assert isinstance(target, (list, dict))
|
|
1090
|
+
assert isinstance(target, (list, tuple, dict))
|
|
1047
1091
|
target = OmegaConf.create(target, flags=flags)
|
|
1048
1092
|
elif is_structured_config(target):
|
|
1049
1093
|
target = OmegaConf.structured(target, flags=flags)
|
|
1050
1094
|
elif not OmegaConf.is_config(target):
|
|
1051
1095
|
raise ValueError(
|
|
1052
1096
|
"Invalid input. Supports one of "
|
|
1053
|
-
+ "[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]"
|
|
1054
1098
|
)
|
|
1055
1099
|
|
|
1056
1100
|
return target
|
|
@@ -1083,7 +1127,11 @@ def is_generic_dict(type_: Any) -> bool:
|
|
|
1083
1127
|
|
|
1084
1128
|
|
|
1085
1129
|
def is_container_annotation(type_: Any) -> bool:
|
|
1086
|
-
return
|
|
1130
|
+
return (
|
|
1131
|
+
is_list_annotation(type_)
|
|
1132
|
+
or is_tuple_annotation(type_)
|
|
1133
|
+
or is_dict_annotation(type_)
|
|
1134
|
+
)
|
|
1087
1135
|
|
|
1088
1136
|
|
|
1089
1137
|
# Characters recognised after a backslash in key paths and dotlist entries.
|
|
@@ -30,6 +30,7 @@ from ._utils import (
|
|
|
30
30
|
get_value_kind,
|
|
31
31
|
is_dict_annotation,
|
|
32
32
|
is_list_annotation,
|
|
33
|
+
is_tuple_annotation,
|
|
33
34
|
is_union_annotation,
|
|
34
35
|
is_valid_value_annotation,
|
|
35
36
|
split_key,
|
|
@@ -377,7 +378,7 @@ class Node(ABC):
|
|
|
377
378
|
class Box(Node):
|
|
378
379
|
"""
|
|
379
380
|
Base class for nodes that can contain other nodes.
|
|
380
|
-
Concrete subclasses include DictConfig, ListConfig, and UnionNode.
|
|
381
|
+
Concrete subclasses include DictConfig, ListConfig, TupleConfig, and UnionNode.
|
|
381
382
|
"""
|
|
382
383
|
|
|
383
384
|
_content: Any
|
|
@@ -393,6 +394,7 @@ class Box(Node):
|
|
|
393
394
|
def _re_parent(self) -> None:
|
|
394
395
|
from .dictconfig import DictConfig
|
|
395
396
|
from .listconfig import ListConfig
|
|
397
|
+
from .tupleconfig import TupleConfig
|
|
396
398
|
|
|
397
399
|
# update parents of first level Config nodes to self
|
|
398
400
|
|
|
@@ -404,7 +406,7 @@ class Box(Node):
|
|
|
404
406
|
value._set_parent(self)
|
|
405
407
|
if isinstance(value, Box):
|
|
406
408
|
value._re_parent()
|
|
407
|
-
elif isinstance(self, ListConfig):
|
|
409
|
+
elif isinstance(self, (ListConfig, TupleConfig)):
|
|
408
410
|
content = self.__dict__["_content"]
|
|
409
411
|
if isinstance(content, list):
|
|
410
412
|
for item in self.__dict__["_content"]:
|
|
@@ -844,6 +846,7 @@ class Container(Box):
|
|
|
844
846
|
def _invalidate_flags_cache(self) -> None:
|
|
845
847
|
from .dictconfig import DictConfig
|
|
846
848
|
from .listconfig import ListConfig
|
|
849
|
+
from .tupleconfig import TupleConfig
|
|
847
850
|
|
|
848
851
|
# invalidate subtree cache only if the cache is initialized in this node.
|
|
849
852
|
|
|
@@ -854,7 +857,7 @@ class Container(Box):
|
|
|
854
857
|
if isinstance(content, dict):
|
|
855
858
|
for value in self.__dict__["_content"].values():
|
|
856
859
|
value._invalidate_flags_cache()
|
|
857
|
-
elif isinstance(self, ListConfig):
|
|
860
|
+
elif isinstance(self, (ListConfig, TupleConfig)):
|
|
858
861
|
content = self.__dict__["_content"]
|
|
859
862
|
if isinstance(content, list):
|
|
860
863
|
for item in self.__dict__["_content"]:
|
|
@@ -969,6 +972,7 @@ class UnionNode(Box):
|
|
|
969
972
|
) -> None:
|
|
970
973
|
from omegaconf.listconfig import ListConfig
|
|
971
974
|
from omegaconf.omegaconf import _node_wrap
|
|
975
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
972
976
|
|
|
973
977
|
ref_type = self._metadata.ref_type
|
|
974
978
|
type_hint = self._metadata.type_hint
|
|
@@ -982,12 +986,25 @@ class UnionNode(Box):
|
|
|
982
986
|
f"Value '$VALUE' is incompatible with type hint '{type_str(type_hint)}'"
|
|
983
987
|
)
|
|
984
988
|
self.__dict__["_content"] = value
|
|
985
|
-
elif isinstance(value, (list, tuple, ListConfig)):
|
|
986
|
-
|
|
987
|
-
|
|
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
|
+
]
|
|
988
1004
|
self._set_container_value(
|
|
989
1005
|
value=value,
|
|
990
|
-
candidates=
|
|
1006
|
+
candidates=preferred_candidates,
|
|
1007
|
+
fallback_candidates=fallback_candidates,
|
|
991
1008
|
type_hint=type_hint,
|
|
992
1009
|
)
|
|
993
1010
|
elif isinstance(value, (dict, Container)):
|
|
@@ -1020,48 +1037,79 @@ class UnionNode(Box):
|
|
|
1020
1037
|
value: Any,
|
|
1021
1038
|
candidates: List[Any],
|
|
1022
1039
|
type_hint: Any,
|
|
1040
|
+
fallback_candidates: Optional[List[Any]] = None,
|
|
1023
1041
|
) -> None:
|
|
1024
1042
|
from omegaconf.listconfig import ListConfig
|
|
1025
1043
|
from omegaconf.omegaconf import _node_wrap
|
|
1044
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
1026
1045
|
|
|
1027
|
-
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
|
+
)
|
|
1028
1052
|
|
|
1029
1053
|
# For an already-typed container (from typed_list/typed_dict or another
|
|
1030
1054
|
# structured config), use its element-type metadata to narrow candidates
|
|
1031
1055
|
# before falling back to content-based validation. This makes empty
|
|
1032
1056
|
# containers unambiguous when the metadata uniquely identifies one branch.
|
|
1033
|
-
|
|
1034
|
-
meta_candidates = [
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
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
|
+
):
|
|
1063
|
+
meta_candidates = [
|
|
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
|
|
1068
|
+
]
|
|
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)
|
|
1044
1072
|
meta_candidates = [
|
|
1045
|
-
t
|
|
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
|
|
1046
1078
|
]
|
|
1047
|
-
|
|
1048
|
-
|
|
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
|
|
1049
1109
|
|
|
1050
|
-
matches
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
node = _node_wrap(
|
|
1054
|
-
value=value,
|
|
1055
|
-
ref_type=candidate_ref_type,
|
|
1056
|
-
is_optional=False,
|
|
1057
|
-
key=None,
|
|
1058
|
-
parent=self,
|
|
1059
|
-
)
|
|
1060
|
-
matches.append(node)
|
|
1061
|
-
if len(matches) > 1:
|
|
1062
|
-
break # ambiguous — stop constructing further candidates
|
|
1063
|
-
except Exception:
|
|
1064
|
-
continue
|
|
1110
|
+
matches = find_matches(candidates)
|
|
1111
|
+
if len(matches) == 0 and fallback_candidates:
|
|
1112
|
+
matches = find_matches(fallback_candidates)
|
|
1065
1113
|
|
|
1066
1114
|
if len(matches) == 0:
|
|
1067
1115
|
raise ValidationError(
|
|
@@ -131,6 +131,8 @@ class BaseContainer(Container, ABC):
|
|
|
131
131
|
dict_copy["_metadata"].ref_type = Dict
|
|
132
132
|
elif is_list_annotation(ref_type):
|
|
133
133
|
dict_copy["_metadata"].ref_type = List
|
|
134
|
+
elif is_tuple_annotation(ref_type):
|
|
135
|
+
pass
|
|
134
136
|
else:
|
|
135
137
|
assert False
|
|
136
138
|
return dict_copy
|
|
@@ -164,6 +166,8 @@ class BaseContainer(Container, ABC):
|
|
|
164
166
|
]
|
|
165
167
|
elif is_generic_list(ref_type):
|
|
166
168
|
state_dict["_metadata"].ref_type = List[element_type] # type: ignore
|
|
169
|
+
elif is_tuple_annotation(ref_type):
|
|
170
|
+
pass
|
|
167
171
|
else:
|
|
168
172
|
assert False
|
|
169
173
|
|
|
@@ -253,8 +257,8 @@ class BaseContainer(Container, ABC):
|
|
|
253
257
|
enum_to_str: bool = False,
|
|
254
258
|
structured_config_mode: SCMode = SCMode.DICT,
|
|
255
259
|
resolved_node_cache: Optional[Dict[int, Node]] = None,
|
|
256
|
-
) -> Union[None, Any, str, Dict[DictKeyType, Any], List[Any]]:
|
|
257
|
-
from omegaconf import MISSING, DictConfig, ListConfig
|
|
260
|
+
) -> Union[None, Any, str, Dict[DictKeyType, Any], List[Any], Tuple[Any, ...]]:
|
|
261
|
+
from omegaconf import MISSING, DictConfig, ListConfig, TupleConfig
|
|
258
262
|
|
|
259
263
|
if resolve and resolved_node_cache is None:
|
|
260
264
|
resolved_node_cache = {}
|
|
@@ -352,6 +356,8 @@ class BaseContainer(Container, ABC):
|
|
|
352
356
|
retlist.append(item)
|
|
353
357
|
|
|
354
358
|
return retlist
|
|
359
|
+
elif isinstance(conf, TupleConfig):
|
|
360
|
+
return tuple(get_node_value(index) for index in range(len(conf)))
|
|
355
361
|
assert False
|
|
356
362
|
|
|
357
363
|
@staticmethod
|
|
@@ -362,7 +368,7 @@ class BaseContainer(Container, ABC):
|
|
|
362
368
|
_allow_readonly_target: bool = False,
|
|
363
369
|
) -> None:
|
|
364
370
|
"""merge src into dest and return a new copy, does not modified input"""
|
|
365
|
-
from omegaconf import AnyNode, DictConfig, ValueNode
|
|
371
|
+
from omegaconf import AnyNode, DictConfig, ListConfig, TupleConfig, ValueNode
|
|
366
372
|
|
|
367
373
|
assert isinstance(dest, DictConfig)
|
|
368
374
|
assert isinstance(src, DictConfig)
|
|
@@ -440,13 +446,18 @@ class BaseContainer(Container, ABC):
|
|
|
440
446
|
|
|
441
447
|
if (
|
|
442
448
|
isinstance(dest_node, Container)
|
|
449
|
+
and not isinstance(dest_node, TupleConfig)
|
|
443
450
|
and dest_node._is_none()
|
|
444
451
|
and not src_node_missing
|
|
445
452
|
and not _is_none(src_node, resolve=True)
|
|
446
453
|
):
|
|
447
454
|
expand(dest_node)
|
|
448
455
|
|
|
449
|
-
if
|
|
456
|
+
if (
|
|
457
|
+
dest_node is not None
|
|
458
|
+
and not isinstance(dest_node, TupleConfig)
|
|
459
|
+
and dest_node._is_interpolation()
|
|
460
|
+
):
|
|
450
461
|
target_node = dest_node._maybe_dereference_node()
|
|
451
462
|
if isinstance(target_node, Container):
|
|
452
463
|
dest[key] = target_node
|
|
@@ -462,7 +473,11 @@ class BaseContainer(Container, ABC):
|
|
|
462
473
|
|
|
463
474
|
if dest_node is not None:
|
|
464
475
|
if isinstance(dest_node, BaseContainer):
|
|
465
|
-
if isinstance(
|
|
476
|
+
if isinstance(dest_node, TupleConfig) and isinstance(
|
|
477
|
+
src_node, (ListConfig, TupleConfig)
|
|
478
|
+
):
|
|
479
|
+
BaseContainer._tuple_merge(dest_node, src_node)
|
|
480
|
+
elif isinstance(src_node, BaseContainer):
|
|
466
481
|
dest_node._merge_with(
|
|
467
482
|
src_node,
|
|
468
483
|
list_merge_mode=list_merge_mode,
|
|
@@ -562,6 +577,21 @@ class BaseContainer(Container, ABC):
|
|
|
562
577
|
if value is not None:
|
|
563
578
|
dest._set_flag(flag, value)
|
|
564
579
|
|
|
580
|
+
@staticmethod
|
|
581
|
+
def _tuple_merge(dest: Any, src: Any) -> None:
|
|
582
|
+
from omegaconf import ListConfig, TupleConfig
|
|
583
|
+
|
|
584
|
+
assert isinstance(dest, TupleConfig)
|
|
585
|
+
assert isinstance(src, (ListConfig, TupleConfig))
|
|
586
|
+
if not src._is_missing():
|
|
587
|
+
dest._set_value(src)
|
|
588
|
+
|
|
589
|
+
flags = src._metadata.flags
|
|
590
|
+
assert flags is not None
|
|
591
|
+
for flag, value in flags.items():
|
|
592
|
+
if value is not None:
|
|
593
|
+
dest._set_flag(flag, value)
|
|
594
|
+
|
|
565
595
|
def merge_with(
|
|
566
596
|
self,
|
|
567
597
|
*others: Union[
|
|
@@ -587,6 +617,7 @@ class BaseContainer(Container, ABC):
|
|
|
587
617
|
) -> None:
|
|
588
618
|
from .dictconfig import DictConfig
|
|
589
619
|
from .listconfig import ListConfig
|
|
620
|
+
from .tupleconfig import TupleConfig
|
|
590
621
|
|
|
591
622
|
"""merge a list of other Config objects into this one, overriding as needed"""
|
|
592
623
|
for other in others:
|
|
@@ -615,14 +646,26 @@ class BaseContainer(Container, ABC):
|
|
|
615
646
|
list_merge_mode=list_merge_mode,
|
|
616
647
|
_allow_readonly_target=_allow_readonly_target,
|
|
617
648
|
)
|
|
618
|
-
elif isinstance(self, ListConfig) and isinstance(
|
|
649
|
+
elif isinstance(self, ListConfig) and isinstance(
|
|
650
|
+
other, (ListConfig, TupleConfig)
|
|
651
|
+
):
|
|
652
|
+
if isinstance(other, TupleConfig):
|
|
653
|
+
other = ListConfig(content=other)
|
|
619
654
|
BaseContainer._list_merge(
|
|
620
655
|
self,
|
|
621
656
|
other,
|
|
622
657
|
list_merge_mode=list_merge_mode,
|
|
623
658
|
)
|
|
659
|
+
elif isinstance(self, TupleConfig) and isinstance(
|
|
660
|
+
other, (ListConfig, TupleConfig)
|
|
661
|
+
):
|
|
662
|
+
if not _allow_readonly_target:
|
|
663
|
+
raise ConfigTypeError(
|
|
664
|
+
"Cannot merge into a TupleConfig in-place"
|
|
665
|
+
)
|
|
666
|
+
BaseContainer._tuple_merge(self, other)
|
|
624
667
|
else:
|
|
625
|
-
raise TypeError("Cannot merge
|
|
668
|
+
raise TypeError("Cannot merge incompatible container types")
|
|
626
669
|
finally:
|
|
627
670
|
if readonly_overridden:
|
|
628
671
|
self._set_flag("readonly", prev_readonly)
|
|
@@ -829,6 +872,9 @@ class BaseContainer(Container, ABC):
|
|
|
829
872
|
def _get_full_key(self, key: Union[DictKeyType, int, slice, None]) -> str:
|
|
830
873
|
from .listconfig import ListConfig
|
|
831
874
|
from .omegaconf import _select_one
|
|
875
|
+
from .tupleconfig import TupleConfig
|
|
876
|
+
|
|
877
|
+
sequence_types = (ListConfig, TupleConfig)
|
|
832
878
|
|
|
833
879
|
if not isinstance(key, (int, str, Enum, float, bool, slice, bytes, type(None))):
|
|
834
880
|
return ""
|
|
@@ -857,9 +903,9 @@ class BaseContainer(Container, ABC):
|
|
|
857
903
|
|
|
858
904
|
assert isinstance(key, str)
|
|
859
905
|
|
|
860
|
-
if issubclass(parent_type,
|
|
906
|
+
if issubclass(parent_type, sequence_types):
|
|
861
907
|
if full_key != "":
|
|
862
|
-
if issubclass(cur_type,
|
|
908
|
+
if issubclass(cur_type, sequence_types):
|
|
863
909
|
full_key = f"[{key}]{full_key}"
|
|
864
910
|
else:
|
|
865
911
|
full_key = f"[{key}].{full_key}"
|
|
@@ -869,7 +915,7 @@ class BaseContainer(Container, ABC):
|
|
|
869
915
|
if full_key == "":
|
|
870
916
|
full_key = key
|
|
871
917
|
else:
|
|
872
|
-
if issubclass(cur_type,
|
|
918
|
+
if issubclass(cur_type, sequence_types):
|
|
873
919
|
full_key = f"{key}{full_key}"
|
|
874
920
|
else:
|
|
875
921
|
full_key = f"{key}.{full_key}"
|
|
@@ -952,9 +998,14 @@ def _update_types(node: Node, ref_type: Any, object_type: Optional[type]) -> Non
|
|
|
952
998
|
|
|
953
999
|
def _deep_update_type_hint(node: Node, type_hint: Any) -> None:
|
|
954
1000
|
"""Ensure node is compatible with type_hint, mutating if necessary."""
|
|
955
|
-
from omegaconf import DictConfig, ListConfig, OmegaConf
|
|
1001
|
+
from omegaconf import DictConfig, ListConfig, OmegaConf, TupleConfig
|
|
956
1002
|
|
|
957
|
-
from ._utils import
|
|
1003
|
+
from ._utils import (
|
|
1004
|
+
get_dict_key_value_types,
|
|
1005
|
+
get_list_element_type,
|
|
1006
|
+
get_tuple_item_types,
|
|
1007
|
+
is_variadic_tuple_annotation,
|
|
1008
|
+
)
|
|
958
1009
|
|
|
959
1010
|
if type_hint is Any:
|
|
960
1011
|
return
|
|
@@ -986,6 +1037,18 @@ def _deep_update_type_hint(node: Node, type_hint: Any) -> None:
|
|
|
986
1037
|
for i in range(len(node)):
|
|
987
1038
|
_deep_update_subnode(node, i, new_element_type)
|
|
988
1039
|
|
|
1040
|
+
if is_tuple_annotation(new_ref_type) and isinstance(node, TupleConfig):
|
|
1041
|
+
item_types = get_tuple_item_types(new_ref_type)
|
|
1042
|
+
variadic = is_variadic_tuple_annotation(new_ref_type)
|
|
1043
|
+
if not variadic and len(node) != len(item_types):
|
|
1044
|
+
raise ValidationError(
|
|
1045
|
+
f"TupleConfig length {len(node)} does not match type hint length {len(item_types)}"
|
|
1046
|
+
)
|
|
1047
|
+
if not _is_special(node):
|
|
1048
|
+
for index in range(len(node)):
|
|
1049
|
+
item_type = item_types[0] if variadic else item_types[index]
|
|
1050
|
+
_deep_update_subnode(node, index, item_type)
|
|
1051
|
+
|
|
989
1052
|
if is_dict_annotation(new_ref_type) and isinstance(node, DictConfig):
|
|
990
1053
|
new_key_type, new_element_type = get_dict_key_value_types(new_ref_type)
|
|
991
1054
|
node._metadata.key_type = new_key_type
|
|
@@ -1028,7 +1091,7 @@ def _deep_update_subnode(node: BaseContainer, key: Any, value_type_hint: Any) ->
|
|
|
1028
1091
|
|
|
1029
1092
|
def _shallow_validate_type_hint(node: Node, type_hint: Any) -> None:
|
|
1030
1093
|
"""Error if node's type, content and metadata are not compatible with type_hint."""
|
|
1031
|
-
from omegaconf import DictConfig, ListConfig, UnionNode, ValueNode
|
|
1094
|
+
from omegaconf import DictConfig, ListConfig, TupleConfig, UnionNode, ValueNode
|
|
1032
1095
|
|
|
1033
1096
|
is_optional, ref_type = _resolve_optional(type_hint)
|
|
1034
1097
|
|
|
@@ -1067,6 +1130,8 @@ def _shallow_validate_type_hint(node: Node, type_hint: Any) -> None:
|
|
|
1067
1130
|
return
|
|
1068
1131
|
elif is_list_annotation(ref_type) and isinstance(node, ListConfig):
|
|
1069
1132
|
return
|
|
1133
|
+
elif is_tuple_annotation(ref_type) and isinstance(node, TupleConfig):
|
|
1134
|
+
return
|
|
1070
1135
|
else:
|
|
1071
1136
|
if isinstance(node, ValueNode):
|
|
1072
1137
|
value = node._value()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
from typing import (
|
|
3
|
+
TYPE_CHECKING,
|
|
3
4
|
Any,
|
|
4
5
|
Callable,
|
|
5
6
|
Dict,
|
|
@@ -13,6 +14,9 @@ from typing import (
|
|
|
13
14
|
Union,
|
|
14
15
|
)
|
|
15
16
|
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from .tupleconfig import TupleConfig
|
|
19
|
+
|
|
16
20
|
from ._utils import (
|
|
17
21
|
ValueKind,
|
|
18
22
|
_get_value,
|
|
@@ -44,7 +48,9 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
|
|
|
44
48
|
|
|
45
49
|
def __init__(
|
|
46
50
|
self,
|
|
47
|
-
content: Union[
|
|
51
|
+
content: Union[
|
|
52
|
+
List[Any], Tuple[Any, ...], "ListConfig", "TupleConfig", str, None
|
|
53
|
+
],
|
|
48
54
|
key: Any = None,
|
|
49
55
|
parent: Optional[Box] = None,
|
|
50
56
|
element_type: Union[Type[Any], Any] = Any,
|
|
@@ -322,7 +328,13 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
|
|
|
322
328
|
assert False
|
|
323
329
|
|
|
324
330
|
def extend(self, lst: Iterable[Any]) -> None:
|
|
325
|
-
|
|
331
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
332
|
+
|
|
333
|
+
if not isinstance(lst, (tuple, list, ListConfig, TupleConfig)):
|
|
334
|
+
raise TypeError(
|
|
335
|
+
"ListConfig.extend() expected a list, tuple, ListConfig, "
|
|
336
|
+
f"or TupleConfig, got {type(lst).__name__}"
|
|
337
|
+
)
|
|
326
338
|
for x in lst:
|
|
327
339
|
self.append(x)
|
|
328
340
|
|
|
@@ -604,6 +616,7 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
|
|
|
604
616
|
self, value: Any, flags: Optional[Dict[str, bool]] = None
|
|
605
617
|
) -> None:
|
|
606
618
|
from omegaconf import MISSING, flag_override
|
|
619
|
+
from omegaconf.tupleconfig import TupleConfig
|
|
607
620
|
|
|
608
621
|
if flags is None:
|
|
609
622
|
flags = {}
|
|
@@ -623,13 +636,18 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
|
|
|
623
636
|
self.__dict__["_content"] = value
|
|
624
637
|
self._metadata.object_type = None
|
|
625
638
|
else:
|
|
626
|
-
if not (
|
|
639
|
+
if not (
|
|
640
|
+
is_primitive_list(value) or isinstance(value, (ListConfig, TupleConfig))
|
|
641
|
+
):
|
|
627
642
|
type_ = type(value)
|
|
628
|
-
msg =
|
|
643
|
+
msg = (
|
|
644
|
+
f"Invalid value assigned: {type_.__name__} is not a "
|
|
645
|
+
"ListConfig, TupleConfig, list, or tuple."
|
|
646
|
+
)
|
|
629
647
|
raise ValidationError(msg)
|
|
630
648
|
|
|
631
649
|
self.__dict__["_content"] = []
|
|
632
|
-
if isinstance(value, ListConfig):
|
|
650
|
+
if isinstance(value, (ListConfig, TupleConfig)):
|
|
633
651
|
self._metadata.flags = copy.deepcopy(flags)
|
|
634
652
|
# disable struct and readonly for the construction phase
|
|
635
653
|
# retaining other flags like allow_objects. The real flags are restored at the end of this function
|