Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Keep newline/tab escapes in quoted strings (fix certificate serializing) #296

Merged
merged 4 commits into from
Sep 14, 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
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to `Semantic Versioning <https://semver.org/spec/v2.0.0

`v0.8.0`_ - 00-Unreleased-2021
------------------------------
Fixed
+++++
- Keep newline/tb escaped in quoted strings


`v0.7.0`_ - 11-September-2021
Expand Down
49 changes: 44 additions & 5 deletions docs/tips.rst
Original file line number Diff line number Diff line change
Expand Up @@ -91,15 +91,54 @@ You can use something like this to handle similar cases.
Multiline value
===============

You can set a multiline variable value:
To get multiline value pass ``multiline=True`` to ```str()```.

.. note::

You shouldn't escape newline/tab characters yourself if you want to preserve
the formatting.

The following example demonstrates the above:

**.env file**:

.. code-block:: shell

# .env file contents
UNQUOTED_CERT=---BEGIN---\r\n---END---
QUOTED_CERT="---BEGIN---\r\n---END---"
ESCAPED_CERT=---BEGIN---\\n---END---

**settings.py file**:

.. code-block:: python

# MULTILINE_TEXT=Hello\\nWorld
>>> print env.str('MULTILINE_TEXT', multiline=True)
Hello
World
# settings.py file contents
import environ


env = environ.Env()

print(env.str('UNQUOTED_CERT', multiline=True))
# ---BEGIN---
# ---END---

print(env.str('UNQUOTED_CERT', multiline=False))
# ---BEGIN---\r\n---END---

print(env.str('QUOTED_CERT', multiline=True))
# ---BEGIN---
# ---END---

print(env.str('QUOTED_CERT', multiline=False))
# ---BEGIN---\r\n---END---

print(env.str('ESCAPED_CERT', multiline=True))
# ---BEGIN---\
# ---END---

print(env.str('ESCAPED_CERT', multiline=False))
# ---BEGIN---\\n---END---

Proxy value
===========
Expand Down
12 changes: 10 additions & 2 deletions environ/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def str(self, var, default=NOTSET, multiline=False):
"""
value = self.get_value(var, cast=str, default=default)
if multiline:
return value.replace('\\n', '\n')
return re.sub(r'(\\r)?\\n', r'\n', value)
return value

def unicode(self, var, default=NOTSET):
Expand Down Expand Up @@ -769,6 +769,13 @@ def read_env(cls, env_file=None, **overrides):

logger.debug('Read environment variables from: {}'.format(env_file))

def _keep_escaped_format_characters(match):
"""Keep escaped newline/tabs in quoted strings"""
escaped_char = match.group(1)
if escaped_char in 'rnt':
return '\\' + escaped_char
return escaped_char

for line in content.splitlines():
m1 = re.match(r'\A(?:export )?([A-Za-z_0-9]+)=(.*)\Z', line)
if m1:
Expand All @@ -778,7 +785,8 @@ def read_env(cls, env_file=None, **overrides):
val = m2.group(1)
m3 = re.match(r'\A"(.*)"\Z', val)
if m3:
val = re.sub(r'\\(.)', r'\1', m3.group(1))
val = re.sub(r'\\(.)', _keep_escaped_format_characters,
m3.group(1))
cls.ENVIRON.setdefault(key, str(val))

# set defaults
Expand Down
2 changes: 2 additions & 0 deletions tests/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class FakeEnv:
def generate_data(cls):
return dict(STR_VAR='bar',
MULTILINE_STR_VAR='foo\\nbar',
MULTILINE_QUOTED_STR_VAR='---BEGIN---\\r\\n---END---',
MULTILINE_ESCAPED_STR_VAR='---BEGIN---\\\\n---END---',
INT_VAR='42',
FLOAT_VAR='33.3',
FLOAT_COMMA_VAR='33,3',
Expand Down
4 changes: 4 additions & 0 deletions tests/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ def test_contains(self):
('STR_VAR', 'bar', False),
('MULTILINE_STR_VAR', 'foo\\nbar', False),
('MULTILINE_STR_VAR', 'foo\nbar', True),
('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\\r\\n---END---', False),
('MULTILINE_QUOTED_STR_VAR', '---BEGIN---\n---END---', True),
('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\\n---END---', False),
('MULTILINE_ESCAPED_STR_VAR', '---BEGIN---\\\n---END---', True),
],
)
def test_str(self, var, val, multiline):
Expand Down
2 changes: 2 additions & 0 deletions tests/test_env.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ INT_VAR=42
STR_LIST_WITH_SPACES= foo, bar
STR_VAR=bar
MULTILINE_STR_VAR=foo\nbar
MULTILINE_QUOTED_STR_VAR="---BEGIN---\r\n---END---"
MULTILINE_ESCAPED_STR_VAR=---BEGIN---\\n---END---
INT_LIST=42,33
CYRILLIC_VAR=фуубар
INT_TUPLE=(42,33)
Expand Down