Skip to content

Commit

Permalink
Option to override existing variables with read_env
Browse files Browse the repository at this point in the history
  • Loading branch information
SmileyChris committed Sep 26, 2021
1 parent cbbd6d6 commit 6fa1fba
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 17 deletions.
20 changes: 18 additions & 2 deletions docs/tips.rst
Original file line number Diff line number Diff line change
Expand Up @@ -195,10 +195,13 @@ Values that being with a ``$`` may be interpolated. Pass ``interpolate=True`` to
FOO
Reading env files
=================

.. _multiple-env-files-label:

Multiple env files
==================
------------------

There is an ability point to the .env file location using an environment
variable. This feature may be convenient in a production systems with a
Expand Down Expand Up @@ -227,7 +230,7 @@ while ``./manage.py runserver`` uses ``.env``.


Using Path objects when reading env
===================================
-----------------------------------

It is possible to use of ``pathlib.Path`` objects when reading environment file from the filesystem:

Expand All @@ -249,3 +252,16 @@ It is possible to use of ``pathlib.Path`` objects when reading environment file
env.read_env(os.path.join(BASE_DIR, '.env'))
env.read_env(pathlib.Path(str(BASE_DIR)).joinpath('.env'))
env.read_env(pathlib.Path(str(BASE_DIR)) / '.env')
Overwriting existing environment values from env files
------------------------------------------------------

If you want variables set within your env files to take higher precidence than
an existing set environment variable, use the ``overwrite=True`` argument of
``read_env``. For example:

.. code-block:: python
env = environ.Env()
env.read_env(BASE_DIR('.env'), overwrite=True)
16 changes: 12 additions & 4 deletions environ/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -732,13 +732,16 @@ def search_url_config(cls, url, engine=None):
return config

@classmethod
def read_env(cls, env_file=None, **overrides):
def read_env(cls, env_file=None, overwrite=False, **overrides):
"""Read a .env file into os.environ.
If not given a path to a dotenv path, does filthy magic stack
backtracking to find the dotenv in the same directory as the file that
called read_env.
By default, won't overwrite any existing environment variables. You can
enable this behaviour by setting ``overwrite=True``.
Refs:
- https://wellfire.co/learn/easier-12-factor-django
- https://gist.github.com/bennylope/2999704
Expand Down Expand Up @@ -788,13 +791,18 @@ def _keep_escaped_format_characters(match):
if m3:
val = re.sub(r'\\(.)', _keep_escaped_format_characters,
m3.group(1))
cls.ENVIRON.setdefault(key, str(val))
overrides[key] = str(val)
else:
logger.warn('Invalid line: %s', line)

# set defaults
if overwrite:
def set_environ(key, value):
cls.ENVIRON[key] = value
else:
set_environ = cls.ENVIRON.setdefault

for key, value in overrides.items():
cls.ENVIRON.setdefault(key, value)
set_environ(key, value)


class FileAwareEnv(Env):
Expand Down
31 changes: 20 additions & 11 deletions tests/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,34 +307,43 @@ def setup_method(self, method):
PATH_VAR=Path(__file__, is_file=True).__root__
)

def test_read_env_path_like(self):
def create_temp_env_file(self, name):
import pathlib
import tempfile

path_like = (pathlib.Path(tempfile.gettempdir()) / 'test_pathlib.env')
env_file_path = (pathlib.Path(tempfile.gettempdir()) / name)
try:
path_like.unlink()
env_file_path.unlink()
except FileNotFoundError:
pass

assert not path_like.exists()
assert not env_file_path.exists()
return env_file_path

def test_read_env_path_like(self):
env_file_path = self.create_temp_env_file('test_pathlib.env')

env_key = 'SECRET'
env_val = 'enigma'
env_str = env_key + '=' + env_val

# open() doesn't take path-like on Python < 3.6
try:
with open(path_like, 'w', encoding='utf-8') as f:
f.write(env_str + '\n')
except TypeError:
return
with open(str(env_file_path), 'w', encoding='utf-8') as f:
f.write(env_str + '\n')

assert path_like.exists()
self.env.read_env(path_like)
self.env.read_env(env_file_path)
assert env_key in self.env.ENVIRON
assert self.env.ENVIRON[env_key] == env_val

@pytest.mark.parametrize("overwrite", [True, False])
def test_existing_overwrite(self, overwrite):
env_file_path = self.create_temp_env_file('test_existing.env')
with open(str(env_file_path), 'w') as f:
f.write("EXISTING=b")
self.env.ENVIRON['EXISTING'] = "a"
self.env.read_env(env_file_path, overwrite=overwrite)
assert self.env.ENVIRON["EXISTING"] == ("b" if overwrite else "a")


class TestSubClass(TestEnv):
def setup_method(self, method):
Expand Down

0 comments on commit 6fa1fba

Please sign in to comment.