Escape backslashes when serializing marker values - #1374
Open
sueun-dev wants to merge 2 commits into
Open
Conversation
Marker and requirement values are read back with ``ast.literal_eval`` (``_parser.process_python_str``), which decodes backslash escapes, but ``Value.serialize`` re-wrapped the decoded value in quotes without re-escaping. A value containing a backslash therefore did not survive a ``str()`` round trip: ``os_name == "C:\\temp"`` (value ``C:\temp``) serialized to ``os_name == "C:\temp"``, which reparses with ``\t`` as a tab, so ``Marker(str(m)) != m`` (and the same for ``Requirement``). Double the backslashes on serialization so the value round-trips unchanged. This completes the round-trip contract added for the quote case in pypa#1213.
henryiii
reviewed
Aug 10, 2026
Comment on lines
+72
to
+75
| # ``process_python_str`` reads a value back with ``ast.literal_eval``, | ||
| # which decodes backslash escapes. Double the backslashes so a value | ||
| # that contains one survives the round trip instead of being re-decoded | ||
| # (e.g. ``C:\temp`` would otherwise reparse to ``C:<TAB>emp``). |
Contributor
There was a problem hiding this comment.
Suggested change
| # ``process_python_str`` reads a value back with ``ast.literal_eval``, | |
| # which decodes backslash escapes. Double the backslashes so a value | |
| # that contains one survives the round trip instead of being re-decoded | |
| # (e.g. ``C:\temp`` would otherwise reparse to ``C:<TAB>emp``). | |
| # ``process_python_str`` reads a value with ``ast.literal_eval``, | |
| # which decodes backslash escapes. |
But are there any string-valued markers that might be a path, though? os_name won't be a path. I'm not sure what chars are valid in a node name, that's the main arbitrary one I can think of. I'm not sure the value if there are no markers that can possibly have a backslash.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Marker and requirement values are read back with
ast.literal_eval(in_parser.process_python_str), which decodes backslash escapes.Value.serializere-wraps the decoded value in quotes but doesn't re-escape it, so a value containing a backslash doesn't survive astr()round trip:Same for
Requirement:serializeis the inverse of theast.literal_evalparser, so it has to escape everything the parser decodes. This is the backslash counterpart of the quote round trip fixed in #1213; the fix doubles backslashes before quoting and leaves the quote-delimiter selection alone.Added regression tests for
Value.serializeand for theMarker/Requirementround trip. Both fail before this change and pass after.