Skip to content

Commit

Permalink
Allow most characters in node interpolations' keys
Browse files Browse the repository at this point in the history
  • Loading branch information
odelalleau committed Dec 9, 2020
1 parent af3870d commit 9e1bc41
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 4 deletions.
2 changes: 1 addition & 1 deletion omegaconf/grammar/OmegaConfGrammarLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,5 @@ INTER_CLOSE: '}' -> popMode;

DOT: '.';
INTER_ID: ID -> type(ID);
LIST_INDEX: INT_UNSIGNED;
INTER_KEY: ~[\\${}()[\]:. \t'"]+; // interpolation key, may contain any non special character
INTER_WS: WS -> skip;
2 changes: 1 addition & 1 deletion omegaconf/grammar/OmegaConfGrammarParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ sequence: element (COMMA element)*;
interpolation: interpolationNode | interpolationResolver;
interpolationNode: INTER_OPEN DOT* configKey (DOT configKey)* INTER_CLOSE;
interpolationResolver: INTER_OPEN (interpolation | ID) COLON sequence? BRACE_CLOSE;
configKey: interpolation | ID | LIST_INDEX;
configKey: interpolation | ID | INTER_KEY;

// Primitive types.

Expand Down
2 changes: 1 addition & 1 deletion omegaconf/grammar_visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def defaultResult(self) -> List[Any]:
def visitConfigKey(self, ctx: OmegaConfGrammarParser.ConfigKeyContext) -> str:
from ._utils import _get_value

# interpolation | ID | LIST_INDEX
# interpolation | ID | INTER_KEY
assert ctx.getChildCount() == 1
child = ctx.getChild(0)
if isinstance(child, OmegaConfGrammarParser.InterpolationContext):
Expand Down
31 changes: 30 additions & 1 deletion tests/test_interpolation.py
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,32 @@ def test_supported_chars() -> None:
assert c.dir1 == supported_chars


def test_valid_key_names() -> None:
invalid_chars = "\\${}()[].: '\""
valid_chars = "".join(chr(i) for i in range(33, 128) if chr(i) not in invalid_chars)
cfg_dict = {valid_chars: 123, "inter": f"${{{valid_chars}}}"}
cfg = OmegaConf.create(cfg_dict)
# Test that we can access the node made of all valid characters, both
# directly and through interpolations.
assert cfg[valid_chars] == 123
assert cfg.inter == 123
# Test that all invalid characters trigger errors in interpolations.
for c in invalid_chars:
cfg_dict["invalid"] = f"${{ab{c}de}}"
cfg = OmegaConf.create(cfg_dict)
error: type
if c in [".", "}"]:
# With '.', we try to access `${ab.de}`.
# With "}", we try to access `${ab}`.
error = ConfigKeyError
elif c == ":":
error = UnsupportedInterpolationType # `${ab:de}`
else:
error = GrammarParseError # other cases are all parse errors
with pytest.raises(error):
cfg.invalid


def test_interpolation_in_list_key_error() -> None:
# Test that a KeyError is thrown if an str_interpolation key is not available
c = OmegaConf.create(["${10}"])
Expand Down Expand Up @@ -655,6 +681,7 @@ def _maybe_create(definition: str) -> Any:
("None", {"True": 1}, ...), # used to test keys with null-like names
("0", 42, ...), # used to test keys with int names
("1", {"2": 1337}, ...), # used to test dot-path with int keys
("x@y", 123, ...),
# Special keywords.
("null", "${test:null}", None),
("true", "${test:TrUe}", True),
Expand Down Expand Up @@ -697,7 +724,7 @@ def _maybe_create(definition: str) -> Any:
("list_access_1", "${prim_list.0}", -1),
("list_access_2", "${test:${prim_list.1},${prim_list.2}}", ["a", 1.1]),
("list_access_underscore", "${prim_list.1_000}", ConfigKeyError), # "working"
("list_access_bad_negative", "${prim_list.-1}", GrammarParseError),
("list_access_bad_negative", "${prim_list.-1}", ConfigKeyError),
("dict_access_list_like_1", "${0}", 42),
("dict_access_list_like_2", "${1.2}", 1337),
("bool_like_keys", "${FalsE.TruE}", True),
Expand All @@ -706,6 +733,7 @@ def _maybe_create(definition: str) -> Any:
("null_like_key_quoted_1", "${'None'.'True'}", GrammarParseError),
("null_like_key_quoted_2", "${'None.True'}", GrammarParseError),
("dotpath_bad_type", "${prim_dict.${float}}", GrammarParseError),
("at_in_key", "${x@y}", 123),
# Resolver interpolations.
("no_args", "${test:}", []),
("space_in_args", "${test:a, b c}", ["a", "b c"]),
Expand All @@ -716,6 +744,7 @@ def _maybe_create(definition: str) -> Any:
("dict_list_as_key", "${test:{[0]: 1}}", GrammarParseError),
("missing_resolver", "${MiSsInG_ReSoLvEr:0}", UnsupportedInterpolationType),
("non_str_resolver", "${${bool}:}", GrammarParseError),
("at_in_resolver", "${y@z:}", GrammarParseError),
# Env resolver (limited: more tests in `test_env_values_are_typed()`).
("env_int", "${env:OMEGACONF_TEST_ENV_INT}", 123),
("env_missing_str", "${env:OMEGACONF_TEST_MISSING,miss}", "miss"),
Expand Down

0 comments on commit 9e1bc41

Please sign in to comment.