Catch OSError(errno.ENOSPC) and PermissionError

When the destination drive is not writable or has insufficient space,
these exceptions are raised during `run_installer`. Unless they are
caught and dealt with, `aqtinstall` requests that the user file a bug
report. This change catches these errors and prints a nice error message
instead of requesting a bug report.
This commit is contained in:
Dave Dalcino
2023-03-13 17:47:09 -07:00
parent 702a0246ac
commit 8dd56ff461
3 changed files with 55 additions and 7 deletions

View File

@@ -102,3 +102,11 @@ class UpdaterError(AqtException):
class OutOfMemory(AqtException): class OutOfMemory(AqtException):
pass pass
class OutOfDiskSpace(AqtException):
pass
class DiskAccessNotPermitted(AqtException):
pass

View File

@@ -22,6 +22,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import argparse import argparse
import errno
import gc import gc
import multiprocessing import multiprocessing
import os import os
@@ -47,6 +48,8 @@ from aqt.exceptions import (
ArchiveListError, ArchiveListError,
CliInputError, CliInputError,
CliKeyboardInterrupt, CliKeyboardInterrupt,
DiskAccessNotPermitted,
OutOfDiskSpace,
OutOfMemory, OutOfMemory,
) )
from aqt.helper import ( from aqt.helper import (
@@ -1127,6 +1130,23 @@ def run_installer(archives: List[QtPackage], base_dir: str, sevenzip: Optional[s
pool.starmap(installer, tasks) pool.starmap(installer, tasks)
pool.close() pool.close()
pool.join() pool.join()
except PermissionError as e: # subclass of OSError
close_worker_pool_on_exception(e)
raise DiskAccessNotPermitted(
f"Failed to write to base directory at {base_dir}",
suggested_action=[
"Check that the destination is writable and does not already contain files owned by another user."
],
) from e
except OSError as e:
close_worker_pool_on_exception(e)
if e.errno == errno.ENOSPC:
raise OutOfDiskSpace(
"Insufficient disk space to complete installation.",
suggested_action=["Check available disk space.", "Check size requirements for installation."],
) from e
else:
raise
except KeyboardInterrupt as e: except KeyboardInterrupt as e:
close_worker_pool_on_exception(e) close_worker_pool_on_exception(e)
raise CliKeyboardInterrupt("Installer halted by keyboard interrupt.") from e raise CliKeyboardInterrupt("Installer halted by keyboard interrupt.") from e

View File

@@ -1,3 +1,4 @@
import errno
import hashlib import hashlib
import logging import logging
import os import os
@@ -1156,10 +1157,10 @@ def test_install_nonexistent_archives(monkeypatch, capsys, cmd, xml_file: Option
@pytest.mark.parametrize( @pytest.mark.parametrize(
"exception_class, settings_file, expect_end_msg, expect_return", "exception, settings_file, expect_end_msg, expect_return",
( (
( (
RuntimeError, RuntimeError(),
"../aqt/settings.ini", "../aqt/settings.ini",
"===========================PLEASE FILE A BUG REPORT===========================\n" "===========================PLEASE FILE A BUG REPORT===========================\n"
"You have discovered a bug in aqt.\n" "You have discovered a bug in aqt.\n"
@@ -1168,14 +1169,14 @@ def test_install_nonexistent_archives(monkeypatch, capsys, cmd, xml_file: Option
Cli.UNHANDLED_EXCEPTION_CODE, Cli.UNHANDLED_EXCEPTION_CODE,
), ),
( (
KeyboardInterrupt, KeyboardInterrupt(),
"../aqt/settings.ini", "../aqt/settings.ini",
"WARNING : Caught KeyboardInterrupt, terminating installer workers\n" "WARNING : Caught KeyboardInterrupt, terminating installer workers\n"
"ERROR : Installer halted by keyboard interrupt.", "ERROR : Installer halted by keyboard interrupt.",
1, 1,
), ),
( (
MemoryError, MemoryError(),
"../aqt/settings.ini", "../aqt/settings.ini",
"WARNING : Caught MemoryError, terminating installer workers\n" "WARNING : Caught MemoryError, terminating installer workers\n"
"ERROR : Out of memory when downloading and extracting archives in parallel.\n" "ERROR : Out of memory when downloading and extracting archives in parallel.\n"
@@ -1187,7 +1188,7 @@ def test_install_nonexistent_archives(monkeypatch, capsys, cmd, xml_file: Option
1, 1,
), ),
( (
MemoryError, MemoryError(),
"data/settings_no_concurrency.ini", "data/settings_no_concurrency.ini",
"WARNING : Caught MemoryError, terminating installer workers\n" "WARNING : Caught MemoryError, terminating installer workers\n"
"ERROR : Out of memory when downloading and extracting archives.\n" "ERROR : Out of memory when downloading and extracting archives.\n"
@@ -1197,11 +1198,30 @@ def test_install_nonexistent_archives(monkeypatch, capsys, cmd, xml_file: Option
"(see https://aqtinstall.readthedocs.io/en/latest/cli.html#cmdoption-list-tool-external)", "(see https://aqtinstall.readthedocs.io/en/latest/cli.html#cmdoption-list-tool-external)",
1, 1,
), ),
(
OSError(errno.ENOSPC, "No space left on device"),
"../aqt/settings.ini",
"WARNING : Caught OSError, terminating installer workers\n"
"ERROR : Insufficient disk space to complete installation.\n"
"==============================Suggested follow-up:==============================\n"
"* Check available disk space.\n"
"* Check size requirements for installation.",
1,
),
(
PermissionError(),
"../aqt/settings.ini",
"WARNING : Caught PermissionError, terminating installer workers\n"
f"ERROR : Failed to write to base directory at {os.getcwd()}\n"
"==============================Suggested follow-up:==============================\n"
"* Check that the destination is writable and does not already contain files owned by another user.",
1,
),
), ),
) )
def test_install_pool_exception(monkeypatch, capsys, exception_class, settings_file, expect_end_msg, expect_return): def test_install_pool_exception(monkeypatch, capsys, exception, settings_file, expect_end_msg, expect_return):
def mock_installer_func(*args): def mock_installer_func(*args):
raise exception_class() raise exception
host, target, ver, arch = "windows", "desktop", "6.1.0", "win64_mingw81" host, target, ver, arch = "windows", "desktop", "6.1.0", "win64_mingw81"
updates_url = "windows_x86/desktop/qt6_610/Updates.xml" updates_url = "windows_x86/desktop/qt6_610/Updates.xml"