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.
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:
-
Removed
self.choices = self— without it,action.choicesstaysNone(argparse default), so Python 3.14’s registration-time check is skipped entirely. -
Moved validation into
__call__— when a user-provided value reaches the action handler at parse time, the code callsself.load()(which triggers the getter) and raisesArgumentErrorif 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
[1] httpie/cli [2] #1641 [3] https://github.com/httpie/cli/pull/1894
📖 Related Reads
- Hermes Tutorials — Hermes Agent setup, configuration, and advanced workflows
Cross-links automatically generated from CodeIntel Log.