Repository navigation
Python: keep Windows backslashes in LocalShellTool shell paths - #9858
NingLin Liu (ninglinLiu) wants to merge 15 commits into
Conversation
Fixes microsoft#9819 resolve_shell used shlex.split on string overrides, which treats \\ as an escape on every platform and turns C:\\Windows\\...\\powershell.exe into a mangled path. On win32, split without escape processing and only honour double quotes so unquoted Windows paths (and AGENT_FRAMEWORK_SHELL) work.
There was a problem hiding this comment.
🟡 Changes recommended
Windows shell overrides mishandle partially quoted executable paths and quoted option values, while Windows-mocking tests fail on Linux/macOS because basename handling remains POSIX.
2 open issues
What changed in this PR
This PR addresses #9819 by preserving Windows backslashes in Python LocalShellTool string overrides while keeping POSIX parsing unchanged.
Changes:
- Adds platform-aware splitting for
shell=andAGENT_FRAMEWORK_SHELL. - Adds regression tests for unquoted Windows paths, environment overrides, and quoted paths containing spaces.
| File | Description |
|---|---|
python/packages/tools/tests/test_shell_resolve.py |
Adds Windows path and PowerShell flag regression tests. |
python/packages/tools/agent_framework_tools/shell/_resolve.py |
Introduces and applies platform-aware shell override parsing. |
Review effort: Balanced
Ruff escape-sequence-in-docstring failed Package Checks after the win32 shell-path fix; mark the docstring raw so ``\\`` examples are valid.
os.path.basename does not split on backslashes on Linux, so win32 path overrides in unit tests (and any cross-platform callers) were treated as non-PowerShell and got -c instead of -Command. Normalize separators before basename.
Adjacent quoted/unquoted path fragments and --flag="value with spaces" must stay one argv element; shlex(posix=False) was splitting them.
| while i < n: | ||
| c = value[i] | ||
| if in_quotes: | ||
| if c == '"': |
There was a problem hiding this comment.
Escaped quotes still close the quoted segment here. A valid Windows command line can contain \" inside a quoted startup script; treating that quote as a delimiter drops the literal quote and can split one argument into several. Please handle Windows backslash-before-quote rules, including odd/even backslash counts, or use a compatible parser, and add a regression with an escaped quote inside a quoted argument.
| if c == '"': | ||
| in_quotes = True | ||
| elif c in " ": | ||
| if buf: |
There was a problem hiding this comment.
This drops an explicitly quoted empty argument because buf is empty. For example, bash --rcfile "" loses the empty value, so the appended -c is consumed as the rcfile path and the command is misparsed. Please track whether an argument was started separately from buffer content, and cover empty quoted arguments before later options and at the end.

Motivation & Context
LocalShellTool(shell=r"C:\Windows\...\powershell.exe")and unquotedAGENT_FRAMEWORK_SHELLpaths fail on Windows withFileNotFoundErrorbecauseresolve_shellusedshlex.split, which treats\as an escape and drops every backslash. The PowerShell-Commandflag is also missed after the path is mangled.Description & Review Guide
_split_shell_override: on win32, non-POSIXshlexthat keeps backslashes and only honours"..."quotes; elsewhere keepshlex.split.shell=argument andAGENT_FRAMEWORK_SHELL.CommandLineToArgvW-style double-quote handling.Related Issue
Fixes #9819
AI Assistance
AI assistance details: locating the
shlex.splitwin32 escape behaviour, implementing_split_shell_override, and writing the regression tests and this PR description.Contribution Checklist