This commit is contained in:
Piotr Jakóbczyk 2024-11-19 18:22:04 +09:00 committed by GitHub
commit b31ac9df15
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 75 additions and 3 deletions

View file

@ -99,6 +99,7 @@ class Configuration(metaclass=ConfigurationBase):
"""
DOTENV_LOADED = None
DOTENV_RELOAD = False
@classmethod
def load_dotenv(cls):
@ -113,6 +114,18 @@ class Configuration(metaclass=ConfigurationBase):
# check if the class has DOTENV set whether with a path or None
dotenv = getattr(cls, 'DOTENV', None)
required = True
override_env = False
# check if the DOTENV is dict, and check all options of it
if isinstance(dotenv, dict):
# whether we want to override previously set envs
override_env = dotenv.get("override", False)
# whether we want to error if the file is not found
required = dotenv.get("required", True)
# whether we want to reload on dotenv, useful if we want to frequently change it
cls.DOTENV_RELOAD = dotenv.get("reload", False)
dotenv = dotenv.get("path", None)
# if DOTENV is falsy we want to disable it
if not dotenv:
return
@ -122,6 +135,8 @@ class Configuration(metaclass=ConfigurationBase):
with open(dotenv) as f:
content = f.read()
except OSError as e:
if not required:
return
raise ImproperlyConfigured("Couldn't read .env file "
"with the path {}. Error: "
"{}".format(dotenv, e)) from e
@ -137,13 +152,16 @@ class Configuration(metaclass=ConfigurationBase):
m3 = re.match(r'\A"(.*)"\Z', val)
if m3:
val = re.sub(r'\\(.)', r'\1', m3.group(1))
os.environ.setdefault(key, val)
if override_env:
os.environ[key] = val
else:
os.environ.setdefault(key, val)
cls.DOTENV_LOADED = dotenv
@classmethod
def pre_setup(cls):
if cls.DOTENV_LOADED is None:
if cls.DOTENV_LOADED is None or cls.DOTENV_RELOAD:
cls.load_dotenv()
@classmethod

View file

@ -57,6 +57,20 @@ A ``.env`` file is a ``.ini``-style file. It must contain a list of
API_KEY1=1234
API_KEY2=5678
``DOTENV`` can also be a dictionary, and then its behavior can be configured more:
.. code-block:: python
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
class Dev(Configuration):
DOTENV = {
"path": str(os.path.join(BASE_DIR, '.env')),
# if True, overriddes previously set environmental variables, if False only sets them if they haven't been set before
"override": True,
# if True errors if the DOTENV is not found at path, if False return
"required": False,
# if True, reloads DOTENV dynamically for example on hot reload
"reload": True,
Envdir
------

View file

@ -1 +1,2 @@
DJANGO_DOTENV_VALUE='is set'
DJANGO_DOTENV_VALUE='is set'
DJANGO_DOTENV_OVERRIDE='overridden'

View file

@ -0,0 +1,12 @@
from configurations import Configuration, values
class DotEnvConfiguration(Configuration):
DOTENV = {
'path': 'test_project/.env',
'override': True,
}
DOTENV_VALUE = values.Value()
DOTENV_OVERRIDE = values.Value("Not overridden")

View file

@ -0,0 +1,12 @@
from configurations import Configuration, values
class DotEnvConfiguration(Configuration):
DOTENV = {
'path': 'some_nonexistant_path',
'override': True,
'required': False,
}
DOTENV_OVERRIDE = values.Value("Not overridden")

View file

@ -13,3 +13,18 @@ class DotEnvLoadingTests(TestCase):
self.assertEqual(dot_env.DOTENV_VALUE, 'is set')
self.assertEqual(dot_env.DOTENV_VALUE_METHOD, 'is set')
self.assertEqual(dot_env.DOTENV_LOADED, dot_env.DOTENV)
@patch.dict(os.environ, clear=True,
DJANGO_CONFIGURATION='DotEnvConfiguration',
DJANGO_SETTINGS_MODULE='tests.settings.dot_env_dict')
def test_env_dict(self):
from tests.settings import dot_env_dict
self.assertEqual(dot_env_dict.DOTENV_VALUE, 'is set')
self.assertEqual(dot_env_dict.DOTENV_OVERRIDE, 'overridden')
@patch.dict(os.environ, clear=True,
DJANGO_CONFIGURATION='DotEnvConfiguration',
DJANGO_SETTINGS_MODULE='tests.settings.dot_env_not_required')
def test_env_not_required(self):
from tests.settings import dot_env_not_required
self.assertEqual(dot_env_not_required.DOTENV_OVERRIDE, 'Not overridden')