Repository: miurahr/aqtinstall

Issue:
ERROR   : 'NoneType' object has no attribute 'extend'
python3 -m aqt list-qt linux desktop --arch 6.4.0

ERROR   : 'NoneType' object has no attribute 'extend'
Traceback (most recent call last):
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 788, in show_list
    output = meta.getList()
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 436, in getList
    return self._action()
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 416, in <lambda>
    self._action = lambda: self.fetch_arches(self._to_version(architectures_ver))
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 443, in fetch_arches
    modules = self._fetch_module_metadata(self.archive_id.to_folder(qt_ver_str))
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 658, in _fetch_module_metadata
    xml = self.fetch_http(rest_of_url)
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 577, in fetch_http
    expected_hash = get_hash(rest_of_url, "sha256", timeout) if is_check_hash else None
  File "/home/user/.local/lib/python3.10/site-packages/aqt/helper.py", line 203, in get_hash
    raise ChecksumDownloadFailure(
aqt.exceptions.ChecksumDownloadFailure: Failed to download checksum for the file 'Updates.xml' from mirrors '['https://download.qt.io']

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/user/.local/lib/python3.10/site-packages/aqt/installer.py", line 108, in run
    args.func(args)
  File "/home/user/.local/lib/python3.10/site-packages/aqt/installer.py", line 524, in run_list_qt
    show_list(meta)
  File "/home/user/.local/lib/python3.10/site-packages/aqt/metadata.py", line 808, in show_list
    e.append_suggested_follow_up(suggested_follow_up(meta))
  File "/home/user/.local/lib/python3.10/site-packages/aqt/exceptions.py", line 44, in append_suggested_follow_up
    self.suggested_action.extend(suggestions)
AttributeError: 'NoneType' object has no attribute 'extend'
ERROR   : aqtinstall(aqt) v2.2.0 on Python 3.10.4 [CPython GCC 11.2.0]
Working dir: `/home/user`
Arguments: `['/home/user/.local/lib/python3.10/site-packages/aqt/__main__.py', 'list-qt', 'linux', 'desktop', '--arch', '6.4.0']` Host: `uname_result(system='Linux', node='laptop', release='5.15.0-43-generic', version='#46-Ubuntu SMP Tue Jul 12 10:30:17 UTC 2022', machine='x86_64')`
===========================PLEASE FILE A BUG REPORT===========================
You have discovered a bug in aqt.
Please file a bug report at https://github.com/miurahr/aqtinstall/issues.
Please remember to include a copy of this program's output in your report.


Discussion:

Patch:
diff --git a/aqt/exceptions.py b/aqt/exceptions.py
index d56fd4f..f76a6ad 100644
--- a/aqt/exceptions.py
+++ b/aqt/exceptions.py
@@ -18,15 +18,15 @@
 # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-from typing import List
+from typing import List, Optional
 
 DOCS_CONFIG = "https://aqtinstall.readthedocs.io/en/stable/configuration.html#configuration"
 
 
 class AqtException(Exception):
-    def __init__(self, *args, **kwargs):
-        self.suggested_action: List[str] = kwargs.pop("suggested_action", [])
-        self.should_show_help: bool = kwargs.pop("should_show_help", False)
+    def __init__(self, *args, suggested_action: Optional[List[str]] = None, should_show_help: bool = False, **kwargs):
+        self.suggested_action: List[str] = suggested_action or []
+        self.should_show_help: bool = should_show_help or False
         super(AqtException, self).__init__(*args, **kwargs)
 
     def __format__(self, format_spec) -> str:
@@ -53,16 +53,19 @@ class ArchiveChecksumError(ArchiveDownloadError):
 
 
 class ChecksumDownloadFailure(ArchiveDownloadError):
-    def __init__(self, *args, **kwargs):
-        kwargs["suggested_action"] = kwargs.pop("suggested_action", []).extend(
+    def __init__(self, *args, suggested_action: Optional[List[str]] = None, **kwargs):
+        if suggested_action is None:
+            suggested_action = []
+        suggested_action.extend(
             [
                 "Check your internet connection",
                 "Consider modifying `requests.max_retries_to_retrieve_hash` in settings.ini",
                 f"Consider modifying `mirrors.trusted_mirrors` in settings.ini (see {DOCS_CONFIG})",
             ]
         )
-        kwargs["should_show_help"] = True
-        super(ChecksumDownloadFailure, self).__init__(*args, **kwargs)
+        super(ChecksumDownloadFailure, self).__init__(
+            *args, suggested_action=suggested_action, should_show_help=True, **kwargs
+        )
 
 
 class ArchiveConnectionError(AqtException):


Test patch:
diff --git a/tests/test_list.py b/tests/test_list.py
index 8aba88d..2e96390 100644
--- a/tests/test_list.py
+++ b/tests/test_list.py
@@ -873,26 +873,55 @@ def test_show_list_empty(monkeypatch, capsys):
     assert format(error.value) == "No tools available for this request."
 
 
-def test_show_list_bad_connection(monkeypatch, capsys):
-    for exception_class, error_msg in (
-        (ArchiveConnectionError, "Failure to connect to some url"),
-        (ArchiveDownloadError, "Failure to download some xml file"),
-    ):
-
-        def mock(self):
-            raise exception_class(error_msg)
-
-        monkeypatch.setattr(MetadataFactory, "getList", mock)
-        meta = MetadataFactory(mac_wasm, spec=SimpleSpec("<5.9"))
-        with pytest.raises(exception_class) as error:
-            show_list(meta)
-        assert error.type == exception_class
-        assert format(error.value) == (
-            f"{error_msg}\n"
-            "==============================Suggested follow-up:==============================\n"
-            "* Please use 'aqt list-qt mac desktop --extensions <QT_VERSION>' to list valid extensions.\n"
-            "* Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."
-        )
+@pytest.mark.parametrize(
+    "exception_class, error_msg, source",
+    (
+        (ArchiveConnectionError, "Failure to connect to some url", "aqt.metadata.getUrl"),
+        (ArchiveDownloadError, "Failure to download some xml file", "aqt.metadata.getUrl"),
+    ),
+)
+def test_show_list_bad_connection(monkeypatch, capsys, exception_class, error_msg, source):
+    def mock(*args, **kwargs):
+        raise exception_class(error_msg)
+
+    monkeypatch.setattr(source, mock)
+    meta = MetadataFactory(mac_wasm, spec=SimpleSpec("<5.9"))
+    with pytest.raises(exception_class) as error:
+        show_list(meta)
+    assert error.type == exception_class
+    assert format(error.value) == (
+        f"{error_msg}\n"
+        "==============================Suggested follow-up:==============================\n"
+        "* Please use 'aqt list-qt mac desktop --extensions <QT_VERSION>' to list valid extensions.\n"
+        "* Please use 'aqt list-qt mac desktop' to check that versions of qt exist within the spec '<5.9'."
+    )
+
+
+@pytest.mark.parametrize(
+    "exception_class, error_msg, source", ((ArchiveConnectionError, "Failure to connect", "aqt.helper.getUrl"),)
+)
+def test_show_list_bad_connection_for_checksum(monkeypatch, capsys, exception_class, error_msg, source):
+    def mock(*args, **kwargs):
+        raise exception_class(error_msg)
+
+    monkeypatch.setattr(source, mock)
+
+    cli = Cli()
+    cli.run("list-qt linux desktop --arch 6.4.0".split())
+    out, err = capsys.readouterr()
+    sys.stdout.write(out)
+    sys.stderr.write(err)
+
+    assert (
+        err == "ERROR   : Failed to download checksum for the file 'Updates.xml' from mirrors "
+        "'['https://download.qt.io']\n"
+        "==============================Suggested follow-up:==============================\n"
+        "* Check your internet connection\n"
+        "* Consider modifying `requests.max_retries_to_retrieve_hash` in settings.ini\n"
+        "* Consider modifying `mirrors.trusted_mirrors` in settings.ini "
+        "(see https://aqtinstall.readthedocs.io/en/stable/configuration.html#configuration)\n"
+        "* Please use 'aqt list-qt linux desktop' to show versions of Qt available.\n"
+    )
 
 
 def fetch_expected_tooldata(json_filename: str) -> ToolData: