Repository: dynaconf/dynaconf

Issue:
[RFC] add FlaskDynaconf.load_extensions method.
Given:

```toml
[default]
EXTENSIONS = [
    "project.ext.appearance.init_app",
    "flask_bootstrap:Bootstrap",
    "flask_simplelogin:SimpleLogin",
]
```

To make Flask to load that extensions we can do

```py
from importlib import import_module
from dynaconf import FlaskDynaconf
from flask import Flask

def load_extensions(app):
    for extension in app.config.EXTENSIONS:
        ext = import_module(extension)
        ext(app)

app = Flask()
FlaskDynaconf(app)
load_extensions(app)
```

## Proposal

`load_extensions` function will be part of `FlaskDynaconf`

```py
from dynaconf import FlaskDynaconf
from flask import Flask
app = Flask()
FlaskDynaconf(app)
...
...
app.config.load_extensions(key="EXTENSIONS")

# alternativelly
app.config.load_extensions(key="EXTENSIONS", extensions=[..., ...])
```



Discussion:

Resolving patch:
diff --git a/dynaconf/contrib/flask_dynaconf.py b/dynaconf/contrib/flask_dynaconf.py
--- a/dynaconf/contrib/flask_dynaconf.py
+++ b/dynaconf/contrib/flask_dynaconf.py
@@ -7,6 +7,7 @@
     Config = object
 
 
+from importlib import import_module
 from dynaconf import LazySettings
 
 
@@ -113,7 +114,10 @@ def make_config(self, app):
         if self.dynaconf_instance:
             self.settings.update(self.kwargs)
         return DynaconfConfig(
-            root_path=root_path, defaults=app.config, _settings=self.settings
+            root_path=root_path,
+            defaults=app.config,
+            _settings=self.settings,
+            _app=app,
         )
 
 
@@ -127,17 +131,12 @@ class DynaconfConfig(Config):
     - Update with data in environmente vars `ENV_FOR_DYNACONF_`
     """
 
-    def get(self, key, default=None):
-        """Gets config from dynaconf variables
-        if variables does not exists in dynaconf try getting from
-        `app.config` to support runtime settings."""
-        return self._settings.get(key, Config.get(self, key, default))
-
-    def __init__(self, _settings, *args, **kwargs):
+    def __init__(self, _settings, _app, *args, **kwargs):
         """perform the initial load"""
         super(DynaconfConfig, self).__init__(*args, **kwargs)
         Config.update(self, _settings.store)
         self._settings = _settings
+        self._app = _app
 
     def __getitem__(self, key):
         """
@@ -162,3 +161,20 @@ def __getattr__(self, name):
 
     def __call__(self, name, *args, **kwargs):
         return self.get(name, *args, **kwargs)
+
+    def get(self, key, default=None):
+        """Gets config from dynaconf variables
+        if variables does not exists in dynaconf try getting from
+        `app.config` to support runtime settings."""
+        return self._settings.get(key, Config.get(self, key, default))
+
+    def load_extensions(self, key="EXTENSIONS", app=None):
+        """Loads flask extensions dynamically."""
+        app = app or self._app
+        for extension in app.config[key]:
+            # Split data in form `extension.path:factory_function`
+            module_name, factory = extension.split(":")
+            # Dynamically import extension module.
+            ext = import_module(module_name)
+            # Invoke factory passing app.
+            getattr(ext, factory)(app)
diff --git a/example/dummy_flask_extension/__init__.py b/example/dummy_flask_extension/__init__.py
new file mode 100644
diff --git a/example/dummy_flask_extension/dummy.py b/example/dummy_flask_extension/dummy.py
new file mode 100644
--- /dev/null
+++ b/example/dummy_flask_extension/dummy.py
@@ -0,0 +1,3 @@
+def init_app(app):
+    """This extension is only for testing."""
+    app.is_dummy_loaded = True


Test patch:
diff --git a/tests/test_flask.py b/tests/test_flask.py
--- a/tests/test_flask.py
+++ b/tests/test_flask.py
@@ -4,6 +4,18 @@
 from example.flask_with_dotenv.app import app as flask_app
 
 
+def test_dynamic_load_exts(settings):
+    """Assert that a config based extensions are loaded"""
+    app = Flask(__name__)
+    app.config["EXTENSIONS"] = ["example.dummy_flask_extension.dummy:init_app"]
+    FlaskDynaconf(app, dynaconf_instance=settings)
+    app.config.load_extensions()
+    assert app.config.EXTENSIONS == [
+        "example.dummy_flask_extension.dummy:init_app"
+    ]
+    assert app.is_dummy_loaded is True
+
+
 def test_flask_dynaconf(settings):
     """
     Test Flask app wrapped with FlaskDynaconf