Repository: rochacbruno/dynaconf
Pull request #244 (2019-10-09 05:36:00)

Add support for PRELOAD_ setting.
Ref https://github.com/pulp/pulpcore/pull/330

Useful for plugin based apps.

```py
from dynaconf import LazySettings

settings = LazySettings(
  PRELOAD_FOR_DYNACONF=["/path/*", "other/settings.toml"],                # <-- Loaded first
  SETTINGS_FILE_FOR_DYNACONF="/etc/foo/settings.py",                      # <-- Loaded second (the main file)
  INCLUDES_FOR_DYNACONF=["other.module.settings", "other/settings.yaml"]  # <-- Loaded at the end
)

```

codecov[bot]: # [Codecov](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=h1) Report
> Merging [#244](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=desc) into [master](https://codecov.io/gh/rochacbruno/dynaconf/commit/94488f519b7e0626b72414218fef7470df5abae1?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `n/a`.

[![Impacted file tree graph](https://codecov.io/gh/rochacbruno/dynaconf/pull/244/graphs/tree.svg?width=650&token=GXszVzJcqW&height=150&src=pr)](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=tree)

```diff
@@          Coverage Diff          @@
##           master   #244   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          18     18           
  Lines         784    784           
=====================================
  Hits          784    784
```



------

[Continue to review full report at Codecov](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=footer). Last update [94488f5...14cef9c](https://codecov.io/gh/rochacbruno/dynaconf/pull/244?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/docs/guides/advanced_usage.md b/docs/guides/advanced_usage.md
index 3a51534..25b34cd 100644
--- a/docs/guides/advanced_usage.md
+++ b/docs/guides/advanced_usage.md
@@ -276,3 +276,21 @@ data = settings.as_dict(env='development')
 # can be .yaml, .toml, .ini, .json, .py
 loaders.write('/path/to/file.yaml', DynaBox(data).to_dict(), merge=False, env='development')
 ```
+
+
+## Preloading files
+
+> New in **2.2.0**
+
+Useful for plugin based apps.
+
+```py
+from dynaconf import LazySettings
+
+settings = LazySettings(
+  PRELOAD_FOR_DYNACONF=["/path/*", "other/settings.toml"],                # <-- Loaded first
+  SETTINGS_FILE_FOR_DYNACONF="/etc/foo/settings.py",                      # <-- Loaded second (the main file)
+  INCLUDES_FOR_DYNACONF=["other.module.settings", "other/settings.yaml"]  # <-- Loaded at the end
+)
+
+```
diff --git a/docs/guides/configuration.md b/docs/guides/configuration.md
index 42b08d1..701f2a2 100644
--- a/docs/guides/configuration.md
+++ b/docs/guides/configuration.md
@@ -44,11 +44,12 @@ It can also be passed as parameters to extensions like `FlaskDynaconf` or set in
     ENVVAR | str | The envvar which holds the list of settings files. | ‘SETTINGS_FILE_FOR_DYNACONF’ | ENVVAR_FOR_DYNACONF=MYPROGRAM_SETTINGS
     ENVVAR_PREFIX | str | Prefix for exporting parameters as env vars. Example: If your program is called *MYPROGRAM* you may want users to use *MYPROGRAM_FOO=bar* instead of *DYNACONF_FOO=bar* on envvars. | “DYNACONF” | ENVVAR_PREFIX_FOR_DYNACONF=MYPROGRAM (loads MYPROGRAM_VAR) ENVVAR_PREFIX_FOR_DYNACONF=’’ (loads _VAR) ENVVAR_PREFIX_FOR_DYNACONF=false (loads VAR)
     FRESH_VARS | list | A list of vars to be re-loaded on every access. | [] | FRESH_VARS_FOR_DYNACONF=[“HOST”, “PORT”]
-    INCLUDES | list | A list of paths or a glob to load can be a toml-like list, or sep by , or ; | [] | INCLUDES_FOR_DYNACONF=”[‘path1.ext’, ‘folder/’]” INCLUDES_FOR_DYNACONF=”path1.toml;path2.toml” INCLUDES_FOR_DYNACONF=”path1.toml,path2.toml” INCLUDES_FOR_DYNACONF=”single_path.toml” INCLUDES_FOR_DYNACONF=”single_path/glob/.toml”
+    INCLUDES | list | A list of paths or a glob to load can be a toml-like list, or sep by , or ; | [] | INCLUDES_FOR_DYNACONF=”[‘path1.ext’, ‘folder/*’]” INCLUDES_FOR_DYNACONF=”path1.toml;path2.toml” INCLUDES_FOR_DYNACONF=”path1.toml,path2.toml” INCLUDES_FOR_DYNACONF=”single_path.toml” INCLUDES_FOR_DYNACONF=”single_path/glob/.toml”
     INSTANCE **used only by** *$dynaconf** *cli*. | str | Custom instance of LazySettings Must be an importable Python module. | None | INSTANCE_FOR_DYNACONF=myapp.settings
     LOADERS | list | A list of enabled external loaders. |	[‘dynaconf.loaders.env_loader’] | LOADERS_FOR_DYNACONF=’[‘module.mycustomloader’, …]’
     MERGE_ENABLED | bool | A bool to activate the global merge feature | False | MERGE_ENABLED_FOR_DYNACONF=1
     NESTED_SEPARATOR | str | Separator for nested assignment like `export DYNACONF_DATABASES__NAME='foo'` | `__` double underline | NESTED_SEPARATOR_FOR_DYNACONF='___'
+    PRELOAD | list | A list of paths or glob to be pre-loaded before main settings file. | [] | PRELOAD_FOR_DYNACONF="['path1.ext', 'folder/*']"
     REDIS_DB | int | Redis DB. | 0 | REDIS_DB_FOR_DYNACONF=1
     REDIS_ENABLED | bool | Redis loader is enabled. | false | REDIS_ENABLED_FOR_DYNACONF=true
     REDIS_HOST | str | Redis server address. | localhost | REDIS_HOST_FOR_DYNACONF=”localhost”
diff --git a/docs/guides/usage.md b/docs/guides/usage.md
index 0464c6b..6cb3890 100644
--- a/docs/guides/usage.md
+++ b/docs/guides/usage.md
@@ -185,15 +185,16 @@ BAR
 Dynaconf loads file in a overriding cascade loading order using the predefined order:
 
 1. First the environment variables (and `.env` file) to read for [configuration](configuration.html) options
-2. Then the files defined in `SETTINGS_FILE_FOR_DYNACONF` using the loaders defined in `CORE_LOADERS_FOR_DYNACONF`
+2. Then the paths provided in `PRELOAD_FOR_DYNACONF` using all enabled loaders.
+3. Then the files defined in `SETTINGS_FILE_FOR_DYNACONF` using all enabled loaders.
     - Files containing `.local.` in its name will be loaded at the end. e.g: `settings.local.yaml`
-3. Then contents of `SECRETS_FOR_DYNACONF` envvar filename if defined (useful for jenkins and other CI)
-4. Then the loaders defined in `LOADERS_FOR_DYNACONF` 
+4. Then contents of `SECRETS_FOR_DYNACONF` envvar filename if defined (useful for jenkins and other CI)
+5. Then the loaders defined in `LOADERS_FOR_DYNACONF` 
     - Redis if enabled by `REDIS_FOR_DYNACONF=1`
     - Vault if enabled by `Vault_FOR_DYNACONF=1`
     - Custom loaders if any added
     - Environment variables loader will be the last always
-5. If there is any `DYNACONF_INCLUDE` key found or `INCLUDES_FOR_DYNACONF` env vars this will be loaded.
+6. If there is any `DYNACONF_INCLUDE` key found or `INCLUDES_FOR_DYNACONF` env vars this will be loaded.
 
 The order can be changed by overriding the `SETTINGS_FILE_FOR_DYNACONF` the `CORE_LOADERS_FOR_DYNACONF` and `LOADERS_FOR_DYNACONF` variables.
 
diff --git a/dynaconf/base.py b/dynaconf/base.py
index 1b6c885..821207f 100644
--- a/dynaconf/base.py
+++ b/dynaconf/base.py
@@ -878,6 +878,7 @@ class Settings(object):
             default_loader(self, self._defaults)
         env = (env or self.current_env).upper()
         silent = silent or self.SILENT_ERRORS_FOR_DYNACONF
+        self.pre_load(env, silent=silent, key=key)
         settings_loader(
             self, env=env, silent=silent, key=key, filename=filename
         )
@@ -889,6 +890,13 @@ class Settings(object):
         self.load_includes(env, silent=silent, key=key)
         self.logger.debug("Loaded Files: %s", deduplicate(self._loaded_files))
 
+    def pre_load(self, env, silent, key):
+        """Do we have any file to pre-load before main settings file?"""
+        preloads = self.get("PRELOAD_FOR_DYNACONF", [])
+        if preloads:
+            self.logger.debug("Processing preloads %s", preloads)
+            self.load_file(path=preloads, env=env, silent=silent, key=key)
+
     def load_includes(self, env, silent, key):
         """Do we have any nested includes we need to process?"""
         includes = self.get("DYNACONF_INCLUDE", [])
diff --git a/dynaconf/default_settings.py b/dynaconf/default_settings.py
index 1f26a2b..fa65742 100644
--- a/dynaconf/default_settings.py
+++ b/dynaconf/default_settings.py
@@ -209,6 +209,9 @@ SECRETS_FOR_DYNACONF = get("SECRETS_FOR_DYNACONF", None)
 # To include extra paths based on envvar
 INCLUDES_FOR_DYNACONF = get("INCLUDES_FOR_DYNACONF", [])
 
+# To pre-load extra paths based on envvar
+PRELOAD_FOR_DYNACONF = get("PRELOAD_FOR_DYNACONF", [])
+
 # Files to skip if found on search tree
 SKIP_FILES_FOR_DYNACONF = get("SKIP_FILES_FOR_DYNACONF", [])
 


Test patch:
diff --git a/tests/test_base.py b/tests/test_base.py
index c2791e4..503a91c 100644
--- a/tests/test_base.py
+++ b/tests/test_base.py
@@ -689,3 +689,43 @@ def test_from_env_method(clean_env, tmpdir):
     assert new_other_settings.ONLY_IN_OTHER is True
     assert new_other_settings.ONLY_IN_DEVELOPMENT is True
     assert settings.A_DEFAULT == "From default env"
+
+
+def test_preload(tmpdir):
+    data = {
+        "default": {
+            "data": {
+                "links": {"twitter": "rochacbruno", "site": "brunorocha.org"}
+            }
+        }
+    }
+    toml_loader.write(str(tmpdir.join("preload.toml")), data, merge=False)
+
+    data = {
+        "dynaconf_merge": True,
+        "default": {"data": {"links": {"github": "rochacbruno.github.io"}}},
+    }
+    toml_loader.write(
+        str(tmpdir.join("main_settings.toml")), data, merge=False
+    )
+
+    data = {
+        "dynaconf_merge": True,
+        "default": {
+            "data": {"links": {"mastodon": "mastodon.social/@rochacbruno"}}
+        },
+    }
+    toml_loader.write(str(tmpdir.join("included.toml")), data, merge=False)
+
+    settings = LazySettings(
+        PRELOAD_FOR_DYNACONF=["preload.toml"],
+        SETTINGS_FILE_FOR_DYNACONF="main_settings.toml",
+        INCLUDES_FOR_DYNACONF=["included.toml"],
+    )
+
+    assert settings.DATA.links == {
+        "twitter": "rochacbruno",
+        "site": "brunorocha.org",
+        "github": "rochacbruno.github.io",
+        "mastodon": "mastodon.social/@rochacbruno",
+    }