Repository: theskumar/python-dotenv (python)

Issue:
Warning for consecutive lines that are comments
With #211 (v0.10.4), if there are consecutive lines that are comments, there will be a warning for each line after the first. because `key` and `value` will be `None`, passing the check for the warning. The only reason the first line does not have a warning is because it is considered as part of the line before, since the regular expression for comments captures any whitespace, including newlines, at the beginning.

Pull request:
Fix handling of lines with just a comment
Closes #223.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3b3a429..78b65cc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -22,6 +22,7 @@ Latest
 - Fix handling of malformed lines and lines without a value:
   - Don't print warning when key has no value.
   - Reject more malformed lines (e.g. "A: B").
+- Fix handling of lines with just a comment.
 
 0.10.4
 -----
diff --git a/src/dotenv/main.py b/src/dotenv/main.py
index 7051cd3..3bf4a2c 100644
--- a/src/dotenv/main.py
+++ b/src/dotenv/main.py
@@ -37,7 +37,7 @@ __posix_variable = re.compile(r'\$\{[^\}]*\}')  # type: Pattern[Text]
 def with_warn_for_invalid_lines(mappings):
     # type: (Iterator[Binding]) -> Iterator[Binding]
     for mapping in mappings:
-        if mapping.key is None:
+        if mapping.error:
             logger.warning(
                 "Python-dotenv could not parse statement starting at line %s",
                 mapping.original.line,
diff --git a/src/dotenv/parser.py b/src/dotenv/parser.py
index 516bacc..2904af8 100644
--- a/src/dotenv/parser.py
+++ b/src/dotenv/parser.py
@@ -25,7 +25,7 @@ _equal_sign = make_regex(r"(=[^\S\r\n]*)")
 _single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'")
 _double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"')
 _unquoted_value_part = make_regex(r"([^ \r\n]*)")
-_comment = make_regex(r"(?:\s*#[^\r\n]*)?")
+_comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?")
 _end_of_line = make_regex(r"[^\S\r\n]*(?:\r\n|\n|\r|$)")
 _rest_of_line = make_regex(r"[^\r\n]*(?:\r|\n|\r\n)?")
 _double_quote_escapes = make_regex(r"\\[\\'\"abfnrtv]")
@@ -52,6 +52,7 @@ try:
             ("key", typing.Optional[typing.Text]),
             ("value", typing.Optional[typing.Text]),
             ("original", Original),
+            ("error", bool),
         ],
     )
 except ImportError:
@@ -69,6 +70,7 @@ except ImportError:
             "key",
             "value",
             "original",
+            "error",
         ],
     )
 
@@ -152,9 +154,11 @@ def decode_escapes(regex, string):
 
 
 def parse_key(reader):
-    # type: (Reader) -> Text
+    # type: (Reader) -> Optional[Text]
     char = reader.peek(1)
-    if char == "'":
+    if char == "#":
+        return None
+    elif char == "'":
         (key,) = reader.read_regex(_single_quoted_key)
     else:
         (key,) = reader.read_regex(_unquoted_key)
@@ -207,6 +211,7 @@ def parse_binding(reader):
             key=key,
             value=value,
             original=reader.get_marked(),
+            error=False,
         )
     except Error:
         reader.read_regex(_rest_of_line)
@@ -214,6 +219,7 @@ def parse_binding(reader):
             key=None,
             value=None,
             original=reader.get_marked(),
+            error=True,
         )
 
 


