Skip to content
This repository has been archived by the owner on Sep 5, 2021. It is now read-only.

Override environment vars that already have value with in a env-file or the OS #15

Merged
merged 3 commits into from
May 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Breaking Changes
module. If an ImportError is encountered while it attempts to do this,
``Env.read_env()`` will assume there's no ``.env`` file to be found, log a
WARN-level log message to that effect, and continue on.
* Make ``Env.read_env()``'s ``overrides`` argument actually override variables.


Features
Expand Down
19 changes: 15 additions & 4 deletions docs/essentials.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ This method is responsible for reading key-value pairs from the ``.env`` file
and adding them to environment variables.

``read_env()`` expects a path to the ``.env`` file. If one is not provided, it
will attempt to use the ``django.BASE_DIR`` constant from the Django settings
will attempt to use the ``django.BASE_DIR`` constant from the Django ``settings``
module. If an ``ImportError`` or ``NameError`` is encountered while it attempts
to do this, ``read_env()`` will assume there's no ``.env`` file to be found, log
a WARN-level log message to that effect, and continue on.
Expand Down Expand Up @@ -44,7 +44,7 @@ The following things should also be mentioned:
* The values read in from the ``.env`` file are overridden by any that already
existed in the environment. This means that environment variables obtained
from the ``os.environ`` will have a higher priority.
* ``read_env()`` also takes an additional key/value **kwargs. Any additional keyword
* ``read_env()`` also takes an additional overrides list. Any additional keyword
arguments provided directly to ``read_env`` will be added to the environment.
If the key matches an existing environment variable, the value will be overridden.
* ``read_env()`` updates ``os.environ`` directly, rather than just that particular
Expand All @@ -55,14 +55,17 @@ The following example demonstrates the above:
**.env file**:

.. code-block:: shell

# .env file contents
DJANGO_SETTINGS_MODULE=settings.prod
[email protected]
DEBUG=on


**settings.py file**:

.. code-block:: python

# settings.py file contents
import environ

Expand All @@ -71,10 +74,18 @@ The following example demonstrates the above:
assert 'SECRET_KEY' not in os.environ
assert 'DEBUG' not in os.environ

# Take environment variables from .env file
overrides = {'SECRET_KEY': 'Enigma'}

overrides = {
'SECRET_KEY': 'Enigma',
'EMAIL': '[email protected]',
}

# Take environment variables from .env file using
# os.path.join(BASE_DIR, '.env'). Also take the overrides list.
environ.Env.read_env(**overrides)

assert os.environ['SECRET_KEY'] == 'Enigma'
assert os.environ['DJANGO_SETTINGS_MODULE'] == 'settings.dev'
assert os.environ['EMAIL'] == '[email protected]'

assert 'DEBUG' in os.environ
9 changes: 6 additions & 3 deletions environ/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -719,7 +719,10 @@ def read_env(cls, env_file=None, **overrides):
"""Read a .env file into ENVIRON.

Existing environment variables take precedent and are NOT overwritten
by the file content or key/value pairs given as `overrides`.
by the file content.

Key/value pairs given as `overrides` DO overwrite existing variables.
Keep in mind, that variable names are case sensitive, when overriding.

:param env_file: The path to the `.env` file your application should
use. If a path is not provided, `read_env` will attempt to import
Expand Down Expand Up @@ -770,9 +773,9 @@ def read_env(cls, env_file=None, **overrides):
val = re.sub(r'\\(.)', r'\1', m3.group(1))
cls.ENVIRON.setdefault(key, str(val))

# set defaults
# set overrides
for key, value in overrides.items():
cls.ENVIRON.setdefault(key, value)
cls.ENVIRON[key] = value


class Path:
Expand Down
8 changes: 7 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,5 +65,11 @@ def search_url(request):

@pytest.fixture
def env_file():
"""Return env file for tests.."""
"""Return test_env.txt file path for the testing purposes."""
return os.path.join(os.path.dirname(__file__), 'test_env.txt')


@pytest.fixture
def simple_env_file():
"""Return simple_env.txt file path for the testing purposes."""
return os.path.join(os.path.dirname(__file__), 'simple_env.txt')
1 change: 1 addition & 0 deletions tests/simple_env.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[email protected]
5 changes: 1 addition & 4 deletions tests/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -263,10 +263,7 @@ def setup_method(self, method):
super().setup_method(method)

Env.ENVIRON = {}
self.env.read_env(
Path(__file__, is_file=True)('test_env.txt'),
PATH_VAR=Path(__file__, is_file=True).__root__
)
self.env.read_env(Path(__file__, is_file=True)('test_env.txt'))


class TestSubClass(TestEnv):
Expand Down
18 changes: 9 additions & 9 deletions tests/test_read_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,15 @@
from environ import Env, Path


def test_read_env_priority(env_file, monkeypatch):
def test_read_env_priority(simple_env_file, monkeypatch):
"""Values obtained from the os.environ should have a higher priority."""
monkeypatch.setenv('PATH_VAR', '/tmp')
monkeypatch.setenv('EMAIL', '[email protected]')

env = Env()
env.read_env(env_file, PATH_VAR='/var')
env.read_env(simple_env_file)

# In env_file: PATH_VAR=/home/dev
assert os.environ['PATH_VAR'] == '/tmp'
# In env_file: [email protected]
assert os.environ['EMAIL'] == '[email protected]'


def test_read_env_overrides(env_file):
Expand All @@ -39,15 +39,15 @@ def test_read_env_overrides(env_file):
assert os.environ['SECRET'] == 'top_secret'


def test_read_env_overrides_os(env_file, monkeypatch):
"""Additional keywords should not override vars from the os.environ."""
def test_read_env_override_os(env_file, monkeypatch):
"""Additional keywords should override vars from the os.environ."""
monkeypatch.setenv('SECRET_KEY', 'enigma')
env = Env()

env.read_env(env_file=env_file, SECRET_KEY='top_secret')

assert env.ENVIRON['SECRET_KEY'] == 'enigma'
assert os.environ['SECRET_KEY'] == 'enigma'
assert env.ENVIRON['SECRET_KEY'] == 'top_secret'
assert os.environ['SECRET_KEY'] == 'top_secret'


@pytest.mark.parametrize(
Expand Down