Skip to content

fix: quote keys that the parser cannot read unquoted - #747

Open
herniqeu wants to merge 1 commit into
theskumar:mainfrom
herniqeu:fix/set-key-quote-keys
Open

herniqeu wants to merge 1 commit into
theskumar:mainfrom
herniqeu:fix/set-key-quote-keys

Conversation

@herniqeu

@herniqeu herniqeu commented Nov 1, 2026

Copy link
Copy Markdown

set_key() writes the key exactly as it is given, but the unquoted key grammar only accepts [^=#\s]+. A key with a space, a # or an = therefore produces a line the parser cannot read, and the variable is lost without any error:

>>> set_key('.env', 'MY VAR', 'x')
>>> dotenv_values('.env')
{}
# python-dotenv could not parse statement starting at line 1

Same for A#B and A=B — all 13 values I tried disappeared. The call reports success, so a script that builds key names from labels writes a .env that silently misses the variable.

The parser already knows how to read a quoted key ('A B'='x' comes back as {'A B': 'x'}), so set_key() now quotes the key whenever it contains whitespace, # or =:

>>> set_key('.env', 'MY VAR', 'x')
>>> dotenv_values('.env')
{'MY VAR': 'x'}

Two keys remain impossible to represent, and now raise ValueError instead of writing a line that will be dropped: an empty key, and a key that needs quoting and contains a ' (the quoted-key grammar has no escape for it). A key that can be written unquoted is left exactly as before, and updating an existing quoted key rewrites that one line in place, keeping the rest of the file byte for byte.

Tests

  • keys with a space, a #, an = and a tab survive set_key → get_key, and the line after them is not swallowed;
  • an existing quoted key is updated in place ('a b'='old' → 'a b'='new', c=1 untouched);
  • an empty key and a b'c raise ValueError.

On main those seven tests fail; tests/test_main.py goes from 153 to 160 passing with the change. (tests/test_cli.py still has failures from $HOME handling in this sandbox, unrelated.)


Written with AI assistance.

set_key() writes the key as given, but the unquoted key grammar only
accepts [^=#\s]+, so a key with a space, a # or an = produces a line the
parser cannot read. The variable then disappears without an error - the
file gets a warning at read time and the value is silently lost:

    >>> set_key('.env', 'MY VAR', 'x')
    >>> dotenv_values('.env')
    {}                                  # and a 'could not parse' warning

The same happens for 'A#B' and 'A=B'. Since the parser already supports
quoted keys ('A B' = 'x' is read back as A B), set_key now quotes the key
whenever it contains whitespace, a # or an =, so the value survives the
round-trip:

    >>> set_key('.env', 'MY VAR', 'x')
    >>> dotenv_values('.env')
    {'MY VAR': 'x'}

Two keys stay impossible to write and now raise ValueError instead of
writing a file that loses the line: an empty key, and a key that needs
quoting and contains a single quote (the quoted-key grammar has no
escape). Updating an existing quoted key rewrites it in place and keeps
the rest of the file untouched.

Tests: keys with a space, #, = and a tab round-trip through set_key and
get_key without swallowing the line after them; an existing quoted key is
updated in place; empty and quote-containing keys raise ValueError. These
seven tests fail on main and tests/test_main.py goes from 153 to 160
passing (5 failures remain in tests/test_cli.py, all from $HOME paths in
this sandbox).

This branch has not been deployed

No deployments
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.

1 participant