Skip to content

Python: keep Windows backslashes in LocalShellTool shell paths - #9858

Open
NingLin Liu (ninglinLiu) wants to merge 15 commits into
microsoft:mainfrom
ninglinLiu:fix/9819-win32-shell-path
Open

NingLin Liu (ninglinLiu) wants to merge 15 commits into
microsoft:mainfrom
ninglinLiu:fix/9819-win32-shell-path

Conversation

@ninglinLiu

Copy link
Copy Markdown
Contributor

Motivation & Context

LocalShellTool(shell=r"C:\Windows\...\powershell.exe") and unquoted AGENT_FRAMEWORK_SHELL paths fail on Windows with FileNotFoundError because resolve_shell used shlex.split, which treats \ as an escape and drops every backslash. The PowerShell -Command flag is also missed after the path is mangled.

Description & Review Guide

  • What are the major changes?
    • Add _split_shell_override: on win32, non-POSIX shlex that keeps backslashes and only honours "..." quotes; elsewhere keep shlex.split.
    • Use it for both the shell= argument and AGENT_FRAMEWORK_SHELL.
    • Tests cover unquoted Windows paths, env override, and quoted paths with spaces.
  • What is the impact of these changes? Windows shell overrides work; POSIX behaviour unchanged.
  • What do you want reviewers to focus on? That win32 splitting matches CommandLineToArgvW-style double-quote handling.

Related Issue

Fixes #9819

AI Assistance

  • No material AI assistance was used.
  • This is an AI-assisted contribution. I reviewed, understood, and verified all submitted content and accept responsibility for it.

AI assistance details: locating the shlex.split win32 escape behaviour, implementing _split_shell_override, and writing the regression tests and this PR description.

Contribution Checklist

  • The code builds clean without any errors or warnings
  • All unit tests pass, and I have added new tests where possible
  • The PR follows the Contribution Guidelines
  • This PR links to an agreed issue with no competing open PR, or the Related Issue section documents a trivial-change or repository-automation exception.
  • This is not a breaking change.

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.

Copilot AI 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.

🟡 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= and AGENT_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 == '"':

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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.

This branch was previously deployed

1 inactive deployment
github-app-auth — e3c7e906 Deployed Oct 30, 2026 by ninglinLiu via add_label #26749
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

python Usage: [Issues, PRs], Target: Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Python: [Bug]: LocalShellTool shell= / AGENT_FRAMEWORK_SHELL strips backslashes from Windows paths

3 participants