Skip to content

feat(scripts): add {run_cwd} placeholder for cross-platform scripts - #3815

Merged
frostming merged 8 commits into
pdm-project:mainfrom
pctablet505:fix-3734-pdm-run-cwd-placeholder
Aug 11, 2026
Merged

feat(scripts): add {run_cwd} placeholder for cross-platform scripts#3815
frostming merged 8 commits into
pdm-project:mainfrom
pctablet505:fix-3734-pdm-run-cwd-placeholder

Conversation

@pctablet505

@pctablet505 pctablet505 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Fixes #3734

Adds a {PDM_RUN_CWD} placeholder for PDM scripts so callers can keep PDM-specific paths out of tools that cannot read environment variables. Works in cmd, shell, and composite scripts; paths with spaces are handled via shlex.quote. Includes tests and docs.

@pctablet505
pctablet505 marked this pull request as ready for review July 17, 2026 12:48

@yangfan-yf-yf yangfan-yf-yf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found a Windows regression in the placeholder substitution.

RE_CWD_PLACEHOLDER.subn(cwd, script) treats cwd as a regular-expression replacement template. A normal Windows working directory such as C:\Users\... contains \U, which re interprets as an invalid replacement escape and raises re.error: bad escape \U at position 2 before the command runs.

I reproduced this with the added coverage on Windows:

python -m pytest tests/cli/test_run.py -k cwd_placeholder -q
6 failed, 74 deselected

This affects both the shell and command modes, including the new spaces/composite cases. Please pass a callable replacement (for example lambda _: cwd) to subn, or otherwise escape the replacement value, so the path is inserted literally.

@yangfan-yf-yf yangfan-yf-yf left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for changing the replacement to a callable; that fixes the original \\U replacement-template failure.

There is still a Windows shell-quoting issue in the new for_shell branch. subprocess.list2cmdline() follows the Microsoft C runtime argument-parsing rules, but the result is passed to cmd.exe. For a working directory without whitespace such as C:\\repo&ver, subprocess.list2cmdline([path]) returns C:\\repo&ver without quotes. With cmd.exe /d /c echo C:\\repo&ver, & is then parsed as a command separator (the output includes both the path and the Windows version) rather than as part of the path.

Could the shell-specific interpolation escape or quote cmd.exe metacharacters even when the path has no spaces, and add a regression case for such a path? The existing spaced-path test does not cover this branch.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.33%. Comparing base (410a651) to head (360ed22).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3815   +/-   ##
=======================================
  Coverage   88.32%   88.33%           
=======================================
  Files         121      121           
  Lines       13213    13215    +2     
  Branches     2244     2246    +2     
=======================================
+ Hits        11671    11673    +2     
  Misses        971      971           
  Partials      571      571           
Flag Coverage Δ
unittests 88.21% <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.

@frostming

Copy link
Copy Markdown
Collaborator

@pctablet505 all other placeholders are lowercased, can you use lowercase, too?

@frostming frostming changed the title feat(scripts): add {PDM_RUN_CWD} placeholder for cross-platform scripts feat(scripts): add {pdm_run_cwd} placeholder for cross-platform scripts Aug 11, 2026
Adds a {PDM_RUN_CWD} placeholder that expands to the absolute path of the directory from which pdm run was invoked, matching the PDM_RUN_CWD environment variable. This lets shell and cmd scripts reference the cwd in a cross-platform way.

Fixes pdm-project#3734
- Add news/3734.feature.md describing the new placeholder.
- Close backtick in _interpolate_cwd docstring.
- Update interpolate docstring to mention {pdm} and {PDM_RUN_CWD}.
- Make _interpolate_cwd return whether substitution occurred so cmd-list
  parts are properly shlex.split after quoting paths containing spaces.
- Add tests for shell, cmd, and composite scripts with spaces in cwd.
…dows

Shell scripts are handed straight to the OS shell, which on Windows is
cmd.exe. cmd.exe does not treat single quotes as quoting syntax, so a
working directory containing spaces (e.g. C:\Users\Jane Doe\Project)
was passed to the invoked program garbled, defeating the whole point of
the placeholder on the platform it exists to help.

Quote the interpolated cwd per-consumer: for shell scripts on Windows
use subprocess.list2cmdline (cmd.exe-safe double quotes); keep POSIX
shlex.quote everywhere else, since cmd and composite parts are always
re-parsed with shlex.split regardless of platform.

Fixes pdm-project#3734
…e errors

Windows paths (e.g. C:\Users\...) contain sequences like \U that re.subn()
interprets as invalid replacement-string escapes when passed as a literal
string, raising re.error instead of substituting the placeholder.
subprocess.list2cmdline implements the MSVCRT argv-quoting convention
used by CreateProcess, not cmd.exe's own command-line grammar. Once the
quoted path reaches cmd.exe (which is what shell scripts run under on
Windows), that mismatch causes two separate problems:

- cmd.exe operators such as & are still parsed as command separators
  inside list2cmdline's double quotes, e.g. a cwd of C:\repo&ver turns
  `echo {PDM_RUN_CWD}` into two commands instead of one.
- Windows CI is currently red because cmd.exe builtins like echo don't
  strip surrounding quotes the way an argv-consuming program would, so
  even the existing spaces-in-path case prints the quotes literally.

Escape cmd.exe's own operator metacharacters (^ & | < > ( )) with
carets instead of quoting the whole path. This is the escaping cmd.exe
itself defines for its command-line parser, it composes correctly
regardless of whether the path also contains whitespace, and it leaves
plain paths completely unchanged since echo does not tokenize on
spaces.

Fixes pdm-project#3734
All other script placeholders (`{args}`, `{pdm}`) are lowercase; the
new one was uppercase, inconsistent with that convention. Renamed the
placeholder token only -- the PDM_RUN_CWD environment variable itself
stays uppercase, matching every other env var this project exposes
(PDM_PROJECT_ROOT, PDM_NO_CACHE, etc).
@frostming
frostming force-pushed the fix-3734-pdm-run-cwd-placeholder branch from d69d087 to 919362e Compare August 11, 2026 02:23
…s scripts

Signed-off-by: Frost Ming <me@frostming.com>
@frostming frostming changed the title feat(scripts): add {pdm_run_cwd} placeholder for cross-platform scripts feat(scripts): add {run_cwd} placeholder for cross-platform scripts Aug 11, 2026
@frostming

Copy link
Copy Markdown
Collaborator

FYI @pctablet505 I've jumped in and changed to the name to run_cwd

…ces_in_path to remove extra argument and assert correct output

Signed-off-by: Frost Ming <me@frostming.com>
@frostming
frostming merged commit d6d4b66 into pdm-project:main Aug 11, 2026
25 checks passed
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.

{PDM_RUN_CWD} placeholder in scripts?

3 participants