coveralls: ## Pull Request Test Coverage Report for [Build 107](https://coveralls.io/builds/26851120)

* **0** of **0**   changed or added relevant lines in **0** files are covered.
* No unchanged relevant lines lost coverage.
* Overall coverage remained the same at ?**%**

---



|  Totals | [![Coverage Status](https://coveralls.io/builds/26851120/badge)](https://coveralls.io/builds/26851120) |
| :-- | --: |
| Change from base [Build 99](https://coveralls.io/builds/26825314): |  0.0% |
| Covered Lines: |  |
| Relevant Lines: | 0 |

---
##### 💛  - [Coveralls](https://coveralls.io)

diff --git a/.circleci/config.yml b/.circleci/config.yml
index acd7272..dcd4af0 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -5,7 +5,7 @@ jobs:
   py27_linux:
     docker:
       # nox got requires python 3 but it can test 2.7
-      - image: circleci/python:3.6
+      - image: circleci/python:3.7
     steps:
       - checkout
       - run: echo 'export NOX_PYTHON_VERSIONS=2.7' >> $BASH_ENV
@@ -14,7 +14,7 @@ jobs:
 
   py35_linux:
     docker:
-      - image: circleci/python:3.5
+      - image: circleci/python:3.7
     steps:
       - checkout
       - run: echo 'export NOX_PYTHON_VERSIONS=3.5' >> $BASH_ENV
@@ -23,7 +23,7 @@ jobs:
 
   py36_linux:
     docker:
-      - image: circleci/python:3.6
+      - image: circleci/python:3.7
     steps:
       - checkout
       - run: echo 'export NOX_PYTHON_VERSIONS=3.6' >> $BASH_ENV
@@ -42,7 +42,7 @@ jobs:
   # Misc
   coverage:
     docker:
-      - image: circleci/python:3.6
+      - image: circleci/python:3.7
     steps:
       - checkout
       - run: sudo pip install nox
diff --git a/news/66.removal b/news/66.removal
new file mode 100644
index 0000000..039ff6d
--- /dev/null
+++ b/news/66.removal
@@ -0,0 +1,1 @@
+Deprecated config.save(file) in favor of OmegaConf.save(config, file)
diff --git a/noxfile.py b/noxfile.py
index 6adfd9a..cd8a450 100644
--- a/noxfile.py
+++ b/noxfile.py
@@ -26,7 +26,7 @@ def omegaconf(session):
 
 
 # code coverage runs with python 3.6
-@nox.session(python="3.6")
+@nox.session(python="3.7")
 def coverage(session):
     session.install("--upgrade", "setuptools", "pip")
     session.install("coverage", "pytest")
@@ -41,7 +41,7 @@ def coverage(session):
     session.run("coveralls", success_codes=[0, 1])
 
 
-@nox.session(python="3.6")
+@nox.session(python="3.7")
 def lint(session):
     session.install("--upgrade", "setuptools", "pip")
     session.run("pip", "install", ".[lint]", silent=True)
diff --git a/omegaconf/config.py b/omegaconf/config.py
index 2fee5ee..61de58c 100644
--- a/omegaconf/config.py
+++ b/omegaconf/config.py
@@ -1,12 +1,10 @@
 import copy
-import io
-import os
-import re
 import sys
 import warnings
 from abc import abstractmethod
 from collections import defaultdict
 
+import re
 import six
 import yaml
 
@@ -65,15 +63,15 @@ class Config(object):
         self.__dict__["_resolver_cache"] = defaultdict(dict)
 
     def save(self, f):
-        data = self.pretty()
-        if isinstance(f, str):
-            with io.open(os.path.abspath(f), "w", encoding="utf-8") as f:
-                f.write(six.u(data))
-        elif hasattr(f, "write"):
-            f.write(data)
-            f.flush()
-        else:
-            raise TypeError("Unexpected file type")
+        warnings.warn(
+            "Use OmegaConf.save(config, filename) (since 1.4.0)",
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+        from omegaconf import OmegaConf
+
+        OmegaConf.save(self, f)
 
     def _set_parent(self, parent):
         assert parent is None or isinstance(parent, Config)
diff --git a/omegaconf/omegaconf.py b/omegaconf/omegaconf.py
index a8e75d5..e43f3be 100644
--- a/omegaconf/omegaconf.py
+++ b/omegaconf/omegaconf.py
@@ -1,12 +1,13 @@
 """OmegaConf module"""
 import copy
-import io
 import os
-import re
 import sys
 import warnings
 from contextlib import contextmanager
 
+import io
+import re
+import six
 import yaml
 
 from .config import Config
@@ -72,6 +73,23 @@ class OmegaConf:
         else:
             raise TypeError("Unexpected file type")
 
+    @staticmethod
+    def save(config, f):
+        """
+        Save as configuration object to a file
+        :param config: omegaconf.Config object (DictConfig or ListConfig).
+        :param f: filename or file object
+        """
+        data = config.pretty()
+        if isinstance(f, str):
+            with io.open(os.path.abspath(f), "w", encoding="utf-8") as f:
+                f.write(six.u(data))
+        elif hasattr(f, "write"):
+            f.write(data)
+            f.flush()
+        else:
+            raise TypeError("Unexpected file type")
+
     @staticmethod
     def from_filename(filename):
         warnings.warn(

Deprecatd config.save() in favor of OmegaConf.save()
Since Config is a dynamic data type, I want to minimize the functions that exists on it to the bare minimum.
A user can easily create a save field that would potentially conflict with the save() function.
A future version would remove config.save() completely.

diff --git a/tests/test_serialization.py b/tests/test_serialization.py
index 8fc8952..4e73eb0 100644
--- a/tests/test_serialization.py
+++ b/tests/test_serialization.py
@@ -6,7 +6,7 @@ from pytest import raises
 from omegaconf import OmegaConf
 
 
-def save_load_file(conf):
+def save_load_file_deprecated(conf):
     try:
         with tempfile.NamedTemporaryFile(mode="wt", delete=False) as fp:
             conf.save(fp.file)
@@ -28,7 +28,10 @@ def save_load_filename(conf):
         os.unlink(fp.name)
 
 
-def save_load__from_file(conf):
+# Test deprecated config.save()
+
+
+def save_load__from_file_deprecated(conf):
     try:
         with tempfile.NamedTemporaryFile(mode="wt", delete=False) as fp:
             conf.save(fp.file)
@@ -39,7 +42,7 @@ def save_load__from_file(conf):
         os.unlink(fp.name)
 
 
-def save_load__from_filename(conf):
+def save_load__from_filename_deprecated(conf):
     # note that delete=False here is a work around windows incompetence.
     try:
         with tempfile.NamedTemporaryFile(delete=False) as fp:
@@ -50,8 +53,55 @@ def save_load__from_filename(conf):
         os.unlink(fp.name)
 
 
+def test_save_load_file_deprecated():
+    save_load_file_deprecated(OmegaConf.create(dict(a=10)))
+
+
+def test_save_load_filename_deprecated():
+    save_load_filename(OmegaConf.create(dict(a=10)))
+
+
+def test_save_load__from_file_deprecated():
+    save_load__from_file_deprecated(OmegaConf.create(dict(a=10)))
+
+
+def test_save_load__from_filename_deprecated():
+    save_load__from_filename_deprecated(OmegaConf.create(dict(a=10)))
+
+
+def test_save_illegal_type_deprecated():
+    with raises(TypeError):
+        OmegaConf.create().save(1000)
+
+
+##############################################
+# Test OmegaConf.save()
+
+
+def save_load__from_file(conf):
+    try:
+        with tempfile.NamedTemporaryFile(mode="wt", delete=False) as fp:
+            OmegaConf.save(conf, fp.file)
+        with io.open(os.path.abspath(fp.name), "rt") as handle:
+            c2 = OmegaConf.from_file(handle)
+        assert conf == c2
+    finally:
+        os.unlink(fp.name)
+
+
+def save_load__from_filename(conf):
+    # note that delete=False here is a work around windows incompetence.
+    try:
+        with tempfile.NamedTemporaryFile(delete=False) as fp:
+            OmegaConf.save(conf, fp.name)
+            c2 = OmegaConf.from_filename(fp.name)
+            assert conf == c2
+    finally:
+        os.unlink(fp.name)
+
+
 def test_save_load_file():
-    save_load_file(OmegaConf.create(dict(a=10)))
+    save_load_file_deprecated(OmegaConf.create(dict(a=10)))
 
 
 def test_save_load_filename():
@@ -66,6 +116,14 @@ def test_save_load__from_filename():
     save_load__from_filename(OmegaConf.create(dict(a=10)))
 
 
+def test_save_illegal_type():
+    with raises(TypeError):
+        OmegaConf.save(OmegaConf.create(), 1000)
+
+
+##############################################
+
+
 def test_pickle_dict():
     with tempfile.TemporaryFile() as fp:
         import pickle
@@ -92,8 +150,3 @@ def test_pickle_list():
 
 def test_save_load_unicode():
     save_load_filename(OmegaConf.create([u"שלום"]))
-
-
-def test_save_illegal_type():
-    with raises(TypeError):
-        OmegaConf.create().save(1000)

pip install -e .
parse_log_pytest
pytest
pytest coveralls
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
certifi==2021.5.30
charset-normalizer==2.0.12
coverage==6.2
coveralls==3.3.1
docopt==0.6.2
idna==3.10
importlib-metadata @ file:///tmp/build/80754af9/importlib-metadata_1631916693255/work
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
-e git+https://github.com/omry/omegaconf.git@cdd3c17f936b2721d9e02c726852efb9984c8426#egg=omegaconf
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
PyYAML==6.0.1
requests==2.27.1
six==1.17.0
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: omegaconf
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib-metadata=4.8.1=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - charset-normalizer==2.0.12
      - coverage==6.2
      - coveralls==3.3.1
      - docopt==0.6.2
      - idna==3.10
      - pyyaml==6.0.1
      - requests==2.27.1
      - six==1.17.0
      - urllib3==1.26.20
prefix: /opt/conda/envs/omegaconf

tests/test_serialization.py::test_save_load__from_file tests/test_serialization.py::test_save_load__from_filename tests/test_serialization.py::test_save_illegal_type
tests/test_serialization.py::test_save_load_file_deprecated tests/test_serialization.py::test_save_load_filename_deprecated tests/test_serialization.py::test_save_load__from_file_deprecated tests/test_serialization.py::test_save_load__from_filename_deprecated tests/test_serialization.py::test_save_illegal_type_deprecated tests/test_serialization.py::test_save_load_file tests/test_serialization.py::test_save_load_filename tests/test_serialization.py::test_pickle_dict tests/test_serialization.py::test_pickle_list tests/test_serialization.py::test_save_load_unicode
news/66.removal omegaconf/omegaconf.py .circleci/config.yml omegaconf/config.py noxfile.py
news/66.removal omegaconf/omegaconf.py .circleci/config.yml omegaconf/config.py noxfile.py