From 40ed1b1bd09321286062ba20ae38535a224167ee Mon Sep 17 00:00:00 2001 From: Kyle Altendorf Date: Tue, 25 Sep 2018 10:24:51 -0400 Subject: [PATCH] Fix empty self-referential variable issue (#135) * Show empty self-referential variable issue theskumar/python-dotenv#132 * Correct test names to use interpolation * Only use previously defined variables when handling interpolation * Recover original os.environ after tweaking it in a test --- dotenv/main.py | 8 +++++--- tests/test_core.py | 29 +++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/dotenv/main.py b/dotenv/main.py index e281f51a..e6040e49 100644 --- a/dotenv/main.py +++ b/dotenv/main.py @@ -187,7 +187,7 @@ def _replacement(name): first search in environ, if not found, then look into the dotenv variables """ - ret = os.getenv(name, values.get(name, "")) + ret = os.getenv(name, new_values.get(name, "")) return ret def _re_sub_callback(match_object): @@ -197,10 +197,12 @@ def _re_sub_callback(match_object): """ return _replacement(match_object.group()[2:-1]) + new_values = {} + for k, v in values.items(): - values[k] = __posix_variable.sub(_re_sub_callback, v) + new_values[k] = __posix_variable.sub(_re_sub_callback, v) - return values + return new_values def _walk_to_root(path): diff --git a/tests/test_core.py b/tests/test_core.py index 4c03f8e7..45a1f86a 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from __future__ import unicode_literals +import contextlib import os import pytest import tempfile @@ -13,6 +14,16 @@ from IPython.terminal.embed import InteractiveShellEmbed +@contextlib.contextmanager +def restore_os_environ(): + environ = dict(os.environ) + + try: + yield + finally: + os.environ.update(environ) + + @pytest.mark.parametrize("test_input,expected", [ ("a=b", ("a", "b")), (" a = b ", ("a", "b")), @@ -161,3 +172,21 @@ def test_dotenv_values_export(): load_dotenv(stream=stream) assert 'foo' in os.environ assert os.environ['foo'] == 'bar' + + +def test_dotenv_empty_selfreferential_interpolation(): + stream = StringIO(u'some_path="${some_path}:a/b/c"\n') + stream.seek(0) + assert u'some_path' not in os.environ + parsed_dict = dotenv_values(stream=stream) + assert {u'some_path': u':a/b/c'} == parsed_dict + + +def test_dotenv_nonempty_selfreferential_interpolation(): + stream = StringIO(u'some_path="${some_path}:a/b/c"\n') + stream.seek(0) + assert u'some_path' not in os.environ + with restore_os_environ(): + os.environ[u'some_path'] = u'x/y/z' + parsed_dict = dotenv_values(stream=stream) + assert {u'some_path': u'x/y/z:a/b/c'} == parsed_dict