diff --git a/src/dotenv/parser.py b/src/dotenv/parser.py index eb100b47..a2283dd6 100644 --- a/src/dotenv/parser.py +++ b/src/dotenv/parser.py @@ -23,6 +23,7 @@ def make_regex(string: str, extra_flags: int = 0) -> Pattern[str]: _unquoted_key = make_regex(r"([^=\#\s]+)") _equal_sign = make_regex(r"(=[^\S\r\n]*)") _single_quoted_value = make_regex(r"'((?:\\'|[^'])*)'") +_single_quote_splice = make_regex('"\'"') _double_quoted_value = make_regex(r'"((?:\\"|[^"])*)"') _unquoted_value = make_regex(r"([^\r\n]*)") _comment = make_regex(r"(?:[^\S\r\n]*#[^\r\n]*)?") @@ -125,11 +126,23 @@ def parse_unquoted_value(reader: Reader) -> str: return re.sub(r"\s+#.*", "", part).rstrip() +def parse_single_quoted_value(reader: Reader) -> str: + splice_value = '"\'"' + value = "" + while True: + (part,) = reader.read_regex(_single_quoted_value) + value += decode_escapes(_single_quote_escapes, part) + if reader.peek(len(splice_value)) != splice_value: + break + reader.read_regex(_single_quote_splice) + value += "'" + return value + + def parse_value(reader: Reader) -> str: char = reader.peek(1) if char == "'": - (value,) = reader.read_regex(_single_quoted_value) - return decode_escapes(_single_quote_escapes, value) + return parse_single_quoted_value(reader) elif char == '"': (value,) = reader.read_regex(_double_quoted_value) return decode_escapes(_double_quote_escapes, value) diff --git a/tests/test_main.py b/tests/test_main.py index 50703af0..f55ec711 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -663,6 +663,7 @@ def test_dotenv_values_file(dotenv_path): # With quotes ({"b": "c"}, 'a="${b}"', True, {"a": "c"}), ({"b": "c"}, "a='${b}'", True, {"a": "c"}), + ({}, "a='b'\"'\"'c'", True, {"a": "b'c"}), # With surrounding text ({"b": "c"}, "a=x${b}y", True, {"a": "xcy"}), # Self-referential diff --git a/tests/test_parser.py b/tests/test_parser.py index 43386e5a..c224170b 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -295,6 +295,17 @@ ) ], ), + ( + "a='b'\"'\"'c'", + [ + Binding( + key="a", + value="b'c", + original=Original(string="a='b'\"'\"'c'", line=1), + error=False, + ) + ], + ), ( "a=à", [