# Imports
from configparser import ConfigParser
from superdjango.utils import smart_cast
import os
# Exports
__all__ = (
"Env",
"Section",
)
# Classes
[docs]class Env(object):
"""The Env class assists with loading environment variables from an INI file.
The file may be located anywhere that is accessible for reading by the user that executes the Python process.
.. code-block:: ini
; All variables defined in the environment section are available as attributes of the Env instance. For example,
; env.debug
[environment]
debug = no
name = live
[database]
host = db.example.com
name = example_com
password = dbpassword
user = example_com
[mailgun]
api_key = mailgunapikey
enabled = yes
sender_domain = mg.example.com
[secret]
key = somesupersecretkey
In your ``settings.py`` file:
.. code-block:: python
from superdjango.env import Env
env = Env("env.ini")
SECRET_KEY = env.secret.key
if env.name == "live":
DEBUG = False
else:
DEBUG = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'HOST': env.database.host,
'NAME': env.database.name,
'PASSWORD': env.database.password,
'USER': env.database.user,
}
}
# ... and so on ...
"""
[docs] def __init__(self, path, autoload=True):
"""Initialize the environment.
:param path: The path to the INI file.
:type path: str
:param autoload: Automatically load the file upon instantiation. See ``load()``.
:type autoload: bool
"""
self.is_loaded = False
self.path = path
self._sections = dict()
if autoload:
self.load()
def __getattr__(self, item):
"""Get a section instance, or if the item exists in the ``environment`` section, get the value of the item."""
if self.has(item):
return self._sections.get(item)
if self.has("environment", key=item):
return self._sections['environment'].values[item]
return Section(item)
[docs] def get(self, section, default=None, key=None):
"""Get a section, and optionally a variable within that section.
:param section: The section name.
:type section: str
:param default: The default value if the key does not exist.
:param key: The name of the variable to get from the section.
:type key: str
"""
if not self.has(section, key=key):
return default
_section = self._sections[section]
if key is not None:
return _section.values.get(key, default)
return _section
[docs] def has(self, section, key=None):
"""Determine if a section exists.
:param section: The section name.
:type section: str
:param key: Also check whether the given variable exists within the section.
:type key: str
"""
if section not in self._sections:
return False
if key is not None:
return key in self._sections[section].values
return True
[docs] def load(self):
"""Load the environment.
:rtype: bool
"""
if not os.path.exists(self.path):
return False
ini = ConfigParser()
ini.read(self.path)
for section in ini.sections():
kwargs = dict()
for key, value in ini.items(section):
kwargs[key] = smart_cast(value)
self._sections[section] = Section(section, **kwargs)
self.is_loaded = True
return True
[docs]class Section(object):
"""A section within an Env INI file."""
[docs] def __init__(self, section_name, **kwargs):
"""Initialize section attributes.
:param section_name: The name of the section. Note that no section in your INI may include a variable named
``section_name``.
:type section_name: str
Keyword arguments are available as attributes of the section instance.
"""
self._name = section_name
self.values = kwargs
def __getattr__(self, item):
return self.values.get(item)