From eb93eabac90292d0aa124e6a69c75fc3e285678c Mon Sep 17 00:00:00 2001 From: Ahmed TAHRI Date: Thu, 4 Apr 2024 16:00:41 +0200 Subject: [PATCH] fix infinite loop update checker in a specific condition close issue https://github.com/httpie/cli/issues/1527 --- CHANGELOG.md | 1 + httpie/config.py | 4 ++++ httpie/internal/daemons.py | 4 ++-- httpie/internal/update_warnings.py | 28 +++++++++++++++++++++++----- tests/test_config.py | 15 +++++++++++++++ 5 files changed, 45 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78747e7e55..ef3b884a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ This project adheres to [Semantic Versioning](https://semver.org/). - Removed support for keeping the original casing of HTTP headers. This come from an outer constraint by newer protocols, namely HTTP/2+ that normalize header keys by default. From the HTTPie user perspective, they are "prettified" on the output by default. e.g. "x-hello-world" is displayed as "X-Hello-World". - Fixed multipart form data having filename not rfc2231 compliant when name contain non-ascii characters. ([#1401](https://github.com/httpie/cli/issues/1401)) +- Fixed issue where the configuration directory was not created at runtime that made the update fetcher run everytime. ([#1527](https://github.com/httpie/cli/issues/1527)) The plugins are expected to work without any changes. The only caveat would be that certain plugin explicitly require `requests`. Future contributions may be made in order to relax the constraints where applicable. diff --git a/httpie/config.py b/httpie/config.py index 5e6fc1245e..34ae72ca4c 100644 --- a/httpie/config.py +++ b/httpie/config.py @@ -143,6 +143,10 @@ class Config(BaseConfigDict): def __init__(self, directory: Union[str, Path] = DEFAULT_CONFIG_DIR): self.directory = Path(directory) super().__init__(path=self.directory / self.FILENAME) + # this one ensure we do not init HTTPie without the proper config directory + # there's an issue where the fetch_update daemon run without having the directory present. that induce a + # loop trying to fetch latest versions information. + self.ensure_directory() self.update(self.DEFAULTS) @property diff --git a/httpie/internal/daemons.py b/httpie/internal/daemons.py index 929f960ca0..4289ec5553 100644 --- a/httpie/internal/daemons.py +++ b/httpie/internal/daemons.py @@ -109,8 +109,8 @@ def _spawn(args: List[str], process_context: ProcessContext) -> None: _spawn_posix(args, process_context) -def spawn_daemon(task: str) -> None: - args = [task, '--daemon'] +def spawn_daemon(task: str, *args: str) -> None: + args = [task, '--daemon', *args] process_context = os.environ.copy() if not is_frozen: file_path = os.path.abspath(inspect.stack()[0][1]) diff --git a/httpie/internal/update_warnings.py b/httpie/internal/update_warnings.py index 5150d0c87f..fb4df97d6c 100644 --- a/httpie/internal/update_warnings.py +++ b/httpie/internal/update_warnings.py @@ -37,16 +37,34 @@ def _read_data_error_free(file: Path) -> Any: return {} -def _fetch_updates(env: Environment) -> str: +def _fetch_updates(env: Environment) -> None: file = env.config.version_info_file data = _read_data_error_free(file) - response = niquests.get(PACKAGE_INDEX_LINK) - response.raise_for_status() + try: + # HTTPie have a server that can return latest versions for various + # package channels, we shall attempt to retrieve this information once in a while + if hasattr(env.args, "verify"): + if env.args.verify.lower() in {"yes", "true", "no", "false"}: + verify = env.args.verify.lower() in {"yes", "true"} + else: + verify = env.args.verify + else: + verify = True + + response = niquests.get(PACKAGE_INDEX_LINK, verify=verify) + response.raise_for_status() + versions = response.json() + except (niquests.exceptions.ConnectionError, niquests.exceptions.HTTPError): + # in case of an error, let's ignore to avoid looping indefinitely. + # (spawn daemon background task maybe_fetch_update) + versions = { + BUILD_CHANNEL: httpie.__version__ + } data.setdefault('last_warned_date', None) data['last_fetched_date'] = datetime.now().isoformat() - data['last_released_versions'] = response.json() + data['last_released_versions'] = versions with open_with_lockfile(file, 'w') as stream: json.dump(data, stream) @@ -54,7 +72,7 @@ def _fetch_updates(env: Environment) -> str: def fetch_updates(env: Environment, lazy: bool = True): if lazy: - spawn_daemon('fetch_updates') + spawn_daemon('fetch_updates', f'--verify={env.args.verify}') else: _fetch_updates(env) diff --git a/tests/test_config.py b/tests/test_config.py index 1d2eea0750..d987b7eca9 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,3 +1,4 @@ +import os.path from pathlib import Path import pytest @@ -23,6 +24,20 @@ def test_default_options(httpbin): } +def test_config_dir_is_created(): + dir_path = str(get_default_config_dir()) + "--fake" + + try: + os.rmdir(dir_path) + except FileNotFoundError: + pass + + assert not os.path.exists(dir_path) + Config(dir_path) + assert os.path.exists(dir_path) + os.rmdir(dir_path) + + def test_config_file_not_valid(httpbin): env = MockEnvironment() env.create_temp_config_dir()