Skip to content

fix(formats): keep Windows paths intact in requirements option lines - #3863

Closed
deepspace28 wants to merge 1 commit into
pdm-project:mainfrom
deepspace28:fix/requirements-windows-paths
Closed

fix(formats): keep Windows paths intact in requirements option lines#3863
deepspace28 wants to merge 1 commit into
pdm-project:mainfrom
deepspace28:fix/requirements-windows-paths

Conversation

@deepspace28

Copy link
Copy Markdown
Contributor

Option lines go through shlex.split, which runs in POSIX mode where a backslash escapes the next character. A native Windows path in a -e or -r line is destroyed before argparse ever sees it:

>>> shlex.split(r"-e C:\src\pkg")
["-e", "C:srcpkg"]
>>> shlex.split(r"-r C:\src\reqs.txt")
["-r", "C:srcreqs.txt"]

So pdm import of a requirements file that references a local package or a nested requirements file by absolute Windows path silently resolves the wrong location. There is no error - the path just loses its separators.

Escape backslashes on Windows before splitting, which keeps quoting behaving as it does everywhere else:

>>> RequirementParser._split_args(r"-e \"C:\src\my pkg\"")
["-e", "C:\src\my pkg"]

POSIX platforms are untouched, since a backslash there is a legitimate escape. Covered by a parametrized test that pins os.name so it runs on every platform.

shlex.split runs in POSIX mode, where a backslash escapes the next
character. Option lines carrying a native Windows path were therefore
mangled before argparse ever saw them: -e C:\src\pkg arrived as
C:srcpkg, and the same for -r. Escape backslashes on Windows before
splitting, so quoting keeps working as it does elsewhere.
@deepspace28

Copy link
Copy Markdown
Contributor Author

Withdrawing this - I should have checked the reference implementation first.

pip does exactly the same thing, for the same reason: break_args_options hands the whole option string to shlex.split in POSIX mode.

>>> from pip._internal.req.req_file import break_args_options, build_parser
>>> # pip 26.2.1
>>> opts, _ = parser.parse_args(shlex.split(break_args_options(r"-e C:\src\pkg")[1]), defaults)
>>> opts.editables
["C:srcpkg"]

-r and --find-links behave the same. Since RequirementParser documents itself as following pip's requirements-file format, matching pip here is the correct behaviour and this patch would be a silent divergence rather than a fix. Sorry for the noise.

@codecov

codecov Bot commented Aug 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.73%. Comparing base (14b5b9d) to head (c0103c7).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3863   +/-   ##
=======================================
  Coverage   88.72%   88.73%           
=======================================
  Files         120      120           
  Lines       13278    13284    +6     
  Branches     2254     2255    +1     
=======================================
+ Hits        11781    11787    +6     
  Misses        931      931           
  Partials      566      566           
Flag Coverage Δ
unittests 88.61% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

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