Repository: dynaconf/dynaconf

Issue:
[RFC] module impersonation, local files, runtime files
Read yaycl readme

https://github.com/seandst/yaycl


Discussion:
- [x] local files
- [x] module impersonation
- [x] runtime configs


Resolving patch:
diff --git a/dynaconf/loaders/__init__.py b/dynaconf/loaders/__init__.py
--- a/dynaconf/loaders/__init__.py
+++ b/dynaconf/loaders/__init__.py
@@ -10,6 +10,7 @@
 from dynaconf.utils import deduplicate
 from dynaconf.utils import ensure_a_list
 from dynaconf.utils.boxing import DynaBox
+from dynaconf.utils.files import get_local_filename
 from dynaconf.utils.parse_conf import false_values
 
 
@@ -96,6 +97,15 @@ def settings_loader(
 
     enabled_core_loaders = obj.get("CORE_LOADERS_FOR_DYNACONF")
 
+    # add `.local.` to found_files list to search for local files.
+    found_files.extend(
+        [
+            get_local_filename(item)
+            for item in found_files
+            if ".local." not in str(item)
+        ]
+    )
+
     for mod_file in modules_names + found_files:
         # can be set to multiple files settings.py,settings.yaml,...
 
diff --git a/dynaconf/utils/__init__.py b/dynaconf/utils/__init__.py
--- a/dynaconf/utils/__init__.py
+++ b/dynaconf/utils/__init__.py
@@ -166,14 +166,6 @@ def deduplicate(list_object):
 
 
 def warn_deprecations(data):
-    if data.get("MERGE_ENABLED_FOR_DYNACONF"):
-        warnings.warn(
-            "MERGE_ENABLED_FOR_DYNACONF is deprecated "
-            "instead it is preferred to use the local merge feature "
-            "see: https://dynaconf.readthedocs.io/en/latest/guides/usage.html"
-            "#merging-existing-values",
-            DeprecationWarning,
-        )
     for old, new in RENAMED_VARS.items():
         if old in data:
             warnings.warn(
diff --git a/dynaconf/utils/files.py b/dynaconf/utils/files.py
--- a/dynaconf/utils/files.py
+++ b/dynaconf/utils/files.py
@@ -93,3 +93,20 @@ def read_file(path, **kwargs):
     with io.open(path, **kwargs) as open_file:
         content = open_file.read().strip()
     return content
+
+
+def get_local_filename(filename):
+    """Takes a filename like `settings.toml` and returns `settings.local.toml`
+
+    Arguments:
+        filename {str} -- The filename or complete path
+
+    Returns:
+        [str] -- The same name or path with `.local.` added.
+    """
+    return os.path.join(
+        os.path.dirname(str(filename)),
+        "{0}.local.{2}".format(
+            *os.path.basename(str(filename)).rpartition(os.path.extsep)
+        ),
+    )


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -10,6 +10,7 @@
 from dynaconf.utils import object_merge
 from dynaconf.utils import trimmed_split
 from dynaconf.utils.files import find_file
+from dynaconf.utils.files import get_local_filename
 from dynaconf.utils.parse_conf import parse_conf_data
 from dynaconf.utils.parse_conf import unparse_conf_data
 
@@ -213,3 +214,9 @@ def test_ensure_a_list():
 
     # other types get wrapped in a list
     assert ensure_a_list(1) == [1]
+
+
+def test_get_local_filename():
+    settings_path = os.path.join("foo", "b", "conf.toml")
+    local_path = os.path.join("foo", "b", "conf.local.toml")
+    assert get_local_filename(settings_path) == local_path
diff --git a/tests/test_yaml_loader.py b/tests/test_yaml_loader.py
--- a/tests/test_yaml_loader.py
+++ b/tests/test_yaml_loader.py
@@ -134,23 +134,6 @@ def test_extra_yaml():
     assert settings.HELLOEXAMPLE == "world"
 
 
-# def test_multi_extra_yaml():
-#     """Test loading extra yaml file"""
-#     load(settings, filename=YAMLS)
-#     yaml = """
-#     example:
-#        helloexample: world
-#     """
-#     yaml2 = """
-#     example:
-#        foo: bar
-#     """
-#     settings.set('YAML', [yaml, yaml2])
-#     settings.execute_loaders(env='EXAMPLE')
-#     assert settings.HELLOEXAMPLE == 'world'
-#     assert settings.FOO == 'bar'
-
-
 def test_empty_value():
     load(settings, filename="")
 
@@ -202,3 +185,96 @@ def test_load_dunder():
     load(settings, filename=yaml, env="FOO")
     assert settings.COLORS.black.code == "#000000"
     assert settings.COLORS.black.name == "Black"
+
+
+def test_local_files(tmpdir):
+
+    settings_file_yaml = """
+    default:
+      name: Bruno
+      colors:
+        - green
+        - blue
+      data:
+        link: brunorocha.org
+      other:
+        foo: bar
+      MUSIC:
+        CURRENT:
+          VOLUME: 10
+          TITLE: The Beatles - Strawberry Fields
+    """
+    tmpdir.join("settings.yaml").write(settings_file_yaml)
+
+    local_file_yaml = """
+    default:
+      name: Bruno Rocha
+      colors:
+        - red
+        - dynaconf_merge
+      data:
+        city: Guarulhos
+        dynaconf_merge: true
+      other:
+        baz: zaz
+      MUSIC__current__volume: 100
+      MUSIC__current__title: Led Zeppelin - Immigrant Song
+    """
+    tmpdir.join("settings.local.yaml").write(local_file_yaml)
+
+    conf = LazySettings()
+    assert conf.NAME == "Bruno Rocha"
+    assert set(conf.COLORS) == set(["red", "green", "blue"])
+    assert conf.DATA.link == "brunorocha.org"
+    assert conf.DATA.city == "Guarulhos"
+    assert conf.OTHER == {"baz": "zaz"}
+    assert conf.MUSIC.current.volume == 100
+    assert conf.MUSIC.current.title == "Led Zeppelin - Immigrant Song"
+
+
+def test_explicit_local_files(tmpdir):
+
+    settings_file_yaml = """
+    default:
+      name: Bruno
+      colors:
+        - green
+        - blue
+      data:
+        link: brunorocha.org
+      other:
+        foo: bar
+      MUSIC:
+        CURRENT:
+          VOLUME: 10
+          TITLE: The Beatles - Strawberry Fields
+    """
+    tmpdir.join("foo.yaml").write(settings_file_yaml)
+
+    local_file_yaml = """
+    default:
+      name: Bruno Rocha
+      colors:
+        - red
+        - dynaconf_merge
+      data:
+        city: Guarulhos
+        dynaconf_merge: true
+      other:
+        baz: zaz
+      MUSIC__current__volume: 100
+      MUSIC__current__title: Led Zeppelin - Immigrant Song
+    """
+    tmpdir.join("foo.local.yaml").write(local_file_yaml)
+
+    conf = LazySettings(
+        SETTINGS_FILE_FOR_DYNACONF=["foo.yaml", "foo.local.yaml"]
+    )
+
+    assert conf.NAME == "Bruno Rocha"
+    assert set(conf.COLORS) == set(["red", "green", "blue"])
+    assert conf.DATA.link == "brunorocha.org"
+    assert conf.DATA.city == "Guarulhos"
+    assert conf.OTHER == {"baz": "zaz"}
+    assert conf.MUSIC.current.volume == 100
+    assert conf.MUSIC.current.title == "Led Zeppelin - Immigrant Song"