Skip to content

fix(parser): escape unescaped '%' in help text for Python 3.14 argpar…#299

Open
YangAn-microsoft wants to merge 1 commit intomicrosoft:devfrom
YangAn-microsoft:fix/argparse-unescaped-percent-help
Open

fix(parser): escape unescaped '%' in help text for Python 3.14 argpar…#299
YangAn-microsoft wants to merge 1 commit intomicrosoft:devfrom
YangAn-microsoft:fix/argparse-unescaped-percent-help

Conversation

@YangAn-microsoft
Copy link
Copy Markdown

@YangAn-microsoft YangAn-microsoft commented Apr 21, 2026

This pull request updates the argument parsing logic in knack/parser.py to ensure that help strings containing % characters are handled correctly in Python 3.14 and above. The main change is to escape % characters in help texts before passing them to argparse, then restore the original help text so that CLI users see the intended output.

Compatibility and Help String Handling:

  • Escapes % characters in help strings before passing them to argparse to avoid errors in Python 3.14+, and restores the original help text after argument creation so users see the correct help output. [1] [2]

Background

Knack currently allows command help texts to contain unescaped % (for example "%Y%m").Normally, this is fine because Knack prints help text directly and does not call argparse’s internal help rendering functions (such as _expand_help or print_help). However, starting from Python 3.14, argparse.ArgumentParser.add_argument() validates help text by attempting to format it. If the help text contains an unescaped %, this validation step raises an exception.

Problem

Simply escaping % in users' help text would change the rendered help output (for example "%Y%m" becoming "%%Y%%m"), which breaks existing behavior expected by Knack users.

Proposed Fix

Before calling argparse.add_argument(), temporarily escape % characters in the help text to satisfy argparse’s validation. After the argument is registered, restore the original help text.### Result- Keeps Knack’s existing rule that users can write % directly in help text- Avoids runtime exceptions introduced by Python 3.14’s stricter argparse validation- Preserves the help output exactly as written by command authors

@YangAn-microsoft
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree company="Microsoft"

Comment thread knack/parser.py
Comment on lines +84 to +89
# Restore the original unescaped help text so the CLI help renderer displays
# the correct single '%' characters to the user. Knack's help renderer reads
# action.help directly (not via argparse's _expand_help), so without this
# restore users would see '%%' literally in help output.
if help_string and '%' in help_string:
param.help = help_string
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.

It may not be a good solution to hack into an argparse.Action, as it may cause other unexpected side effects or failures as argparse actually expects % in the help message to be escaped.

Would it be possible to use help_string % params like argparse when printing the help message?

Copy link
Copy Markdown
Author

@YangAn-microsoft YangAn-microsoft Apr 21, 2026

Choose a reason for hiding this comment

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

Thanks for the feedback. I agree this area needs to be conservative.

I evaluated applying argparse-style help interpolation in Knack, but I believe that would be a broader behavior change, not just a percent-sign fix.

  1. It introduces a new contract for all command modules (both azure-cli and extensions): bare percent signs in help text become invalid unless escaped.
  2. It enables interpolation semantics such as %(default)s, %(prog)s, %(metavar)s, which can change current output and introduce new KeyError/TypeError failure modes for malformed templates.
  3. It would require coordinated updates across multiple teams. I scanned azure-cli-extensions and found existing bare-percent help text patterns that would need updates if Knack switched to argparse-style interpolation.
  • networkcloud: 6 hits (for example, 50%, percentage text)
  • databox: 4 hits (password character set includes %)
  • datamigration: 3 hits (150%, %LocalAppData%)
  • containerapp: 2 hits (maximum %, URL with %2C)
  • cosmosdb-preview: 1 hit ((cn=%s))
  • machinelearningservices: 1 hit (100%)
  • storagesync: 1 hit (50%)

On the argparse.Action concern specifically: the current fix is intentionally scoped to help text only. It does not modify parsing semantics such as type, nargs, choices, required, defaults, or validators. It only escapes percent signs to pass Python 3.14 add_argument validation, then restores original help text so displayed help remains unchanged.

Given that, I believe the current approach is the lowest-risk compatibility fix for Python 3.14 without forcing broad migrations and contract sync.

Copy link
Copy Markdown
Collaborator

@bebound bebound Apr 22, 2026

Choose a reason for hiding this comment

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

What's the cost of changing it to _expand_help? If %% is the right way, we should not restore to a deprecated method.

Knack's help renderer reads action.help directly (not via argparse's _expand_help)

Copy link
Copy Markdown
Author

@YangAn-microsoft YangAn-microsoft Apr 22, 2026

Choose a reason for hiding this comment

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

Thanks for the question. The cost of switching to _expand_help is ecosystem migration and rollout risk.

  • It changes the contract for help text: literal % must be escaped as %%.

  • That means existing help strings with unescaped % (in both azure-cli and extensions) need to be updated.

  • This cannot be treated as an isolated Knack change. It needs coordinated rollout with the help-text updates.

  • If rollout is not coordinated:

    • help text updated first, Knack unchanged: behavior can differ from current rendering expectations
    • Knack changed first, help text not updated: exceptions can occur from unescaped %

I agree extension owners may not need direct notification if the check is enforced by test/CI, because unescaped % should fail validation and be caught. But that still means this is a breaking contract change and requires planned migration, not a narrow compatibility fix.

That is why I treated the current patch as a compatibility fix and considered _expand_help migration a separate, broader change.

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.

As this PR is not changing how knack supports %-formatting (not supported). Some problems may occur:

  1. If the help string contains %% as argparse requested, it will be printed as %%, not %.
  2. If the help string contains %(...), it will not be %-formatted.

An alternative solution:

  1. If there is %( or %% in th help string, leave the string as is.
  2. If not, escape % to %%, like AWS CLI did (Add support for Python 3.14 aws/aws-cli#9790).

The bottom line is that knack should support %-formatting when printing the help message, thus not rejecting correct usages according to argparse.

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.

3 participants