Skip to content

Escape backslashes so set_key values round-trip#673

Open
eeshsaxena wants to merge 2 commits into
theskumar:mainfrom
eeshsaxena:fix-set-key-backslash
Open

Escape backslashes so set_key values round-trip#673
eeshsaxena wants to merge 2 commits into
theskumar:mainfrom
eeshsaxena:fix-set-key-backslash

Conversation

@eeshsaxena

Copy link
Copy Markdown

Summary

Fixes #661.

set_key (and dotenv set) writes single-quoted values but only escapes single quotes, not backslashes. Because the single-quoted-value parser treats backslash as an escape character, values containing backslashes did not round-trip:

  • an embedded \ collapsed to \ on read;
  • a value ending in a backslash (e.g. a Windows path like C:\) wrote a trailing \', which the parser read as an escaped quote - swallowing the closing quote and corrupting the whole file (subsequent lines became unparseable).

Reproduction (before)

import dotenv
dotenv.set_key(".env", "A", r"a\b")   # embedded double backslash
dotenv.get_key(".env", "A")            # -> "a\b"   (one backslash lost)

dotenv.set_key(".env", "B", "C:\\")    # trailing backslash
dotenv.get_key(".env", "B")            # -> None    (line unparseable)

Fix

  1. In set_key, escape backslashes before single quotes (order matters) so the written value round-trips.
  2. Update the _single_quoted_value tokenizer from (?:\'|[^'])* to (?:\[\s\S]|[^'\])* so every \<char> (including \) is consumed as one escaped unit. Without this, even a correctly-escaped trailing backslash ('C:\') merges its second backslash with the closing quote. The [\s\S] keeps the previous acceptance of newlines after a backslash inside single quotes.

The change is scoped to single-quoted values (the form set_key emits). Values without backslashes are unaffected, so all existing set_key output expectations are unchanged.

Tests

  • test_set_key_backslash_round_trip - round-trips Windows paths, regexes, embedded/trailing backslashes, and backslash-before-quote.
  • Two test_parse_stream cases - an escaped backslash decodes to one backslash, and a trailing escaped backslash does not consume the closing quote.

Full test_parser.py + test_main.py pass (124 passed). (test_cli.py::test_run_with_invalid_cmd fails on main too - a pre-existing Windows-only error-message assertion, unrelated to this change.)

set_key writes single-quoted values but only escaped single quotes, not
backslashes. Since the single-quoted-value parser treats backslash as an
escape character, values containing backslashes did not round-trip: an
embedded "\\" collapsed to "\", and a value ending in a backslash (e.g. a
Windows path like "C:\") wrote a trailing "\'" that swallowed the closing
quote and corrupted the whole file.

Escape backslashes before single quotes in set_key, and update the
single-quoted-value tokenizer to treat every "\<char>" as one escaped
unit so an escaped trailing backslash no longer merges with the closing
quote.

Fixes theskumar#661
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

set_key corrupts values containing backslashes (Windows paths, regexes) on round-trip

1 participant