Repository: miurahr/aqtinstall

Issue:
Failed to install Qt 6.3.2 for Android
**Describe the bug**
Failed to install Qt 6.3.2 for Android

**To Reproduce**
Steps to reproduce the behavior:
1. aqt install-qt linux android 6.3.2 android -m all

**`aqt` output**
Add program output to help explain your problem.

```
aqt install-qt linux android 6.3.2 android -m all
INFO    : aqtinstall(aqt) v2.2.3 on Python 3.10.4 [CPython GCC 11.2.0]
WARNING : Specified Qt version is unknown: 6.3.2.
ERROR   : 'NoneType' object is not iterable
Traceback (most recent call last):
  File "/usr/local/lib/python3.10/dist-packages/aqt/installer.py", line 108, in run
    args.func(args)
  File "/usr/local/lib/python3.10/dist-packages/aqt/installer.py", line 312, in run_install_qt
    qt_archives = retry_on_bad_connection(
  File "/usr/local/lib/python3.10/dist-packages/aqt/helper.py", line 165, in retry_on_bad_connection
    return function(base_url)
  File "/usr/local/lib/python3.10/dist-packages/aqt/installer.py", line 313, in <lambda>
    lambda base_url: QtArchives(
  File "/usr/local/lib/python3.10/dist-packages/aqt/archives.py", line 292, in __init__
    self._get_archives()
  File "/usr/local/lib/python3.10/dist-packages/aqt/archives.py", line 359, in _get_archives
    self._get_archives_base(f"qt{self.version.major}_{self._version_str()}{self._arch_ext()}", self._target_packages())
  File "/usr/local/lib/python3.10/dist-packages/aqt/archives.py", line 383, in _get_archives_base
    self._parse_update_xml(os_target_folder, update_xml_text, target_packages)
  File "/usr/local/lib/python3.10/dist-packages/aqt/archives.py", line 404, in _parse_update_xml
    for archive in packageupdate.downloadable_archives:
TypeError: 'NoneType' object is not iterable
ERROR   : aqtinstall(aqt) v2.2.3 on Python 3.10.4 [CPython GCC 11.2.0]
Working dir: `/home/igor/Qt`
Arguments: `['/usr/local/bin/aqt', 'install-qt', 'linux', 'android', '6.3.2', 'android', '-m', 'all']` Host: `uname_result(system='Linux', node='gmi', release='5.15.0-47-generic', version='#51-Ubuntu SMP Thu Aug 11 07:51:15 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.
```

**Desktop (please complete the following information):**
 - OS: linux
 - `aqt` version aqtinstall(aqt) v2.2.3 on Python 3.10.4 [CPython GCC 11.2.0]


Discussion:
ddalcino: All Mac jobs failed due to the scheduled macOS-10.15 brownout. Looks like we need to do something about #541.

Patch:
diff --git a/aqt/archives.py b/aqt/archives.py
index eb694f8..4aadcbe 100644
--- a/aqt/archives.py
+++ b/aqt/archives.py
@@ -124,13 +124,21 @@ class PackageUpdate:
     description: str
     release_date: str
     full_version: str
-    dependencies: List[str]
-    auto_dependon: Optional[List[str]]
-    downloadable_archives: Optional[List[str]]
+    dependencies: Iterable[str]
+    auto_dependon: Iterable[str]
+    downloadable_archives: Iterable[str]
     default: bool
     virtual: bool
     base: str
 
+    def __post_init__(self):
+        for iter_of_str in self.dependencies, self.auto_dependon, self.downloadable_archives:
+            assert isinstance(iter_of_str, Iterable) and not isinstance(iter_of_str, str)
+        for _str in self.name, self.display_name, self.description, self.release_date, self.full_version, self.base:
+            assert isinstance(_str, str)
+        for boolean in self.default, self.virtual:
+            assert isinstance(boolean, bool)
+
     @property
     def version(self):
         return Version.permissive(self.full_version)
@@ -219,7 +227,7 @@ class Updates:
     def merge(self, other):
         self.package_updates.extend(other.package_updates)
 
-    def get_depends(self, target: str):
+    def get_depends(self, target: str) -> Iterable[str]:
         # initialize
         filo = [target]
         packages = []
@@ -237,19 +245,19 @@ class Updates:
                                 filo.append(depend)
         return packages
 
-    def _get_text(self, item):
+    def _get_text(self, item) -> str:
         if item is not None and item.text is not None:
             return item.text
         else:
             return ""
 
-    def _get_list(self, item):
+    def _get_list(self, item) -> Iterable[str]:
         if item is not None and item.text is not None:
             return ssplit(item.text)
         else:
-            return None
+            return []
 
-    def _get_boolean(self, item):
+    def _get_boolean(self, item) -> bool:
         if "true" == item:
             return True
         else:
@@ -426,7 +434,7 @@ class QtArchives:
         # if we have located every requested package, then target_packages will be empty
         if not self.all_extra and len(target_packages) > 0:
             message = f"The packages {target_packages} were not found while parsing XML of package information!"
-            raise NoPackageFound(message, suggested_action=self.help_msg(target_packages.get_modules()))
+            raise NoPackageFound(message, suggested_action=self.help_msg(list(target_packages.get_modules())))
 
     def _append_tool_update(self, os_target_folder, update_xml, target, tool_version_str):
         packageupdate = update_xml.get(target)
@@ -463,12 +471,13 @@ class QtArchives:
                 )
             )
 
-    def help_msg(self, missing_modules: Iterable[str]) -> Iterable[str]:
+    def help_msg(self, missing_modules: Optional[List[str]] = None) -> List[str]:
+        _missing_modules: List[str] = missing_modules or []
         base_cmd = f"aqt list-qt {self.os_name} {self.target}"
         arch = f"Please use '{base_cmd} --arch {self.version}' to show architectures available."
         mods = f"Please use '{base_cmd} --modules {self.version} <arch>' to show modules available."
-        has_base_pkg: bool = self._base_module_name() in missing_modules
-        has_non_base_pkg: bool = len(list(missing_modules)) > 1 or not has_base_pkg
+        has_base_pkg: bool = self._base_module_name() in _missing_modules
+        has_non_base_pkg: bool = len(list(_missing_modules)) > 1 or not has_base_pkg
         messages = []
         if has_base_pkg:
             messages.append(arch)
@@ -548,11 +557,12 @@ class SrcDocExamplesArchives(QtArchives):
         """
         return TargetConfig("src_doc_examples", self.target, self.arch, self.os_name)
 
-    def help_msg(self, missing_modules: Iterable[str]) -> Iterable[str]:
+    def help_msg(self, missing_modules: Optional[List[str]] = None) -> List[str]:
+        _missing_modules: List[str] = missing_modules or []
         cmd_type = "example" if self.flavor == "examples" else self.flavor
         base_cmd = f"aqt list-{cmd_type} {self.os_name} {self.version}"
         mods = f"Please use '{base_cmd} --modules' to show modules available."
-        has_non_base_pkg: bool = len(list(missing_modules)) > 1
+        has_non_base_pkg: bool = len(list(_missing_modules)) > 1
         messages = []
         if has_non_base_pkg:
             messages.append(mods)
@@ -605,7 +615,7 @@ class ToolArchives(QtArchives):
         update_xml = Updates.fromstring(self.base, update_xml_text)
         self._append_tool_update(os_target_folder, update_xml, self.arch, self.tool_version_str)
 
-    def help_msg(self, *args) -> Iterable[str]:
+    def help_msg(self, *args) -> List[str]:
         return [f"Please use 'aqt list-tool {self.os_name} {self.target} {self.tool_name}' to show tool variants available."]
 
     def get_target_config(self) -> TargetConfig:


Test patch:
diff --git a/tests/test_archives.py b/tests/test_archives.py
index 4e22eb2..66ef50f 100644
--- a/tests/test_archives.py
+++ b/tests/test_archives.py
@@ -346,6 +346,28 @@ qt_doc_5152_xml_weird_module = """
  </PackageUpdate>
 </Updates>
 """
+qt_doc_5152_xml_no_archives = """
+<Updates>
+ <PackageUpdate>
+  <Name>qt.qt5.5152.doc.qtcharts</Name>
+  <DisplayName>Documentation for Qt 5.15.2 GPLv3 components (QtCharts)</DisplayName>
+  <Description>Documentation for Qt 5.15.2 GPLv3 components (QtCharts)</Description>
+  <Version>5.15.2-0-202011130724</Version>
+  <ReleaseDate>2020-11-13</ReleaseDate>
+  <Dependencies>lots.of.stuff</Dependencies>
+  <DownloadableArchives/>
+ </PackageUpdate>
+ <PackageUpdate>
+  <Name>qt.qt5.5152.doc</Name>
+  <DisplayName>Qt 5.15.2 Documentation</DisplayName>
+  <Description>Qt 5.15.2 documentation</Description>
+  <Version>5.15.2-0-202011130724</Version>
+  <ReleaseDate>2020-11-13</ReleaseDate>
+  <Dependencies>lots.more.stuff</Dependencies>
+  <DownloadableArchives></DownloadableArchives>
+ </PackageUpdate>
+</Updates>
+"""
 qt_example_5152_xml_module = """
 <Updates>
 <PackageUpdate>
@@ -475,6 +497,7 @@ def make_example_archives(subarchives, modules, is_include_base) -> SrcDocExampl
         (["tqtc"], ["all"], False, qt_doc_5152_xml_weird_module, make_doc_archives, {"weird-qtcharts-docs.7z"}),
         (["tqtc"], ["qtcharts"], False, qt_doc_5152_xml_weird_module, make_doc_archives, {"weird-qtcharts-docs.7z"}),
         (["qtdoc"], ["qtcharts"], False, qt_example_5152_xml_module, make_example_archives, {"qtcharts-examples.7z"}),
+        ([], [], True, qt_doc_5152_xml_no_archives, make_doc_archives, set()),
     ),
 )
 def test_archives_weird_module_7z_name(