Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Better and more consistent handling of the "missing" status #543

Closed
odelalleau opened this issue Feb 12, 2021 · 3 comments · Fixed by #545
Closed

Better and more consistent handling of the "missing" status #543

odelalleau opened this issue Feb 12, 2021 · 3 comments · Fixed by #545
Assignees
Labels
bug Something isn't working
Milestone

Comments

@odelalleau
Copy link
Collaborator

odelalleau commented Feb 12, 2021

Describe the bug

Currently the handling of a missing node in interpolations can lead to surprising behavior like:

cfg = OmegaConf.create({"x": "${missing}", "y": "abc ${missing}", "missing": "???"})
print(f'{cfg._get_node("x")._is_missing() = }')
print(f'{cfg._get_node("x")._dereference_node() = }')
print(f'{cfg._get_node("y")._is_missing() = }')
print(f'{cfg._get_node("y")._dereference_node() = }')

which prints:

cfg._get_node("x")._is_missing() = True
cfg._get_node("x")._dereference_node() = '???'
cfg._get_node("y")._is_missing() = False
cfg._get_node("y")._dereference_node() = 'abc ???'

The two potentially surprising things are:

  1. The fact that x is considered as missing while y isn't (while both refer to a missing node)
  2. The fact that y is happily dereferenced into a StringNode containing the string "abc ???"

Expected behavior

I propose the following behavior:

  1. A node is never missing if it is an interpolation (even if it interpolates into a missing node). This is consistent with the spirit of Behavior change: Merging a MISSING node should not change target #462, where we want "missing" to mean only being set to "???"
  2. Dereferencing a node containing an interpolation to a missing node should return None (unless throw_on_missing throw_on_resolution_failure is True, in which case it should raise the InterpolationToMissingValueError exception)

(Edit: I replaced throw_on_missing with throw_on_resolution_failure in point 2 because throw_on_missing when dereferencing a node should only refer to the status of the node being referenced, not to what happens when trying to resolve interpolations, while throw_on_resolution_failure relates to errors encountered when resolving interpolations)

@odelalleau odelalleau added the bug Something isn't working label Feb 12, 2021
@odelalleau odelalleau self-assigned this Feb 12, 2021
@omry
Copy link
Owner

omry commented Feb 12, 2021

The only surprise to me is cfg._get_node("y")._dereference_node() = 'abc ???'.
This should raise an exception while resolving the string interpolation.

This behavior is driving the contains API behavior for missing fields:

"missing" in cfg`

which returns False.
In a previous version string interpolation using a missing field was also considered missing, but I changed that at some point.

I am open to changing this behavior but we should consider it carefully and check in downstream apps (at least in Hydra).

@odelalleau
Copy link
Collaborator Author

odelalleau commented Feb 12, 2021

In a previous version string interpolation using a missing field was also considered missing, but I changed that at some point.

Yes, that was in #462, and I think it makes sense to also extend the same logic (not being missing) to any interpolation using a missing field, not just string interpolations, including in particular:

  • Nested interpolations like ${env:${missing}} or ${foo.${missing}}
  • Direct interpolations ${missing}

The last one is probably the most controversial, but I think it is what makes most sense to have simple and consistent semantics for what "missing" means ("has the key been set in the config?"). I ran Hydra's test suite with this change btw, and nothing broke.

@omry
Copy link
Owner

omry commented Feb 12, 2021

The origin of this behavior predated the OmegaConf.is_missing() public API.
key in cfg was used as a proxy to detect if a field is missing.

Now that we have a formal API to check if a field is missing I support this change.

odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 12, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
@omry omry added this to the OmegaConf 2.1 milestone Feb 16, 2021
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 23, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 24, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 25, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 26, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Feb 26, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, if `throw_on_missing` is False, then the result is
  always `None` (while it used to either be a missing Node, or an
  expression computed from the "???" string)

* Similarly, this commit also ensures that if
  `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
@omry omry closed this as completed in #545 Mar 3, 2021
omry pushed a commit that referenced this issue Mar 3, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, an `InterpolationToMissingValueError` exception is raised

* When resolving an expression containing an interpolation pointing to a
  node that does not exist, an `InterpolationKeyError` exception is raised

* `key in cfg` returns True whenever `key` is an interpolation, even if it cannot be resolved (including interpolations to missing nodes)

* `get()` and `pop()` no longer return the default value in case of interpolation resolution failure (same thing for `OmegaConf.select()`)

* If `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes #543
Fixes #561
Fixes #562
Fixes #565
odelalleau added a commit to odelalleau/omegaconf that referenced this issue Mar 10, 2021
* Interpolations are never considered to be missing anymore, even if
  they point to a missing node

* When resolving an expression containing an interpolation pointing to a
  missing node, an `InterpolationToMissingValueError` exception is raised

* When resolving an expression containing an interpolation pointing to a
  node that does not exist, an `InterpolationKeyError` exception is raised

* `key in cfg` returns True whenever `key` is an interpolation, even if it cannot be resolved (including interpolations to missing nodes)

* `get()` and `pop()` no longer return the default value in case of interpolation resolution failure (same thing for `OmegaConf.select()`)

* If `throw_on_resolution_failure` is False, then resolving an
  interpolation resulting in a resolution failure always leads to the
  result being `None` (instead of potentially being an expression computed
  from `None`)

Fixes omry#543
Fixes omry#561
Fixes omry#562
Fixes omry#565
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants