1 2
import warnings
2

3 2
try:
4 2
    from flask.config import Config
5

6 2
    flask_installed = True
7
except ImportError:  # pragma: no cover
8
    flask_installed = False
9
    Config = object
10

11

12 2
import dynaconf
13 2
from importlib import import_module
14

15

16 2
class FlaskDynaconf(object):
17
    """The arguments are.
18
    app = The created app
19
    dynaconf_args = Extra args to be passed to Dynaconf (validator for example)
20

21
    All other values are stored as config vars specially::
22

23
        ENVVAR_PREFIX_FOR_DYNACONF = env prefix for your envvars to be loaded
24
                            example:
25
                                if you set to `MYSITE` then
26
                                export MYSITE_SQL_PORT='@int 5445'
27

28
                            with that exported to env you access using:
29
                                app.config.SQL_PORT
30
                                app.config.get('SQL_PORT')
31
                                app.config.get('sql_port')
32
                                # get is case insensitive
33
                                app.config['SQL_PORT']
34

35
                            Dynaconf uses `@int, @bool, @float, @json` to cast
36
                            env vars
37

38
        SETTINGS_FILE_FOR_DYNACONF = The name of the module or file to use as
39
                                    default to load settings. If nothing is
40
                                    passed it will be `settings.*` or value
41
                                    found in `ENVVAR_FOR_DYNACONF`
42
                                    Dynaconf supports
43
                                    .py, .yml, .toml, ini, json
44

45
    ATTENTION: Take a look at `settings.yml` and `.secrets.yml` to know the
46
            required settings format.
47

48
    Settings load order in Dynaconf:
49

50
    - Load all defaults and Flask defaults
51
    - Load all passed variables when applying FlaskDynaconf
52
    - Update with data in settings files
53
    - Update with data in environment vars `ENVVAR_FOR_DYNACONF_`
54

55

56
    TOML files are very useful to have `envd` settings, lets say,
57
    `production` and `development`.
58

59
    You can also achieve the same using multiple `.py` files naming as
60
    `settings.py`, `production_settings.py` and `development_settings.py`
61
    (see examples/validator)
62

63
    Example::
64

65
        app = Flask(__name__)
66
        FlaskDynaconf(
67
            app,
68
            ENV='MYSITE',
69
            SETTINGS_FILE='settings.yml',
70
            EXTRA_VALUE='You can add aditional config vars here'
71
        )
72

73
    Take a look at examples/flask in Dynaconf repository
74

75
    """
76

77 2
    def __init__(
78
        self,
79
        app=None,
80
        instance_relative_config=False,
81
        dynaconf_instance=None,
82
        extensions_list=False,
83
        **kwargs,
84
    ):
85
        """kwargs holds initial dynaconf configuration"""
86
        if not flask_installed:  # pragma: no cover
87
            raise RuntimeError(
88
                "To use this extension Flask must be installed "
89
                "install it with: pip install flask"
90
            )
91 2
        self.kwargs = kwargs
92

93 2
        kwargs.setdefault("ENVVAR_PREFIX", "FLASK")
94 2
        env_prefix = f"{kwargs['ENVVAR_PREFIX']}_ENV"  # FLASK_ENV
95 2
        kwargs.setdefault("ENV_SWITCHER", env_prefix)
96 2
        kwargs.setdefault("ENVIRONMENTS", True)
97 2
        kwargs.setdefault("load_dotenv", True)
98 2
        kwargs.setdefault(
99
            "default_settings_paths", dynaconf.DEFAULT_SETTINGS_FILES
100
        )
101

102 2
        self.dynaconf_instance = dynaconf_instance
103 2
        self.instance_relative_config = instance_relative_config
104 2
        self.extensions_list = extensions_list
105 2
        if app:
106 2
            self.init_app(app, **kwargs)
107

108 2
    def init_app(self, app, **kwargs):
109
        """kwargs holds initial dynaconf configuration"""
110 2
        self.kwargs.update(kwargs)
111 2
        self.settings = self.dynaconf_instance or dynaconf.LazySettings(
112
            **self.kwargs
113
        )
114 2
        dynaconf.settings = self.settings  # rebind customized settings
115 2
        app.config = self.make_config(app)
116 2
        app.dynaconf = self.settings
117

118 2
        if self.extensions_list:
119 2
            if not isinstance(self.extensions_list, str):
120 2
                self.extensions_list = "EXTENSIONS"
121 2
            app.config.load_extensions(self.extensions_list)
122

123 2
    def make_config(self, app):
124 2
        root_path = app.root_path
125
        if self.instance_relative_config:  # pragma: no cover
126
            root_path = app.instance_path
127 2
        if self.dynaconf_instance:
128 2
            self.settings.update(self.kwargs)
129 2
        return DynaconfConfig(
130
            root_path=root_path,
131
            defaults=app.config,
132
            _settings=self.settings,
133
            _app=app,
134
        )
135

136

137 2
class DynaconfConfig(Config):
138
    """
139
    Settings load order in Dynaconf:
140

141
    - Load all defaults and Flask defaults
142
    - Load all passed variables when applying FlaskDynaconf
143
    - Update with data in settings files
144
    - Update with data in environmente vars `ENV_FOR_DYNACONF_`
145
    """
146

147 2
    def __init__(self, _settings, _app, *args, **kwargs):
148
        """perform the initial load"""
149 2
        super(DynaconfConfig, self).__init__(*args, **kwargs)
150 2
        Config.update(self, _settings.store)
151 2
        self._settings = _settings
152 2
        self._app = _app
153

154 2
    def __getitem__(self, key):
155
        """
156
        Flask templates always expects a None when key is not found in config
157
        """
158 2
        return self.get(key)
159

160 2
    def __setitem__(self, key, value):
161
        """
162
        Allows app.config['key'] = 'foo'
163
        """
164 2
        return self._settings.__setitem__(key, value)
165

166 2
    def __getattr__(self, name):
167
        """
168
        First try to get value from dynaconf then from Flask
169
        """
170 2
        try:
171 2
            return getattr(self._settings, name)
172 2
        except AttributeError:
173 2
            return self[name]
174

175 2
    def __call__(self, name, *args, **kwargs):
176 2
        return self.get(name, *args, **kwargs)
177

178 2
    def get(self, key, default=None):
179
        """Gets config from dynaconf variables
180
        if variables does not exists in dynaconf try getting from
181
        `app.config` to support runtime settings."""
182 2
        return self._settings.get(key, Config.get(self, key, default))
183

184 2
    def load_extensions(self, key="EXTENSIONS", app=None):
185
        """Loads flask extensions dynamically."""
186 2
        app = app or self._app
187 2
        extensions = app.config.get(key)
188 2
        if not extensions:
189 2
            warnings.warn(
190
                f"Settings is missing {key} to load Flask Extensions",
191
                RuntimeWarning,
192
            )
193 2
            return
194

195 2
        for extension in app.config[key]:
196
            # Split data in form `extension.path:factory_function`
197 2
            module_name, factory = extension.split(":")
198
            # Dynamically import extension module.
199 2
            ext = import_module(module_name)
200
            # Invoke factory passing app.
201 2
            getattr(ext, factory)(app)

Read our documentation on viewing source code .

Loading