From a23066443c73940c33aeb4b6ffd939f51ed17d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E9=91=AB=E4=BA=BF?= <98445030+zhaoxinyi02@users.noreply.github.com> Date: Thu, 13 Aug 2026 02:56:54 +0800 Subject: [PATCH] fix(cli): avoid triggering lazy getter during argparse validation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.14 added _check_help to ArgumentParser.add_argument (see python/cpython#124899). It visits action.help during argument registration, which made LazyChoices.help trigger the lazy getter()/help_formatter() immediately — violating the lazy contract and breaking test_lazy_choices_help on Python 3.14. Detect the validation phase by looking for _check_help on the call stack and skip the lazy computation there; the help string is still lazily computed when argparse renders --help. Fixes #1641 Signed-off-by: 赵鑫亿 <98445030+zhaoxinyi02@users.noreply.github.com> --- httpie/cli/utils.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/httpie/cli/utils.py b/httpie/cli/utils.py index ad27da37f7..ba0ac40427 100644 --- a/httpie/cli/utils.py +++ b/httpie/cli/utils.py @@ -1,4 +1,5 @@ import argparse +import sys from typing import Any, Callable, Generic, Iterator, Iterable, Optional, TypeVar T = TypeVar('T') @@ -54,12 +55,26 @@ def load(self) -> T: return self._obj @property - def help(self) -> str: + def help(self) -> Optional[str]: + # Python 3.14's argparse calls ``_check_help`` during + # ``add_argument``, which accesses ``action.help``. We must not + # trigger the lazy getter at that point. Detect the validation + # phase by looking for ``_check_help`` on the call stack; in any + # other context (e.g. rendering ``--help``) we lazily compute the + # help string via ``help_formatter``. if self._help is None and self.help_formatter is not None: - self._help = self.help_formatter( - self.load(), - isolation_mode=self.isolation_mode - ) + frame = sys._getframe(1) + in_check_help = False + while frame is not None: + if frame.f_code.co_name == "_check_help": + in_check_help = True + break + frame = frame.f_back + if not in_check_help: + self._help = self.help_formatter( + self.load(), + isolation_mode=self.isolation_mode + ) return self._help @help.setter