diff --git a/lean/click.py b/lean/click.py index 0eb4e86d..5f42de81 100644 --- a/lean/click.py +++ b/lean/click.py @@ -274,6 +274,9 @@ def invoke(self, ctx: Context): update_manager = container.update_manager update_manager.show_announcements() + if ctx.params.get("update", False): + update_manager.force_refresh_modules_json() + result = super().invoke(ctx) update_manager.warn_if_cli_outdated() diff --git a/lean/components/util/update_manager.py b/lean/components/util/update_manager.py index e8411b38..ef6e6e80 100644 --- a/lean/components/util/update_manager.py +++ b/lean/components/util/update_manager.py @@ -162,6 +162,23 @@ def show_announcements(self) -> None: self._logger.info(Panel.fit(table, title="Announcements", box=box.SQUARE)) + def force_refresh_modules_json(self) -> None: + """Forces a re-download of the modules JSON file, bypassing the 24-hour cache interval.""" + from lean.models import url as modules_url, file_path as modules_file_path + from requests import get + try: + res = get(modules_url, timeout=5) + if res.ok: + from json import dump + modules_file_path.parent.mkdir(parents=True, exist_ok=True) + with open(modules_file_path, 'w', encoding='utf-8') as f: + dump(res.json(), f, ensure_ascii=False, indent=4) + else: + res.raise_for_status() + except Exception: + # No need to do anything if file isn't available + pass + def _should_check_for_updates(self, key: str, interval_hours: int) -> bool: """Returns whether an update check should be performed. diff --git a/tests/components/util/test_update_manager.py b/tests/components/util/test_update_manager.py index 4144c476..55d7a0f4 100644 --- a/tests/components/util/test_update_manager.py +++ b/tests/components/util/test_update_manager.py @@ -277,6 +277,31 @@ def test_show_announcements_does_nothing_when_latest_announcements_shown_before( logger.info.assert_not_called() +def test_force_refresh_modules_json_downloads_file(requests_mock: RequestsMock) -> None: + from lean.models import url as modules_url, file_path as modules_file_path + modules_json = {"modules": {"SomeModule": {"version": "1.0"}}} + requests_mock.add(requests_mock.GET, modules_url, json=modules_json) + + logger, storage, docker_manager, update_manager = create_objects() + + update_manager.force_refresh_modules_json() + + assert modules_file_path.is_file() + with open(modules_file_path) as f: + assert json.load(f) == modules_json + + +def test_force_refresh_modules_json_does_nothing_when_offline(requests_mock: RequestsMock) -> None: + from lean.models import url as modules_url + from requests.exceptions import ConnectionError as RequestsConnectionError + requests_mock.add(requests_mock.GET, modules_url, body=RequestsConnectionError()) + + logger, storage, docker_manager, update_manager = create_objects() + + # Should not raise + update_manager.force_refresh_modules_json() + + def test_show_announcements_does_nothing_when_there_are_no_announcements(requests_mock: RequestsMock) -> None: requests_mock.add(requests_mock.GET, "https://raw.githubusercontent.com/QuantConnect/lean-cli/master/announcements.json",