Repository: omry/omegaconf

Issue:
Document select() and update()
1. deprecated cfg.select(key) and favor of OmegaConf.select(cfg, key)
2. deprecated cfg.update_node(key, value) and favor of OmegaConf.update_node(cfg, key, value)
3. document OmegaConf.update and OmegaConf.select

Discussion:
deepcode-ci-bot[bot]: <img src="https://www.deepcode.ai/icons/green_check.svg" width= "50px" align= "left"/> Congratulations :tada:. DeepCode [analyzed](https://www.deepcode.ai/app/gh/omry/omegaconf/7598f526998fa913d2456245395b3c8710b13525/omry/omegaconf/1827d47545307a57e0004ab32d54782d337a7622/_/%2F/code/?utm_source=gh_review) your code in 1.772 seconds and we found no issues. Enjoy a moment of no bugs :sunny:.

##### 👉 View analysis in [**DeepCode’s Dashboard**](https://www.deepcode.ai/app/gh/omry/omegaconf/7598f526998fa913d2456245395b3c8710b13525/omry/omegaconf/1827d47545307a57e0004ab32d54782d337a7622/_/%2F/code/?utm_source=gh_review)
---


Patch:
diff --git a/docs/source/usage.rst b/docs/source/usage.rst
index 4cd6b77..8d3eb71 100644
--- a/docs/source/usage.rst
+++ b/docs/source/usage.rst
@@ -1,6 +1,6 @@
 .. testsetup:: *
 
-    from omegaconf import OmegaConf
+    from omegaconf import OmegaConf, DictConfig
     import os
     import sys
     os.environ['USER'] = 'omry'
@@ -39,7 +39,7 @@ From a dictionary
 
 .. doctest::
 
-    >>> conf = OmegaConf.create(dict(k='v',list=[1,dict(a='1',b='2')]))
+    >>> conf = OmegaConf.create({"k" : "v", "list" : [1, {"a": "1", "b": "2"}]})
     >>> print(conf.pretty())
     k: v
     list:
@@ -53,7 +53,7 @@ From a list
 
 .. doctest::
 
-    >>> conf = OmegaConf.create([1, dict(a=10, b=dict(a=10))])
+    >>> conf = OmegaConf.create([1, {"a":10, "b": {"a":10}}])
     >>> print(conf.pretty())
     - 1
     - a: 10
@@ -83,8 +83,6 @@ From a yaml file
 From a yaml string
 ^^^^^^^^^^^^^^^^^^
 
-
-
 .. doctest::
 
     >>> s = """
@@ -428,7 +426,7 @@ An attempt to modify it will result in omegaconf.ReadonlyConfigError exception
 
 .. doctest:: loaded
 
-    >>> conf = OmegaConf.create(dict(a=dict(b=10)))
+    >>> conf = OmegaConf.create({"a": {"b": 10}})
     >>> OmegaConf.set_readonly(conf, True)
     >>> conf.a.b = 20
     Traceback (most recent call last):
@@ -440,7 +438,7 @@ You can temporarily remove the read only flag from a config object:
 .. doctest:: loaded
 
     >>> import omegaconf
-    >>> conf = OmegaConf.create(dict(a=dict(b=10)))
+    >>> conf = OmegaConf.create({"a": {"b": 10}})
     >>> OmegaConf.set_readonly(conf, True)
     >>> with omegaconf.read_write(conf):
     ...   conf.a.b = 20
@@ -458,7 +456,7 @@ It's sometime useful to change this behavior.
 
 .. doctest:: loaded
 
-    >>> conf = OmegaConf.create(dict(a=dict(aa=10, bb=20)))
+    >>> conf = OmegaConf.create({"a": {"aa": 10, "bb": 20}})
     >>> OmegaConf.set_struct(conf, True)
     >>> conf.a.cc = 30
     Traceback (most recent call last):
@@ -470,7 +468,7 @@ You can temporarily remove the struct flag from a config object:
 .. doctest:: loaded
 
     >>> import omegaconf
-    >>> conf = OmegaConf.create(dict(a=dict(aa=10, bb=20)))
+    >>> conf = OmegaConf.create({"a": {"aa": 10, "bb": 20}})
     >>> OmegaConf.set_struct(conf, True)
     >>> with omegaconf.open_dict(conf):
     ...   conf.a.cc = 30
@@ -506,19 +504,16 @@ Tests if an object is an OmegaConf object, or if it's representing a list or a d
 
 OmegaConf.to_container
 ^^^^^^^^^^^^^^^^^^^^^^
-OmegaConf config objects looks very similar to python dict and lists, but in fact are not.
-Use OmegaConf.to_container(cfg, resolve) to convert to a primitive container.
-If resolve is set to True the values will be resolved during conversion.
+OmegaConf config objects looks very similar to python dict and list, but in fact are not.
+Use OmegaConf.to_container(cfg : Container, resolve : bool) to convert to a primitive container.
+If resolve is set to True, interpolations will be resolved during conversion.
 
 .. doctest::
 
-    >>> # Simulating command line arguments
     >>> conf = OmegaConf.create({"foo": "bar", "foo2": "${foo}"})
-    >>> print(type(conf).__name__)
-    DictConfig
+    >>> assert type(conf) == DictConfig
     >>> primitive = OmegaConf.to_container(conf)
-    >>> print(type(primitive).__name__)
-    dict
+    >>> assert type(primitive) == dict
     >>> print(primitive)
     {'foo': 'bar', 'foo2': '${foo}'}
     >>> resolved = OmegaConf.to_container(conf, resolve=True)
@@ -526,12 +521,44 @@ If resolve is set to True the values will be resolved during conversion.
     {'foo': 'bar', 'foo2': 'bar'}
 
 
+OmegaConf.select
+^^^^^^^^^^^^^^^^
+OmegaConf.select() allow you to select a config node or value using a dot-notation key.
+
+.. doctest::
+
+    >>> cfg = OmegaConf.create({"foo" : {"bar": {"zonk" : 10, "missing" : "???"}}})
+    >>> assert OmegaConf.select(cfg, "foo") == {"bar": {"zonk" : 10, "missing" : "???"}}
+    >>> assert OmegaConf.select(cfg, "foo.bar") == {"zonk" : 10, "missing" : "???"}
+    >>> assert OmegaConf.select(cfg, "foo.bar.zonk") == 10
+    >>> assert OmegaConf.select(cfg, "foo.bar.missing") is None
+    >>> OmegaConf.select(cfg, "foo.bar.missing", throw_on_missing=True)
+    Traceback (most recent call last):
+    ...
+    omegaconf.errors.MissingMandatoryValue: missing node selected
+        full_key: foo.bar.missing
+
+OmegaConf.update
+^^^^^^^^^^^^^^^^
+OmegaConf.update() allow you to update values in your config using a dot-notation key.
+
+.. doctest::
+
+    >>> cfg = OmegaConf.create({"foo" : {"bar": 10}})
+    >>> OmegaConf.update(cfg, "foo.bar", 20)
+    >>> assert cfg.foo.bar == 20
+    >>> OmegaConf.update(cfg, "foo.bar", {"zonk" : 30})
+    >>> assert cfg.foo.bar == {"zonk" : 30}
+
+
+
 OmegaConf.masked_copy
 ^^^^^^^^^^^^^^^^^^^^^
 Creates a copy of a DictConfig that contains only specific keys.
+
 .. doctest:: loaded
 
-    >>> conf = OmegaConf.create(dict(a=dict(b=10), c=20))
+    >>> conf = OmegaConf.create({"a": {"b": 10}, "c":20})
     >>> print(conf.pretty())
     a:
       b: 10
diff --git a/news/116.removal.1 b/news/116.removal.1
new file mode 100644
index 0000000..4a15ef5
--- /dev/null
+++ b/news/116.removal.1
@@ -0,0 +1,1 @@
+cfg.select(key) deprecated in favor of OmegaConf.select(cfg, key)
\ No newline at end of file
diff --git a/news/116.removal.2 b/news/116.removal.2
new file mode 100644
index 0000000..508fb95
--- /dev/null
+++ b/news/116.removal.2
@@ -0,0 +1,1 @@
+cfg.update(key, value) deprecated in favor of OmegaConf.update(cfg, key, value)
\ No newline at end of file
diff --git a/omegaconf/base.py b/omegaconf/base.py
index 6249b51..52177ce 100644
--- a/omegaconf/base.py
+++ b/omegaconf/base.py
@@ -194,14 +194,14 @@ class Container(Node):
         ...  # pragma: no cover
 
     @abstractmethod
-    def update_node(self, key: str, value: Any = None) -> None:
+    def update(self, key: str, value: Any = None) -> None:
         ...  # pragma: no cover
 
     @abstractmethod
     def select(self, key: str, throw_on_missing: bool = False) -> Any:
         ...  # pragma: no cover
 
-    def get_node(self, key: Any) -> Optional[Node]:
+    def _get_node(self, key: Any) -> Optional[Node]:
         ...  # pragma: no cover
 
     @abstractmethod
diff --git a/omegaconf/basecontainer.py b/omegaconf/basecontainer.py
index 0a2aa62..07e7d7e 100644
--- a/omegaconf/basecontainer.py
+++ b/omegaconf/basecontainer.py
@@ -1,5 +1,6 @@
 import copy
 import sys
+import warnings
 from abc import ABC, abstractmethod
 from enum import Enum
 from typing import Any, Dict, List, Optional, Tuple, Union
@@ -93,6 +94,8 @@ class BaseContainer(Container, ABC):
         self.merge_with_dotlist(args_list)
 
     def merge_with_dotlist(self, dotlist: List[str]) -> None:
+        from omegaconf import OmegaConf
+
         def fail() -> None:
             raise ValueError("Input list must be a list or a tuple of strings")
 
@@ -112,50 +115,27 @@ class BaseContainer(Container, ABC):
                 value = arg[idx + 1 :]
                 value = yaml.load(value, Loader=get_yaml_loader())
 
-            self.update_node(key, value)
+            OmegaConf.update(self, key, value)
 
-    def update_node(self, key: str, value: Any = None) -> None:
-        from .dictconfig import DictConfig
-        from .listconfig import ListConfig
-        from .omegaconf import _select_one
+    def update(self, key: str, value: Any = None) -> None:
+        from omegaconf import OmegaConf
+
+        warnings.warn(
+            "update() is deprecated, use OmegaConf.update(). (Since 2.0)",
+            category=DeprecationWarning,
+        )
 
-        """Updates a dot separated key sequence to a value"""
-        split = key.split(".")
-        root = self
-        for i in range(len(split) - 1):
-            k = split[i]
-            # if next_root is a primitive (string, int etc) replace it with an empty map
-            next_root, key_ = _select_one(root, k, throw_on_missing=False)
-            if not isinstance(next_root, Container):
-                root[key_] = {}
-            root = root[key_]
-
-        last = split[-1]
-
-        assert isinstance(
-            root, Container
-        ), f"Unexpected type for root : {type(root).__name__}"
-
-        if isinstance(root, DictConfig):
-            setattr(root, last, value)
-        elif isinstance(root, ListConfig):
-            idx = int(last)
-            root[idx] = value
+        return OmegaConf.update(self, key, value)
 
     def select(self, key: str, throw_on_missing: bool = False) -> Any:
-        try:
-            _root, _last_key, value = self._select_impl(key, throw_on_missing=False)
-            if value is not None and value._is_missing():
-                if throw_on_missing:
-                    raise MissingMandatoryValue(
-                        f"Missing mandatory value : {self._get_full_key('')}"
-                    )
-                else:
-                    return None
+        from omegaconf import OmegaConf
+
+        warnings.warn(
+            "select() is deprecated, use OmegaConf.select(). (Since 2.0)",
+            category=DeprecationWarning,
+        )
 
-            return _get_value(value)
-        except Exception as e:
-            self._format_and_raise(key=key, value=None, cause=e)
+        return OmegaConf.select(self, key, throw_on_missing)
 
     def is_empty(self) -> bool:
         """return true if config is empty"""
@@ -242,11 +222,11 @@ class BaseContainer(Container, ABC):
                     if OmegaConf.is_missing(dest, key):
                         dest[key] = src_value
 
-            if (dest.get_node(key) is not None) or element_typed:
-                dest_node = dest.get_node(key)
+            if (dest._get_node(key) is not None) or element_typed:
+                dest_node = dest._get_node(key)
                 if dest_node is None and element_typed:
                     dest[key] = DictConfig(content=dest_element_type, parent=dest)
-                    dest_node = dest.get_node(key)
+                    dest_node = dest._get_node(key)
 
                 if isinstance(dest_node, BaseContainer):
                     if isinstance(src_value, BaseContainer):
@@ -261,7 +241,7 @@ class BaseContainer(Container, ABC):
                         assert isinstance(dest_node, ValueNode)
                         dest_node._set_value(src_value)
             else:
-                dest[key] = src.get_node(key)
+                dest[key] = src._get_node(key)
 
         if src_type is not Any and not is_primitive_dict(src_type):
             dest._metadata.object_type = src_type
@@ -314,7 +294,7 @@ class BaseContainer(Container, ABC):
             self._validate_set(key, value)
 
         input_config = isinstance(value, Container)
-        target_node_ref = self.get_node(key)
+        target_node_ref = self._get_node(key)
         special_value = value is None or value == "???"
         should_set_value = (
             target_node_ref is not None
@@ -338,7 +318,7 @@ class BaseContainer(Container, ABC):
             ):
                 ref_type = self._metadata.element_type
             else:
-                target = self.get_node(key)
+                target = self._get_node(key)
                 if target is None:
                     if is_structured_config(val):
                         ref_type = OmegaConf.get_type(val)
@@ -385,8 +365,8 @@ class BaseContainer(Container, ABC):
         c2: "BaseContainer",
         k2: Union[str, int],
     ) -> bool:
-        v1 = c1.get_node(k1)
-        v2 = c2.get_node(k2)
+        v1 = c1._get_node(k1)
+        v2 = c2._get_node(k2)
         v1_kind = get_value_kind(v1)
         v2_kind = get_value_kind(v2)
 
diff --git a/omegaconf/dictconfig.py b/omegaconf/dictconfig.py
index c14df8b..f23a4b1 100644
--- a/omegaconf/dictconfig.py
+++ b/omegaconf/dictconfig.py
@@ -38,7 +38,7 @@ from .errors import (
 from .nodes import EnumNode, ValueNode
 
 
-class DictConfig(BaseContainer, MutableMapping[str, Any]):
+class DictConfig(BaseContainer, MutableMapping[str, Any]):  # type: ignore
 
     _metadata: ContainerMetadata
 
@@ -133,7 +133,7 @@ class DictConfig(BaseContainer, MutableMapping[str, Any]):
 
         if OmegaConf.is_none(value):
             if key is not None:
-                node = self.get_node(key)
+                node = self._get_node(key)
                 if node is not None and not node._is_optional():
                     raise ValidationError("field '$FULL_KEY' is not Optional")
             else:
@@ -154,7 +154,7 @@ class DictConfig(BaseContainer, MutableMapping[str, Any]):
         if key is None:
             target = self
         else:
-            target = self.get_node(key)
+            target = self._get_node(key)
 
         if (target is not None and target._get_flag("readonly")) or self._get_flag(
             "readonly"
@@ -332,7 +332,7 @@ class DictConfig(BaseContainer, MutableMapping[str, Any]):
             key=key, value=node, default_value=default_value,
         )
 
-    def get_node(self, key: Union[str, Enum]) -> Optional[Node]:
+    def _get_node(self, key: Union[str, Enum]) -> Optional[Node]:
         return self.get_node_ex(key, default_value=DEFAULT_VALUE_MARKER)
 
     def get_node_ex(
@@ -366,7 +366,7 @@ class DictConfig(BaseContainer, MutableMapping[str, Any]):
                     cause=ReadonlyConfigError("Cannot pop from read-only node"),
                 )
 
-            node = self.get_node(key=key)
+            node = self._get_node(key=key)
             if node is not None:
                 value = self._resolve_with_default(
                     key=key, value=node, default_value=default
@@ -403,7 +403,7 @@ class DictConfig(BaseContainer, MutableMapping[str, Any]):
 
         key = self._validate_and_normalize_key(key)
         try:
-            node: Optional[Node] = self.get_node(key)
+            node: Optional[Node] = self._get_node(key)
         except (KeyError, AttributeError):
             node = None
 
diff --git a/omegaconf/listconfig.py b/omegaconf/listconfig.py
index 6426e31..fed3f70 100644
--- a/omegaconf/listconfig.py
+++ b/omegaconf/listconfig.py
@@ -65,7 +65,7 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
             raise ReadonlyConfigError("ListConfig is read-only")
 
         if 0 <= key < self.__len__():
-            target = self.get_node(key)
+            target = self._get_node(key)
             if target is not None:
                 if value is None and not target._is_optional():
                     raise ValidationError(
@@ -147,7 +147,7 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
 
     def _update_keys(self) -> None:
         for i in range(len(self)):
-            node = self.get_node(i)
+            node = self._get_node(i)
             if node is not None:
                 node._metadata.key = i
 
@@ -242,7 +242,7 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
     def copy(self) -> "ListConfig":
         return copy.copy(self)
 
-    def get_node(self, key: Any) -> Optional[Node]:
+    def _get_node(self, key: Any) -> Optional[Node]:
         return self.get_node_ex(key)
 
     def get_node_ex(self, key: Any, validate_access: bool = True) -> Optional[Node]:
@@ -289,7 +289,7 @@ class ListConfig(BaseContainer, MutableSequence[Any]):
 
             assert isinstance(self._content, list)
             ret = self._resolve_with_default(
-                key=index, value=self.get_node(index), default_value=None
+                key=index, value=self._get_node(index), default_value=None
             )
             del self._content[index]
             self._update_keys()
diff --git a/omegaconf/omegaconf.py b/omegaconf/omegaconf.py
index e609aeb..eb7cf5f 100644
--- a/omegaconf/omegaconf.py
+++ b/omegaconf/omegaconf.py
@@ -29,7 +29,9 @@ from typing_extensions import Protocol
 from . import DictConfig, ListConfig
 from ._utils import (
     ValueKind,
+    _get_value,
     decode_primitive,
+    format_and_raise,
     get_type_of,
     get_value_kind,
     is_primitive_container,
@@ -382,7 +384,7 @@ class OmegaConf:
     @staticmethod
     def is_missing(cfg: BaseContainer, key: Union[int, str]) -> bool:
         try:
-            node = cfg.get_node(key)
+            node = cfg._get_node(key)
             if node is None:
                 return False
             return node._is_missing()
@@ -393,7 +395,7 @@ class OmegaConf:
     def is_optional(obj: Any, key: Optional[Union[int, str]] = None) -> bool:
         if key is not None:
             assert isinstance(obj, Container)
-            obj = obj.get_node(key)
+            obj = obj._get_node(key)
         if isinstance(obj, Node):
             return obj._is_optional()
         else:
@@ -403,7 +405,7 @@ class OmegaConf:
     def is_none(obj: Any, key: Optional[Union[int, str]] = None) -> bool:
         if key is not None:
             assert isinstance(obj, Container)
-            obj = obj.get_node(key)
+            obj = obj._get_node(key)
         if isinstance(obj, Node):
             return obj._is_none()
         else:
@@ -413,7 +415,7 @@ class OmegaConf:
     def is_interpolation(node: Node, key: Optional[Union[int, str]] = None) -> bool:
         if key is not None:
             assert isinstance(node, Container)
-            target = node.get_node(key)
+            target = node._get_node(key)
         else:
             target = node
         if target is not None:
@@ -449,7 +451,7 @@ class OmegaConf:
                 return get_type_of(obj)
 
         if key is not None:
-            c = obj.get_node(key)
+            c = obj._get_node(key)
         else:
             c = obj
 
@@ -477,7 +479,7 @@ class OmegaConf:
     @staticmethod
     def get_ref_type(obj: Any, key: Optional[str] = None) -> Optional[Type[Any]]:
         if key is not None:
-            c = obj.get_node(key)
+            c = obj._get_node(key)
         else:
             c = obj
 
@@ -486,6 +488,45 @@ class OmegaConf:
         else:
             return None
 
+    @staticmethod
+    def select(cfg: Container, key: str, throw_on_missing: bool = False) -> Any:
+        try:
+            _root, _last_key, value = cfg._select_impl(key, throw_on_missing=False)
+            if value is not None and value._is_missing():
+                if throw_on_missing:
+                    raise MissingMandatoryValue("missing node selected")
+                else:
+                    return None
+
+            return _get_value(value)
+        except Exception as e:
+            format_and_raise(node=cfg, key=key, value=None, cause=e, msg=str(e))
+
+    @staticmethod
+    def update(cfg: Container, key: str, value: Any) -> None:
+        """Updates a dot separated key sequence to a value"""
+        split = key.split(".")
+        root = cfg
+        for i in range(len(split) - 1):
+            k = split[i]
+            # if next_root is a primitive (string, int etc) replace it with an empty map
+            next_root, key_ = _select_one(root, k, throw_on_missing=False)
+            if not isinstance(next_root, Container):
+                root[key_] = {}
+            root = root[key_]
+
+        last = split[-1]
+
+        assert isinstance(
+            root, Container
+        ), f"Unexpected type for root : {type(root).__name__}"
+
+        if isinstance(root, DictConfig):
+            setattr(root, last, value)
+        elif isinstance(root, ListConfig):
+            idx = int(last)
+            root[idx] = value
+
 
 # register all default resolvers
 register_default_resolvers()
@@ -507,7 +548,6 @@ def flag_override(
 # noinspection PyProtectedMember
 @contextmanager
 def read_write(config: Node) -> Generator[Node, None, None]:
-    # noinspection PyProtectedMember
     prev_state = config._get_node_flag("readonly")
     try:
         OmegaConf.set_readonly(config, False)
@@ -516,9 +556,9 @@ def read_write(config: Node) -> Generator[Node, None, None]:
         OmegaConf.set_readonly(config, prev_state)
 
 
+# noinspection PyProtectedMember
 @contextmanager
 def open_dict(config: Container) -> Generator[Container, None, None]:
-    # noinspection PyProtectedMember
     prev_state = config._get_node_flag("struct")
     try:
         OmegaConf.set_struct(config, False)
@@ -673,7 +713,7 @@ def _select_one(
             if ret_key < 0 or ret_key + 1 > len(c):
                 val = None
             else:
-                val = c.get_node(ret_key)
+                val = c._get_node(ret_key)
     else:
         assert False
 


Test patch:
diff --git a/tests/structured_conf/test_structured_config.py b/tests/structured_conf/test_structured_config.py
index fd40375..a560e95 100644
--- a/tests/structured_conf/test_structured_config.py
+++ b/tests/structured_conf/test_structured_config.py
@@ -317,12 +317,12 @@ class TestConfigs:
             # noinspection PyStatementEffect
             conf.mandatory_missing
 
-        assert type(conf.get_node("null_default")) == AnyNode
-        assert type(conf.get_node("int_default")) == AnyNode
-        assert type(conf.get_node("float_default")) == AnyNode
-        assert type(conf.get_node("str_default")) == AnyNode
-        assert type(conf.get_node("bool_default")) == AnyNode
-        assert type(conf.get_node("mandatory_missing")) == AnyNode
+        assert type(conf._get_node("null_default")) == AnyNode
+        assert type(conf._get_node("int_default")) == AnyNode
+        assert type(conf._get_node("float_default")) == AnyNode
+        assert type(conf._get_node("str_default")) == AnyNode
+        assert type(conf._get_node("bool_default")) == AnyNode
+        assert type(conf._get_node("mandatory_missing")) == AnyNode
 
         assert conf.int_default == expected.int_default
         with pytest.raises(ValidationError):
diff --git a/tests/test_basic_ops_dict.py b/tests/test_basic_ops_dict.py
index d937168..6d52dab 100644
--- a/tests/test_basic_ops_dict.py
+++ b/tests/test_basic_ops_dict.py
@@ -403,7 +403,7 @@ def test_dir(cfg: Any, key: Any, expected: Any) -> None:
     if key is None:
         assert dir(c) == expected
     else:
-        assert dir(c.get_node(key)) == expected
+        assert dir(c._get_node(key)) == expected
 
 
 def test_hash() -> None:
@@ -541,11 +541,11 @@ def test_is_missing() -> None:
             "missing_node_inter": "${missing_node}",
         }
     )
-    assert cfg.get_node("foo")._is_missing()  # type:ignore
-    assert cfg.get_node("inter")._is_missing()  # type:ignore
-    assert cfg.get_node("str_inter")._is_missing()  # type:ignore
-    assert cfg.get_node("missing_node")._is_missing()  # type:ignore
-    assert cfg.get_node("missing_node_inter")._is_missing()  # type:ignore
+    assert cfg._get_node("foo")._is_missing()  # type:ignore
+    assert cfg._get_node("inter")._is_missing()  # type:ignore
+    assert cfg._get_node("str_inter")._is_missing()  # type:ignore
+    assert cfg._get_node("missing_node")._is_missing()  # type:ignore
+    assert cfg._get_node("missing_node_inter")._is_missing()  # type:ignore
 
 
 @pytest.mark.parametrize("ref_type", [None, Any])  # type: ignore
diff --git a/tests/test_basic_ops_list.py b/tests/test_basic_ops_list.py
index 611fa51..f5a8a29 100644
--- a/tests/test_basic_ops_list.py
+++ b/tests/test_basic_ops_list.py
@@ -235,7 +235,7 @@ def test_list_dir() -> None:
 def validate_list_keys(c: Any) -> None:
     # validate keys are maintained
     for i in range(len(c)):
-        assert c.get_node(i)._metadata.key == i
+        assert c._get_node(i)._metadata.key == i
 
 
 @pytest.mark.parametrize(  # type: ignore
@@ -281,7 +281,7 @@ def test_insert(
     if expectation is None:
         c.insert(index, value)
         assert c == expected
-        assert type(c.get_node(index)) == expected_node_type
+        assert type(c._get_node(index)) == expected_node_type
     else:
         with pytest.raises(expectation):
             c.insert(index, value)
diff --git a/tests/test_errors.py b/tests/test_errors.py
index 0471de3..f78f956 100644
--- a/tests/test_errors.py
+++ b/tests/test_errors.py
@@ -33,27 +33,27 @@ params = [
     ##############
     # DictConfig #
     ##############
-    # update_node
+    # update
     pytest.param(
         lambda: OmegaConf.structured(StructuredWithMissing),
-        lambda cfg: cfg.update_node("num", "hello"),
+        lambda cfg: OmegaConf.update(cfg, "num", "hello"),
         ValidationError,
         "Value 'hello' could not be converted to Integer",
-        id="structured:update_node_with_invalid_value",
+        id="structured:update_with_invalid_value",
     ),
     pytest.param(
         lambda: OmegaConf.structured(StructuredWithMissing),
-        lambda cfg: cfg.update_node("num", None),
+        lambda cfg: OmegaConf.update(cfg, "num", None),
         ValidationError,
         "field 'num' is not Optional",
-        id="structured:update_node:none_to_non_optional",
+        id="structured:update:none_to_non_optional",
     ),
     pytest.param(
         lambda: OmegaConf.create({}),
-        lambda cfg: cfg.update_node("a", IllegalType()),
+        lambda cfg: OmegaConf.update(cfg, "a", IllegalType()),
         UnsupportedValueType,
         "Value 'IllegalType' is not a supported primitive type",
-        id="dict:update_node:object_of_illegal_type",
+        id="dict:update:object_of_illegal_type",
     ),
     # pop
     pytest.param(
diff --git a/tests/test_get_full_key.py b/tests/test_get_full_key.py
index eef4e71..aeb26f1 100644
--- a/tests/test_get_full_key.py
+++ b/tests/test_get_full_key.py
@@ -75,13 +75,13 @@ def test_get_full_key_from_config(
     cfg: Any, select: str, key: Any, expected: Any
 ) -> None:
     c = OmegaConf.create(cfg)
-    node = c.select(select)
+    node = OmegaConf.select(c, select)
     assert node._get_full_key(key) == expected
 
 
 def test_value_node_get_full_key() -> None:
     cfg = OmegaConf.create({"foo": IntegerNode(value=10)})
-    assert cfg.get_node("foo")._get_full_key(None) == "foo"  # type: ignore
+    assert cfg._get_node("foo")._get_full_key(None) == "foo"  # type: ignore
 
     node = IntegerNode(value=10)
     assert node._get_full_key(None) == ""
diff --git a/tests/test_interpolation.py b/tests/test_interpolation.py
index d4bf56b..eb57d8d 100644
--- a/tests/test_interpolation.py
+++ b/tests/test_interpolation.py
@@ -121,7 +121,7 @@ def test_complex_str_interpolation_is_always_str_1() -> None:
 )
 def test_interpolation(input_: Dict[str, Any], key: str, expected: str) -> None:
     c = OmegaConf.create(input_)
-    assert c.select(key) == expected
+    assert OmegaConf.select(c, key) == expected
 
 
 def test_2_step_interpolation() -> None:
@@ -342,7 +342,7 @@ def test_incremental_dict_with_interpolation() -> None:
 )
 def test_interpolations(cfg: DictConfig, key: str, expected: Any) -> None:
     c = OmegaConf.create(cfg)
-    assert c.select(key) == expected
+    assert OmegaConf.select(c, key) == expected
 
 
 def test_interpolation_with_missing() -> None:
diff --git a/tests/test_matrix.py b/tests/test_matrix.py
index 28e2699..070eec2 100644
--- a/tests/test_matrix.py
+++ b/tests/test_matrix.py
@@ -31,7 +31,7 @@ def verify(
     inter: bool,
     exp: Any = SKIP,
 ) -> None:
-    target_node = cfg.get_node(key)
+    target_node = cfg._get_node(key)
     assert target_node._key() == key
     assert target_node._is_none() == none
     assert target_node._is_optional() == opt
@@ -121,14 +121,14 @@ class TestNodeTypesMatrix:
             verify(cfg, "node", none=True, opt=True, missing=False, inter=False)
 
             cfg.node = value
-            assert cfg.get_node("node") == value
+            assert cfg._get_node("node") == value
             verify(cfg, "node", none=False, opt=True, missing=False, inter=False)
 
             cfg.node = "???"
             verify(cfg, "node", none=False, opt=True, missing=True, inter=False)
 
             cfg.node = value_backup
-            assert cfg.get_node("node") == value_backup
+            assert cfg._get_node("node") == value_backup
             verify(cfg, "node", none=False, opt=True, missing=False, inter=False)
 
     def test_none_assignment_in_list(self, node_type: Any, values: Any) -> None:
diff --git a/tests/test_nodes.py b/tests/test_nodes.py
index d0aae03..25711ee 100644
--- a/tests/test_nodes.py
+++ b/tests/test_nodes.py
@@ -112,7 +112,7 @@ def test_assigned_value_node_type(input_: type, expected_type: Any) -> None:
     c = OmegaConf.create()
     assert isinstance(c, DictConfig)
     c.foo = input_
-    assert type(c.get_node("foo")) == expected_type
+    assert type(c._get_node("foo")) == expected_type
 
 
 def test_get_node_no_validate_access() -> None:
@@ -137,18 +137,18 @@ def test_dict_any() -> None:
     c[Enum1.FOO] = "bar"
 
     assert c.foo == 10
-    assert type(c.get_node("foo")) == AnyNode
+    assert type(c._get_node("foo")) == AnyNode
     c.foo = "string"
     assert c.foo == "string"
 
-    assert type(c.get_node(Enum1.FOO)) == AnyNode
+    assert type(c._get_node(Enum1.FOO)) == AnyNode
 
 
 def test_dict_integer_1() -> None:
     c = OmegaConf.create()
     assert isinstance(c, DictConfig)
     c.foo = IntegerNode(10)
-    assert type(c.get_node("foo")) == IntegerNode
+    assert type(c._get_node("foo")) == IntegerNode
     assert c.foo == 10
 
 
@@ -159,7 +159,7 @@ def test_list_any() -> None:
     # default type is Any
     c.append(10)
     assert c[0] == 10
-    assert type(c.get_node(0)) == AnyNode
+    assert type(c._get_node(0)) == AnyNode
     c[0] = "string"
     assert c[0] == "string"
 
@@ -169,7 +169,7 @@ def test_list_integer() -> None:
     c = OmegaConf.create([])
     assert isinstance(c, ListConfig)
     c.append(IntegerNode(val))
-    assert type(c.get_node(0)) == IntegerNode
+    assert type(c._get_node(0)) == IntegerNode
     assert c.get(0) == val
 
 
@@ -181,7 +181,7 @@ def test_list_integer_rejects_string() -> None:
     with pytest.raises(ValidationError):
         c[0] = "string"
     assert c[0] == 10
-    assert type(c.get_node(0)) == IntegerNode
+    assert type(c._get_node(0)) == IntegerNode
 
 
 # Test merge raises validation error
@@ -224,11 +224,11 @@ def test_accepts_mandatory_missing(
     conf = OmegaConf.create({"foo": node})
     assert isinstance(conf, DictConfig)
     assert "foo" not in conf
-    assert type(conf.get_node("foo")) == type_
+    assert type(conf._get_node("foo")) == type_
 
     conf.foo = valid_value
     # make sure valid assignment does not change the type
-    assert type(conf.get_node("foo")) == type_
+    assert type(conf._get_node("foo")) == type_
     assert "foo" in conf
     assert conf.foo == valid_value
 
diff --git a/tests/test_readonly.py b/tests/test_readonly.py
index 29b93de..5be9af2 100644
--- a/tests/test_readonly.py
+++ b/tests/test_readonly.py
@@ -18,7 +18,7 @@ from omegaconf import DictConfig, ListConfig, OmegaConf, ReadonlyConfigError
         ),
         (
             {},
-            lambda c: c.update_node("a.b", 10),
+            lambda c: OmegaConf.update(c, "a.b", 10),
             raises(ReadonlyConfigError, match="a"),
         ),
         (
@@ -36,7 +36,7 @@ from omegaconf import DictConfig, ListConfig, OmegaConf, ReadonlyConfigError
         ([], lambda c: c.__setitem__(0, 1), raises(ReadonlyConfigError, match="0")),
         (
             [],
-            lambda c: c.update_node("0.b", 10),
+            lambda c: OmegaConf.update(c, "0.b", 10),
             raises(ReadonlyConfigError, match="[0]"),
         ),
         ([10], lambda c: c.pop(), raises(ReadonlyConfigError)),
diff --git a/tests/test_select.py b/tests/test_select.py
index e55a933..4400bd0 100644
--- a/tests/test_select.py
+++ b/tests/test_select.py
@@ -12,7 +12,7 @@ from . import does_not_raise
 def test_select_key_from_empty(struct: Optional[bool]) -> None:
     c = OmegaConf.create()
     OmegaConf.set_struct(c, struct)
-    assert c.select("not_there") is None
+    assert OmegaConf.select(c, "not_there") is None
 
 
 @pytest.mark.parametrize(  # type: ignore
@@ -67,12 +67,18 @@ def test_select(
     c = OmegaConf.create(cfg)
     with expectation:
         for idx, key in enumerate(keys):
-            assert c.select(key) == expected[idx]
+            assert OmegaConf.select(c, key) == expected[idx]
 
 
 def test_select_from_dict() -> None:
     c = OmegaConf.create({"missing": "???"})
     with pytest.raises(MissingMandatoryValue):
-        c.select("missing", throw_on_missing=True)
-    assert c.select("missing", throw_on_missing=False) is None
-    assert c.select("missing") is None
+        OmegaConf.select(c, "missing", throw_on_missing=True)
+    assert OmegaConf.select(c, "missing", throw_on_missing=False) is None
+    assert OmegaConf.select(c, "missing") is None
+
+
+def test_select_deprecated() -> None:
+    c = OmegaConf.create({"foo": "bar"})
+    with pytest.deprecated_call():
+        c.select("foo")
diff --git a/tests/test_update.py b/tests/test_update.py
index d6f02fe..bc2cf6f 100644
--- a/tests/test_update.py
+++ b/tests/test_update.py
@@ -11,7 +11,7 @@ def test_update_map_value() -> None:
     # Replacing an existing key in a map
     s = "hello: world"
     c = OmegaConf.create(s)
-    c.update_node("hello", "there")
+    c.update("hello", "there")
     assert {"hello": "there"} == c
 
 
@@ -19,7 +19,7 @@ def test_update_map_new_keyvalue() -> None:
     # Adding another key to a map
     s = "hello: world"
     c = OmegaConf.create(s)
-    c.update_node("who", "goes there")
+    c.update("who", "goes there")
     assert {"hello": "world", "who": "goes there"} == c
 
 
@@ -27,56 +27,56 @@ def test_update_map_to_value() -> None:
     # changing map to single node
     s = "hello: world"
     c = OmegaConf.create(s)
-    c.update_node("value")
+    c.update("value")
     assert {"hello": "world", "value": None} == c
 
 
 def test_update_with_empty_map_value() -> None:
     c = OmegaConf.create()
-    c.update_node("a", {})
+    c.update("a", {})
     assert {"a": {}} == c
 
 
 def test_update_with_map_value() -> None:
     c = OmegaConf.create()
-    c.update_node("a", {"aa": 1, "bb": 2})
+    c.update("a", {"aa": 1, "bb": 2})
     assert {"a": {"aa": 1, "bb": 2}} == c
 
 
 def test_update_deep_from_empty() -> None:
     c = OmegaConf.create()
-    c.update_node("a.b", 1)
+    c.update("a.b", 1)
     assert {"a": {"b": 1}} == c
 
 
 def test_update_deep_with_map() -> None:
     c = OmegaConf.create("a: b")
-    c.update_node("a.b", {"c": 1})
+    c.update("a.b", {"c": 1})
     assert {"a": {"b": {"c": 1}}} == c
 
 
 def test_update_deep_with_value() -> None:
     c = OmegaConf.create()
-    c.update_node("a.b", 1)
+    c.update("a.b", 1)
     assert {"a": {"b": 1}} == c
 
 
 def test_update_deep_with_map2() -> None:
     c = OmegaConf.create("a: 1")
-    c.update_node("b.c", 2)
+    c.update("b.c", 2)
     assert {"a": 1, "b": {"c": 2}} == c
 
 
 def test_update_deep_with_map_update() -> None:
     c = OmegaConf.create("a: {b : {c: 1}}")
-    c.update_node("a.b.d", 2)
+    c.update("a.b.d", 2)
     assert {"a": {"b": {"c": 1, "d": 2}}} == c
 
 
 def test_list_value_update() -> None:
     # List update is always a replace because a list can be merged in too many ways
     c = OmegaConf.create("a: [1,2]")
-    c.update_node("a", [2, 3, 4])
+    c.update("a", [2, 3, 4])
     assert {"a": [2, 3, 4]} == c
 
 
@@ -85,7 +85,7 @@ def test_override_mandatory_value() -> None:
     assert isinstance(c, DictConfig)
     with raises(MissingMandatoryValue):
         c.get("a")
-    c.update_node("a", 123)
+    c.update("a", 123)
     assert {"a": 123} == c
 
 
@@ -93,7 +93,7 @@ def test_update_empty_to_value() -> None:
     """"""
     s = ""
     c = OmegaConf.create(s)
-    c.update_node("hello")
+    c.update("hello")
     assert {"hello": None} == c
 
 
@@ -101,31 +101,31 @@ def test_update_same_value() -> None:
     """"""
     s = "hello"
     c = OmegaConf.create(s)
-    c.update_node("hello")
+    c.update("hello")
     assert {"hello": None} == c
 
 
 def test_update_value_to_map() -> None:
     s = "hello"
     c = OmegaConf.create(s)
-    c.update_node("hi", "there")
+    c.update("hi", "there")
     assert {"hello": None, "hi": "there"} == c
 
 
 def test_update_map_empty_to_map() -> None:
     s = ""
     c = OmegaConf.create(s)
-    c.update_node("hello", "there")
+    c.update("hello", "there")
     assert {"hello": "there"} == c
 
 
 def test_update_list() -> None:
     c = OmegaConf.create([1, 2, 3])
     assert isinstance(c, ListConfig)
-    c.update_node("1", "abc")
-    c.update_node("-1", "last")
+    c.update("1", "abc")
+    c.update("-1", "last")
     with raises(IndexError):
-        c.update_node("4", "abc")
+        c.update("4", "abc")
 
     assert len(c) == 3
     assert c[0] == 1
@@ -135,10 +135,10 @@ def test_update_list() -> None:
 
 def test_update_nested_list() -> None:
     c = OmegaConf.create(dict(deep=dict(list=[1, 2, 3])))
-    c.update_node("deep.list.1", "abc")
-    c.update_node("deep.list.-1", "last")
+    c.update("deep.list.1", "abc")
+    c.update("deep.list.-1", "last")
     with raises(IndexError):
-        c.update_node("deep.list.4", "abc")
+        c.update("deep.list.4", "abc")
 
     assert c.deep.list[0] == 1
     assert c.deep.list[1] == "abc"
@@ -148,10 +148,10 @@ def test_update_nested_list() -> None:
 def test_update_list_make_dict() -> None:
     c = OmegaConf.create([None, None])
     assert isinstance(c, ListConfig)
-    c.update_node("0.a.a", "aa")
-    c.update_node("0.a.b", "ab")
-    c.update_node("1.b.a", "ba")
-    c.update_node("1.b.b", "bb")
+    c.update("0.a.a", "aa")
+    c.update("0.a.b", "ab")
+    c.update("1.b.a", "ba")
+    c.update("1.b.b", "bb")
     assert c[0].a.a == "aa"
     assert c[0].a.b == "ab"
     assert c[1].b.a == "ba"
@@ -198,3 +198,9 @@ def test_merge_with_dotlist_errors(dotlist: List[str]) -> None:
     c = OmegaConf.create()
     with pytest.raises(ValueError):
         c.merge_with_dotlist(dotlist)
+
+
+def test_update_deprecated() -> None:
+    c = OmegaConf.create()
+    with pytest.deprecated_call():
+        c.update("foo", "bar")
diff --git a/tests/test_utils.py b/tests/test_utils.py
index d0aeead..24d95ec 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -211,9 +211,9 @@ def test_value_kind(value: Any, kind: _utils.ValueKind) -> None:
 def test_re_parent() -> None:
     def validate(cfg1: DictConfig) -> None:
         assert cfg1._get_parent() is None
-        assert cfg1.get_node("str")._get_parent() == cfg1  # type:ignore
-        assert cfg1.get_node("list")._get_parent() == cfg1  # type:ignore
-        assert cfg1.list.get_node(0)._get_parent() == cfg1.list
+        assert cfg1._get_node("str")._get_parent() == cfg1  # type:ignore
+        assert cfg1._get_node("list")._get_parent() == cfg1  # type:ignore
+        assert cfg1.list._get_node(0)._get_parent() == cfg1.list
 
     cfg = OmegaConf.create({})
     assert isinstance(cfg, DictConfig)
@@ -222,9 +222,9 @@ def test_re_parent() -> None:
 
     validate(cfg)
 
-    cfg.get_node("str")._set_parent(None)  # type:ignore
-    cfg.get_node("list")._set_parent(None)  # type:ignore
-    cfg.list.get_node(0)._set_parent(None)  # type: ignore
+    cfg._get_node("str")._set_parent(None)  # type:ignore
+    cfg._get_node("list")._set_parent(None)  # type:ignore
+    cfg.list._get_node(0)._set_parent(None)  # type: ignore
     # noinspection PyProtectedMember
     cfg._re_parent()
     validate(cfg)