Code verified

Fix: test_lazy_choices_help fails on Python 3.14

Fixed httpie/cli#1641 — 6 line bug-fix addressing Python 3.14 argparse validation changes.

Contents

    The Bug

    Repo: httpie/cli Issue: #1641 Status: PR-submitted PR: https://github.com/httpie/cli/pull/1894

    Description: test_lazy_choices_help fails on Python 3.14 because argparse now validates that default is in choices at argument registration time — during add_argument() itself.

    Fix scope: 6 lines changed across httpie/cli/...

    Root Cause

    On Python 3.14+, argparse validates that the default value belongs to the set of choices at argument registration time (inside add_argument()). The LazyChoices class was setting self.choices = self, which caused argparse to call __contains__ → load() → getter() during parser construction — triggering the getter prematurely.

    This broke the core invariant of test_lazy_choices_help: the getter must NOT be called during parser initialization. The bug is subtle because it’s triggered by a behavioral change in the Python standard library, not by a code change in httpie itself. Users on Python 3.13 and earlier saw no problem, while anyone running Python 3.14.0b3+ would encounter the test failure.

    Impact: Although this is a test failure rather than a runtime crash for end users, it blocks CI/CD pipelines on Python 3.14 environments. Anyone running httpie on Python 3.14 from source or contributing to the project would see failing tests until this is addressed. The underlying behavior — a getter being called at parser registration time — could also cause side effects in production if the getter had observable side effects.

    The Fix

    The fix makes two simple changes:

    1. Removed self.choices = self — without it, action.choices stays None (argparse default), so Python 3.14’s registration-time check is skipped entirely.

    2. Moved validation into __call__ — when a user-provided value reaches the action handler at parse time, the code calls self.load() (which triggers the getter) and raises ArgumentError if the value isn’t valid.

    The lazy-loading contract is preserved: the getter is only called when a value is actually parsed or help is displayed, never during parser construction.

    # Before: self.choices = self triggered getter during add_argument()
    # After: choices stays None; validation deferred to __call__

    Pattern & Takeaways

    Pattern: Python 3.x behavioral changes in the standard library can break assumptions that worked across many versions. The LazyChoices pattern assumed that add_argument() never validates choices eagerly — an assumption that held across Python 3.0 through 3.13. This is a reminder that no CPython behavior is frozen forever.

    Key insight: When a test fails only on a new Python version, the most likely cause is a behavioral change in the stdlib, not a regression in your own code. The minimal-change approach — deferring validation to parse time rather than fighting argparse’s new behavior — works with the framework rather than against it.

    Transfer Potential

    High — the pattern of lazy argument choices is common in CLI tools built on argparse. As Python 3.14 adoption grows, any project using lazy-loaded choices or custom argparse actions that interact with the choices set during construction will need similar fixes. The same approach (deferring validation to __call__) applies broadly.


    Auto-generated from PR #1894. View all patches on GitHub.

    References

    • Hermes Tutorials — Hermes Agent setup, configuration, and advanced workflows

    Cross-links automatically generated from CodeIntel Log.