Repository: joke2k/django-environ
Pull request #475 (2023-05-30 15:49:45)

handle inline comments, and ignore spaces outside quotes
capable of handling inline comments (outside quotes).

So if you want "#" hashes in your values, place them inside quotes.

Also ignoring spaces outside quotes 

github-actions[bot]: Your PR was set to target `main`, PRs should be target `develop`
The base branch of this PR has been automatically changed to `develop`, please check that there are no merge conflicts.
lvanderree: I noticed issues regarding this PR:

- https://github.com/joke2k/django-environ/issues/91
- https://github.com/joke2k/django-environ/pull/475
coveralls: 
[![Coverage Status](https://coveralls.io/builds/60964447/badge)](https://coveralls.io/builds/60964447)

coverage: 91.682% (-0.5%) from 92.215% when pulling **56e8e131cc281e6438fc1df2a286a98dc658c613 on lvanderree:issue/91-inline-comments** into **e1486b2c0051bf81991cb1429e76e7b003a56889 on joke2k:develop**.

sergeyklay: Looks great. Thank you for contributing and patience

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1fc3124..aa7b3ff 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -15,11 +15,15 @@ Added
   `#463 <https://github.com/joke2k/django-environ/pull/463>`_.
 - Added variable expansion
   `#468 <https://github.com/joke2k/django-environ/pull/468>`_.
+- Added capability to handle comments after #, after quoted values, like ``KEY= 'part1 # part2' # comment``
+  `#475 <https://github.com/joke2k/django-environ/pull/475>`_.
 
 Changed
 +++++++
 - Used ``mssql-django`` as engine for SQL Server
   `#446 <https://github.com/joke2k/django-environ/pull/446>`_.
+- Changed handling bool values, stripping whitespace around value
+  `#475 <https://github.com/joke2k/django-environ/pull/475>`_.
 
 Removed
 +++++++
diff --git a/environ/environ.py b/environ/environ.py
index f045639..644286e 100644
--- a/environ/environ.py
+++ b/environ/environ.py
@@ -473,7 +473,7 @@ class Env:
             try:
                 value = int(value) != 0
             except ValueError:
-                value = value.lower() in cls.BOOLEAN_TRUE_STRINGS
+                value = value.lower().strip() in cls.BOOLEAN_TRUE_STRINGS
         elif isinstance(cast, list):
             value = list(map(cast[0], [x for x in value.split(',') if x]))
         elif isinstance(cast, tuple):
@@ -970,9 +970,17 @@ class Env:
             m1 = re.match(r'\A(?:export )?([A-Za-z_0-9]+)=(.*)\Z', line)
             if m1:
                 key, val = m1.group(1), m1.group(2)
-                m2 = re.match(r"\A'(.*)'\Z", val)
+                # Look for value in quotes, ignore post-# comments
+                # (outside quotes)
+                m2 = re.match(r"\A\s*'(?<!\\)(.*)'\s*(#.*\s*)?\Z", val)
                 if m2:
                     val = m2.group(1)
+                else:
+                    # For no quotes, find value, ignore comments
+                    # after the first #
+                    m2a = re.match(r"\A(.*?)(#.*\s*)?\Z", val)
+                    if m2a:
+                        val = m2a.group(1)
                 m3 = re.match(r'\A"(.*)"\Z', val)
                 if m3:
                     val = re.sub(r'\\(.)', _keep_escaped_format_characters,


Test patch:
diff --git a/tests/fixtures.py b/tests/fixtures.py
index 6990d9a..69e5e90 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -38,6 +38,8 @@ class FakeEnv:
     @classmethod
     def generate_data(cls):
         return dict(STR_VAR='bar',
+                    STR_QUOTED_IGNORE_COMMENT='foo',
+                    STR_QUOTED_INCLUDE_HASH='foo # with hash',
                     MULTILINE_STR_VAR='foo\\nbar',
                     MULTILINE_QUOTED_STR_VAR='---BEGIN---\\r\\n---END---',
                     MULTILINE_ESCAPED_STR_VAR='---BEGIN---\\\\n---END---',
@@ -50,12 +52,14 @@ class FakeEnv:
                     BOOL_TRUE_STRING_LIKE_INT='1',
                     BOOL_TRUE_INT=1,
                     BOOL_TRUE_STRING_LIKE_BOOL='True',
+                    BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT='True',
                     BOOL_TRUE_STRING_1='on',
                     BOOL_TRUE_STRING_2='ok',
                     BOOL_TRUE_STRING_3='yes',
                     BOOL_TRUE_STRING_4='y',
                     BOOL_TRUE_STRING_5='true',
                     BOOL_TRUE_BOOL=True,
+                    BOOL_TRUE_BOOL_WITH_COMMENT=True,
                     BOOL_FALSE_STRING_LIKE_INT='0',
                     BOOL_FALSE_INT=0,
                     BOOL_FALSE_STRING_LIKE_BOOL='False',
@@ -65,7 +69,8 @@ class FakeEnv:
                     INT_LIST='42,33',
                     INT_TUPLE='(42,33)',
                     MIX_TUPLE='(42,Test)',
-                    STR_LIST_WITH_SPACES=' foo,  bar',
+                    STR_LIST_WITH_SPACES=' foo,  spaces',
+                    STR_LIST_WITH_SPACES_QUOTED="' foo', '  quoted'",
                     EMPTY_LIST='',
                     DICT_VAR='foo=bar,test=on',
                     DICT_WITH_EQ_VAR='key1=sub_key1=sub_value1,key2=value2',
diff --git a/tests/test_env.py b/tests/test_env.py
index 396c212..0b105f5 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -112,8 +112,10 @@ class TestEnv:
         [
             (True, 'BOOL_TRUE_STRING_LIKE_INT'),
             (True, 'BOOL_TRUE_STRING_LIKE_BOOL'),
+            (True, 'BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT'),
             (True, 'BOOL_TRUE_INT'),
             (True, 'BOOL_TRUE_BOOL'),
+            (True, 'BOOL_TRUE_BOOL_WITH_COMMENT'),
             (True, 'BOOL_TRUE_STRING_1'),
             (True, 'BOOL_TRUE_STRING_2'),
             (True, 'BOOL_TRUE_STRING_3'),
@@ -175,9 +177,9 @@ class TestEnv:
         )
 
     def test_str_list_with_spaces(self):
-        assert_type_and_value(list, [' foo', '  bar'],
+        assert_type_and_value(list, [' foo', '  spaces'],
                               self.env('STR_LIST_WITH_SPACES', cast=[str]))
-        assert_type_and_value(list, [' foo', '  bar'],
+        assert_type_and_value(list, [' foo', '  spaces'],
                               self.env.list('STR_LIST_WITH_SPACES'))
 
     def test_empty_list(self):
@@ -339,6 +341,8 @@ class TestEnv:
 
     def test_smart_cast(self):
         assert self.env.get_value('STR_VAR', default='string') == 'bar'
+        assert self.env.get_value('STR_QUOTED_IGNORE_COMMENT', default='string') == 'foo'
+        assert self.env.get_value('STR_QUOTED_INCLUDE_HASH', default='string') == 'foo # with hash'
         assert self.env.get_value('BOOL_TRUE_STRING_LIKE_INT', default=True)
         assert not self.env.get_value(
             'BOOL_FALSE_STRING_LIKE_INT',
diff --git a/tests/test_env.txt b/tests/test_env.txt
index 237489b..d5480bf 100644
--- a/tests/test_env.txt
+++ b/tests/test_env.txt
@@ -25,6 +25,8 @@ BOOL_TRUE_STRING_3='yes'
 BOOL_TRUE_STRING_4='y'
 BOOL_TRUE_STRING_5='true'
 BOOL_TRUE_BOOL=True
+BOOL_TRUE_STRING_LIKE_BOOL_WITH_COMMENT='True' # comment
+BOOL_TRUE_BOOL_WITH_COMMENT=True # comment
 BOOL_FALSE_STRING_LIKE_INT='0'
 BOOL_FALSE_INT=0
 BOOL_FALSE_STRING_LIKE_BOOL='False'
@@ -42,8 +44,11 @@ ESCAPED_VAR=\$baz
 EMPTY_LIST=
 EMPTY_INT_VAR=
 INT_VAR=42
-STR_LIST_WITH_SPACES= foo,  bar
+STR_LIST_WITH_SPACES= foo,  spaces
+STR_LIST_WITH_SPACES_QUOTED=' foo','  quoted'
 STR_VAR=bar
+STR_QUOTED_IGNORE_COMMENT= 'foo' # comment
+STR_QUOTED_INCLUDE_HASH='foo # with hash' # not comment
 MULTILINE_STR_VAR=foo\nbar
 MULTILINE_QUOTED_STR_VAR="---BEGIN---\r\n---END---"
 MULTILINE_ESCAPED_STR_VAR=---BEGIN---\\n---END---