Skip to content

fix: escape backslashes in set_key to preserve round-trip - #666

Closed
gaoflow wants to merge 1 commit into
theskumar:mainfrom
gaoflow:fix-set-key-backslash-roundtrip
Closed

fix: escape backslashes in set_key to preserve round-trip#666
gaoflow wants to merge 1 commit into
theskumar:mainfrom
gaoflow:fix-set-key-backslash-roundtrip

Conversation

@gaoflow

@gaoflow gaoflow commented Jun 24, 2026

Copy link
Copy Markdown

Bug

set_key() writes values wrapped in single quotes, and the parser decodes \\ as a literal backslash via decode_escapes(). But set_key() only escaped single-quote characters (\'), not backslashes themselves. This meant any value containing two or more consecutive backslashes lost one during round-trip:

Also reproduces for Windows paths (C:\Users\...) and any value with \\ sequences.

Root Cause

The parser's _single_quote_escapes regex matches both \' and \\, decoding them via codecs.decode(..., 'unicode-escape'). When set_key writes a value containing literal \\, the parser interprets it as an escaped backslash and decodes it to \, losing one backslash on each consecutive pair.

Fix

Escape backslashes in addition to single-quotes in set_key(), before wrapping in single quotes. The order is important: backslashes must be escaped first (\\ -> \\\\), then single-quotes (' -> \').

Diff

  • src/dotenv/main.py: +2/-0 (escape backslashes before single-quotes)
  • tests/test_main.py: +23 (parametrized round-trip regression test covering 0-3 backslashes, Windows paths, and mixed quotes+backslashes)

set_key() writes values wrapped in single quotes, and the parser
decodes \\ as a literal backslash via decode_escapes().  But
set_key() only escaped single-quote characters (\'), not backslashes
themselves.  This meant any value containing two or more consecutive
backslashes lost one during round-trip:

    set_key(path, 'K', 'a\\\\b')
    dotenv_values(path)['K']  # -> 'a\\b' (lost one backslash)

The parser's _single_quote_escapes regex matches both \\' and \\\\,
so backslashes MUST be doubled when writing to preserve them.  Fix by
escaping backslashes before single-quotes in the value quoting step.

- src/dotenv/main.py: +2/-0
- tests/test_main.py: +23 (parametrized round-trip regression test)
@theskumar theskumar added the inprogress Being actively reviewed/worked on label Aug 16, 2026
@theskumar

Copy link
Copy Markdown
Owner

Hey @gaoflow - thank you for this, and for the clear root-cause writeup, it made the bug easy to follow. I'm consolidating the backslash fix for #661 into #680, which builds on the same idea and also fixes the parser side (a trailing backslash used to swallow the next line). Closing this in its favor - nothing wrong with your patch, just landing one. Thanks again!

@theskumar theskumar closed this Aug 16, 2026
@theskumar theskumar removed the inprogress Being actively reviewed/worked on label Aug 16, 2026
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.

2 participants