Test patch:
diff --git a/tests/test_parser.py b/tests/test_parser.py
index 837717d..dae51d3 100644
--- a/tests/test_parser.py
+++ b/tests/test_parser.py
@@ -7,84 +7,119 @@ from dotenv.parser import Binding, Original, parse_stream
 
 @pytest.mark.parametrize("test_input,expected", [
     (u"", []),
-    (u"a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"a=b", line=1))]),
-    (u"'a'=b", [Binding(key=u"a", value=u"b", original=Original(string=u"'a'=b", line=1))]),
-    (u"[=b", [Binding(key=u"[", value=u"b", original=Original(string=u"[=b", line=1))]),
-    (u" a = b ", [Binding(key=u"a", value=u"b", original=Original(string=u" a = b ", line=1))]),
-    (u"export a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"export a=b", line=1))]),
-    (u" export 'a'=b", [Binding(key=u"a", value=u"b", original=Original(string=u" export 'a'=b", line=1))]),
-    (u"# a=b", [Binding(key=None, value=None, original=Original(string=u"# a=b", line=1))]),
-    (u"a=b#c", [Binding(key=u"a", value=u"b#c", original=Original(string=u"a=b#c", line=1))]),
-    (u'a=b # comment', [Binding(key=u"a", value=u"b", original=Original(string=u"a=b # comment", line=1))]),
-    (u"a=b space ", [Binding(key=u"a", value=u"b space", original=Original(string=u"a=b space ", line=1))]),
-    (u"a='b space '", [Binding(key=u"a", value=u"b space ", original=Original(string=u"a='b space '", line=1))]),
-    (u'a="b space "', [Binding(key=u"a", value=u"b space ", original=Original(string=u'a="b space "', line=1))]),
+    (u"a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"a=b", line=1), error=False)]),
+    (u"'a'=b", [Binding(key=u"a", value=u"b", original=Original(string=u"'a'=b", line=1), error=False)]),
+    (u"[=b", [Binding(key=u"[", value=u"b", original=Original(string=u"[=b", line=1), error=False)]),
+    (u" a = b ", [Binding(key=u"a", value=u"b", original=Original(string=u" a = b ", line=1), error=False)]),
+    (u"export a=b", [Binding(key=u"a", value=u"b", original=Original(string=u"export a=b", line=1), error=False)]),
+    (
+        u" export 'a'=b",
+        [Binding(key=u"a", value=u"b", original=Original(string=u" export 'a'=b", line=1), error=False)],
+    ),
+    (u"# a=b", [Binding(key=None, value=None, original=Original(string=u"# a=b", line=1), error=False)]),
+    (u"a=b#c", [Binding(key=u"a", value=u"b#c", original=Original(string=u"a=b#c", line=1), error=False)]),
+    (
+        u'a=b # comment',
+        [Binding(key=u"a", value=u"b", original=Original(string=u"a=b # comment", line=1), error=False)],
+    ),
+    (
+        u"a=b space ",
+        [Binding(key=u"a", value=u"b space", original=Original(string=u"a=b space ", line=1), error=False)],
+    ),
+    (
+        u"a='b space '",
+        [Binding(key=u"a", value=u"b space ", original=Original(string=u"a='b space '", line=1), error=False)],
+    ),
+    (
+        u'a="b space "',
+        [Binding(key=u"a", value=u"b space ", original=Original(string=u'a="b space "', line=1), error=False)],
+    ),
     (
         u"export export_a=1",
         [
-            Binding(key=u"export_a", value=u"1", original=Original(string=u"export export_a=1", line=1))
+            Binding(key=u"export_a", value=u"1", original=Original(string=u"export export_a=1", line=1), error=False)
         ],
     ),
-    (u"export port=8000", [Binding(key=u"port", value=u"8000", original=Original(string=u"export port=8000", line=1))]),
-    (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1))]),
-    (u"a='b\nc'", [Binding(key=u"a", value=u"b\nc", original=Original(string=u"a='b\nc'", line=1))]),
-    (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1))]),
-    (u'a="b\\nc"', [Binding(key=u"a", value=u'b\nc', original=Original(string=u'a="b\\nc"', line=1))]),
-    (u"a='b\\nc'", [Binding(key=u"a", value=u'b\\nc', original=Original(string=u"a='b\\nc'", line=1))]),
-    (u'a="b\\"c"', [Binding(key=u"a", value=u'b"c', original=Original(string=u'a="b\\"c"', line=1))]),
-    (u"a='b\\'c'", [Binding(key=u"a", value=u"b'c", original=Original(string=u"a='b\\'c'", line=1))]),
-    (u"a=à", [Binding(key=u"a", value=u"à", original=Original(string=u"a=à", line=1))]),
-    (u'a="à"', [Binding(key=u"a", value=u"à", original=Original(string=u'a="à"', line=1))]),
-    (u'no_value_var', [Binding(key=u'no_value_var', value=None, original=Original(string=u"no_value_var", line=1))]),
-    (u'a: b', [Binding(key=None, value=None, original=Original(string=u"a: b", line=1))]),
+    (
+        u"export port=8000",
+        [Binding(key=u"port", value=u"8000", original=Original(string=u"export port=8000", line=1), error=False)],
+    ),
+    (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1), error=False)]),
+    (u"a='b\nc'", [Binding(key=u"a", value=u"b\nc", original=Original(string=u"a='b\nc'", line=1), error=False)]),
+    (u'a="b\nc"', [Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"', line=1), error=False)]),
+    (u'a="b\\nc"', [Binding(key=u"a", value=u'b\nc', original=Original(string=u'a="b\\nc"', line=1), error=False)]),
+    (u"a='b\\nc'", [Binding(key=u"a", value=u'b\\nc', original=Original(string=u"a='b\\nc'", line=1), error=False)]),
+    (u'a="b\\"c"', [Binding(key=u"a", value=u'b"c', original=Original(string=u'a="b\\"c"', line=1), error=False)]),
+    (u"a='b\\'c'", [Binding(key=u"a", value=u"b'c", original=Original(string=u"a='b\\'c'", line=1), error=False)]),
+    (u"a=à", [Binding(key=u"a", value=u"à", original=Original(string=u"a=à", line=1), error=False)]),
+    (u'a="à"', [Binding(key=u"a", value=u"à", original=Original(string=u'a="à"', line=1), error=False)]),
+    (
+        u'no_value_var',
+        [Binding(key=u'no_value_var', value=None, original=Original(string=u"no_value_var", line=1), error=False)],
+    ),
+    (u'a: b', [Binding(key=None, value=None, original=Original(string=u"a: b", line=1), error=True)]),
     (
         u"a=b\nc=d",
         [
-            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1)),
-            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2)),
+            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1), error=False),
+            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False),
         ],
     ),
     (
         u"a=b\rc=d",
         [
-            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r", line=1)),
-            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2)),
+            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r", line=1), error=False),
+            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False),
         ],
     ),
     (
         u"a=b\r\nc=d",
         [
-            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r\n", line=1)),
-            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2)),
+            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\r\n", line=1), error=False),
+            Binding(key=u"c", value=u"d", original=Original(string=u"c=d", line=2), error=False),
         ],
     ),
     (
         u'a=\nb=c',
         [
-            Binding(key=u"a", value=u'', original=Original(string=u'a=\n', line=1)),
-            Binding(key=u"b", value=u'c', original=Original(string=u"b=c", line=2)),
+            Binding(key=u"a", value=u'', original=Original(string=u'a=\n', line=1), error=False),
+            Binding(key=u"b", value=u'c', original=Original(string=u"b=c", line=2), error=False),
         ]
     ),
     (
         u'a=b\n\nc=d',
         [
-            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1)),
-            Binding(key=u"c", value=u"d", original=Original(string=u"\nc=d", line=2)),
+            Binding(key=u"a", value=u"b", original=Original(string=u"a=b\n", line=1), error=False),
+            Binding(key=u"c", value=u"d", original=Original(string=u"\nc=d", line=2), error=False),
         ]
     ),
     (
         u'a="\nb=c',
         [
-            Binding(key=None, value=None, original=Original(string=u'a="\n', line=1)),
-            Binding(key=u"b", value=u"c", original=Original(string=u"b=c", line=2)),
+            Binding(key=None, value=None, original=Original(string=u'a="\n', line=1), error=True),
+            Binding(key=u"b", value=u"c", original=Original(string=u"b=c", line=2), error=False),
         ]
     ),
     (
         u'# comment\na="b\nc"\nd=e\n',
         [
-            Binding(key=None, value=None, original=Original(string=u"# comment\n", line=1)),
-            Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"\n', line=2)),
-            Binding(key=u"d", value=u"e", original=Original(string=u"d=e\n", line=4)),
+            Binding(key=None, value=None, original=Original(string=u"# comment\n", line=1), error=False),
+            Binding(key=u"a", value=u"b\nc", original=Original(string=u'a="b\nc"\n', line=2), error=False),
+            Binding(key=u"d", value=u"e", original=Original(string=u"d=e\n", line=4), error=False),
+        ],
+    ),
+    (
+        u'a=b\n# comment 1',
+        [
+            Binding(key="a", value="b", original=Original(string=u"a=b\n", line=1), error=False),
+            Binding(key=None, value=None, original=Original(string=u"# comment 1", line=2), error=False),
+        ],
+    ),
+    (
+        u'# comment 1\n# comment 2',
+        [
+            Binding(key=None, value=None, original=Original(string=u"# comment 1\n", line=1), error=False),
+            Binding(key=None, value=None, original=Original(string=u"# comment 2", line=2), error=False),
         ],
     ),
     (
@@ -92,8 +127,8 @@ from dotenv.parser import Binding, Original, parse_stream
         [
             Binding(key=u'uglyKey[%$',
                     value=u'S3cr3t_P4ssw#rD',
-                    original=Original(string=u"uglyKey[%$=\"S3cr3t_P4ssw#rD\" #\n", line=1)),
-            Binding(key=u"a", value=u"b", original=Original(string=u'a=b', line=2)),
+                    original=Original(string=u"uglyKey[%$=\"S3cr3t_P4ssw#rD\" #\n", line=1), error=False),
+            Binding(key=u"a", value=u"b", original=Original(string=u'a=b', line=2), error=False),
         ],
     ),
 ])