Propagate exceptions from run_installer()

This change catches exceptions derived from Exception and
KeyboardInterrupt raised by `installer`, while run by multiple
processes, and propagates them back to earlier stack entries. This will
prevent any OSError and BrokenPipe exceptions that would otherwise be
raised when one process has an exception while the other processes are
still running.

This also handles the MemoryError exception we saw in #416, and offers
some suggestions for solving the issue.
This commit is contained in:
David Dalcino
2021-10-02 18:26:03 -07:00
parent 438e96233d
commit 776ab6181f
4 changed files with 296 additions and 20 deletions

View File

@@ -73,3 +73,7 @@ class ArchiveExtractionError(AqtException):
class UpdaterError(AqtException):
pass
class OutOfMemory(AqtException):
pass

View File

@@ -45,6 +45,7 @@ from aqt.exceptions import (
ArchiveListError,
CliInputError,
CliKeyboardInterrupt,
OutOfMemory,
)
from aqt.helper import MyQueueListener, Settings, downloadBinaryFile, getUrl, setup_logging
from aqt.metadata import ArchiveId, MetadataFactory, QtRepoProperty, SimpleSpec, Version, show_list, suggested_follow_up
@@ -814,7 +815,12 @@ class Cli:
def run_installer(archives: List[QtPackage], base_dir: str, sevenzip: Optional[str], keep: bool):
key_interrupt = False
def close_worker_pool_on_exception():
logger = getLogger("aqt.installer")
logger.warning(f"Caught {e.__class__.__name__}, terminating installer workers")
pool.terminate()
pool.join()
queue = multiprocessing.Manager().Queue(-1)
listener = MyQueueListener(queue)
listener.start()
@@ -828,17 +834,27 @@ def run_installer(archives: List[QtPackage], base_dir: str, sevenzip: Optional[s
pool.starmap(installer, tasks)
pool.close()
pool.join()
except KeyboardInterrupt:
logger = getLogger("aqt.installer")
logger.warning("Caught KeyboardInterrupt, terminating installer workers")
pool.terminate()
pool.join()
key_interrupt = True
# all done, close logging service for sub-processes
listener.enqueue_sentinel()
listener.stop()
if key_interrupt:
raise CliKeyboardInterrupt("Installer halted by keyboard interrupt.")
except KeyboardInterrupt as e:
close_worker_pool_on_exception()
raise CliKeyboardInterrupt("Installer halted by keyboard interrupt.") from e
except MemoryError as e:
close_worker_pool_on_exception()
if Settings.concurrency > 1:
docs_url = "https://aqtinstall.readthedocs.io/en/stable/configuration.html#configuration"
raise OutOfMemory(
"Out of memory when downloading and extracting archives in parallel.",
suggested_action=[f"Please reduce your 'concurrency' setting (see {docs_url})"],
) from e
raise OutOfMemory(
"Out of memory when downloading and extracting archives.", suggested_action=["Please free up more memory."]
)
except Exception as e:
close_worker_pool_on_exception()
raise e from e
finally:
# all done, close logging service for sub-processes
listener.enqueue_sentinel()
listener.stop()
def init_worker_sh():

View File

@@ -0,0 +1,222 @@
[DEFAULTS]
[aqt]
concurrency: 1
baseurl: https://download.qt.io
7zcmd: 7z
print_stacktrace_on_error: False
[requests]
connection_timeout: 3.5
response_timeout: 30
max_retries: 5
retry_backoff: 0.1
[mirrors]
blacklist:
http://mirrors.ocf.berkeley.edu
http://mirrors.tuna.tsinghua.edu.cn
http://mirrors.geekpie.club
fallbacks:
https://ftp.jaist.ac.jp/pub/qtproject
http://ftp1.nluug.nl/languages/qt
https://mirrors.dotsrc.org/qtproject
[kde_patches]
patches:
0001-toolchain.prf-Use-vswhere-to-obtain-VS-installation-.patch
0002-Fix-allocated-memory-of-QByteArray-returned-by-QIODe.patch
0003-Update-CLDR-to-v37-adding-Nigerian-Pidgin-as-a-new-l.patch
0004-QLayout-docs-explain-better-what-the-QWidget-ctor-ar.patch
0005-QMacStyle-fix-tab-rendering.patch
0006-QMacStyle-more-pixel-refinements.patch
0007-Deprecate-ordering-on-QItemSelectionRange.patch
0008-Deprecate-QLocale-Language-entries-that-no-locale-da.patch
0009-Deprecate-old-aliases-for-two-countries-and-several-.patch
0010-QAbstractItemModelTester-don-t-rely-on-hasChildren.patch
0011-Doc-Remove-mentioning-of-old-macos-versions-from-QSe.patch
0012-Pass-SameSite-through-QNetworkCookie.patch
0013-doc-fix-typo-consise-concise.patch
0014-Selftest-copy-XAUTHORITY-environment-variable.patch
0015-Fix-delay-first-time-a-font-is-used.patch
0016-Fix-QScreen-orientation-not-being-updated-when-setti.patch
0017-Android-Request-cursor-to-be-in-proper-position.patch
0018-testlib-Let-logger-report-whether-it-is-logging-to-s.patch
0019-Android-disable-Gradle-caching-by-default.patch
0020-Android-replace-stacktrace-with-debug-message-in-sea.patch
0021-Use-void-instead-of-Q_UNUSED.patch
0022-Don-t-show-QPushButton-as-hovered-unless-the-mouse-i.patch
0023-MinGW-Fix-assert-in-QCoreApplication-arguments-when-.patch
0024-QCombobox-propagate-the-palette-to-the-embedded-line.patch
0025-QMarginsF-document-that-isNull-operator-operator-are.patch
0026-qmake-vcxproj-Fix-handling-of-extra-compiler-outputs.patch
0027-Android-fix-documentation-about-ANDROID_EXTRA_LIBS.patch
0028-Offscreen-QPA-implement-a-native-interface.patch
0029-DropSite-example-support-markdown.patch
0030-Do-not-define-dynamic_cast.patch
0031-Android-fix-crash-by-passing-the-right-Handle-to-dls.patch
0032-testlib-Add-private-API-to-add-test-logger.patch
0033-Update-third-party-md4c-to-version-0.4.6.patch
0034-moc-Handle-include-in-enum-take-2.patch
0035-InputMethod-should-call-reset-function-when-proxywid.patch
0036-Add-possibility-to-set-QNX-Screen-pipeline-value.patch
0037-qglobal-Only-define-QT_ENSURE_STACK_ALIGNED_FOR_SSE-.patch
0038-macOS-FreeType-fix-crash-with-non-printable-unicode.patch
0039-Linux-fix-crash-in-AtSpi-adaptor-when-handling-windo.patch
0040-Add-_MSC_VER-check-to-MSVC-ARM-compiler-workaround.patch
0041-QMap-suppress-warning-about-strict-aliasing-violatio.patch
0042-Add-changes-file-for-Qt-5.15.2.patch
0043-Set-the-url-to-have-the-AtNx-filename-if-one-is-foun.patch
0044-QMap-don-t-tell-everyone-QMapNode-has-no-friends.patch
0045-QNAM-Work-around-QObject-finicky-orphan-cleanup-deta.patch
0046-xcb-ensure-that-available-glx-version-is-greater-tha.patch
0047-Protect-QImage-colorspace-transform-on-shutdown.patch
0048-Fix-qstylesheetstyle-clip-border-error.patch
0049-Correct-processEvents-documentation.patch
0050-Update-CLDR-to-v38.patch
0051-Fix-compilation-when-using-no-mimetype-database.patch
0052-Reduce-memory-reallocations-in-QTextTablePrivate-upd.patch
0053-Fix-regular-expression-initialize-with-incorrect-fil.patch
0054-Cocoa-Allow-CMD-H-to-hide-the-application-when-a-too.patch
0055-Fix-pcre2-feature-conditions.patch
0056-Q_PRIMITIVE_TYPE-improve-the-documentation.patch
0057-QAsn1Element-Read-value-in-blocks-to-avoid-oom-at-wr.patch
0058-Android-Don-t-use-putIfAbsent-as-that-is-not-availab.patch
0059-QMutex-order-reads-from-QMutexPrivate-waiters-and-QB.patch
0060-Android-Add-the-QtAndroidBearer.jar-to-the-jar-depen.patch
0061-Android-Add-the-required-linker-flags-for-unwinding-.patch
0062-Android-recommend-against-using-ANDROID_ABIS-inside-.patch
0063-Android-fix-android-java-and-templates-targets-with-.patch
0064-QCharRef-properly-disable-assignment-from-char.patch
0065-Android-Treat-ACTION_CANCEL-as-TouchPointReleased.patch
0066-Fix-misidentification-of-some-shearing-QTransforms-a.patch
0067-Fix-QGraphicsItem-crash-if-click-right-button-of-mou.patch
0068-Bump-version.patch
0069-Android-Ensure-windows-always-have-a-geometry-on-cre.patch
0070-macOS-Account-for-Big-Sur-always-enabling-layer-back.patch
0071-Fix-shaping-problems-on-iOS-14-macOS-11.patch
0072-Link-to-qAlpha-in-qRgb-and-qRgba-docs.patch
0073-HTTP2-fix-crash-from-assertion.patch
0074-Fuzzing-Add-a-test-for-QDateTime-fromString.patch
0075-QSocks5SocketEngine-Fix-out-of-bounds-access-of-QBA.patch
0076-Use-QTRY_COMPARE-in-an-attempt-to-make-the-test-less.patch
0077-Doc-Document-QGradient-Preset-enum-values.patch
0078-Doc-Fix-documentation-warnings-for-Qt-XML.patch
0079-Doc-Fix-documentation-warnings-in-Qt-Network.patch
0080-Ensure-that-QMenu-is-polished-before-setting-the-scr.patch
0081-widgets-Don-t-report-new-focus-object-during-clearFo.patch
0082-QDtls-remove-redundant-RAII-struct.patch
0083-macOS-Propagate-device-pixel-ratio-of-system-tray-ic.patch
0084-tst_qocsp-improve-code-coverage.patch
0085-Doc-explain-how-to-create-a-test-touch-device-for-us.patch
0086-macOS-Upgrade-supported-SDK-to-11.0.patch
0087-Fix-logic-error-in-QString-replace-ch-after-cs.patch
0088-Be-more-consistent-when-converting-JSON-values-from-.patch
0089-QCoreApplication-add-more-information-to-processEven.patch
0090-Fix-QSFPM-not-emitting-dataChanged-when-source-model.patch
0091-Android-Fix-android-accessibility-not-being-set-acti.patch
0092-Fix-x-height-name-in-stylesheet-docs.patch
0093-QMutex-Work-around-ICC-bug-in-dealing-with-constexpr.patch
0094-wasm-fix-resizing-of-qwidget-windows.patch
0095-Avoid-integer-overflow-and-division-by-zero.patch
0096-QPasswordDigestor-improve-code-coverage.patch
0097-QStackedLayout-fix-a-memory-leak.patch
0098-Limit-value-in-setFontWeightFromValue.patch
0099-Doc-Fix-documentation-of-qmake-s-exists-function.patch
0100-QVLA-do-not-include-QtTest.patch
0101-Clean-up-docs-of-QCalendar-related-QLocale-toString-.patch
0102-Change-android-target-SDK-version-to-29.patch
0103-QVLA-always-use-new-to-create-new-objects.patch
0104-QPushButton-fix-support-of-style-sheet-rule-for-text.patch
0105-Limit-pen-width-to-maximal-32767.patch
0106-Doc-Consistently-use-book-style-capitalization-for-Q.patch
0107-qstring.h-fix-warnings-about-shortening-qsizetype-to.patch
0108-Fix-invalid-QSortFilterProxyModel-dataChanged-parame.patch
0109-Minor-refactor-of-installMetaFile.patch
0110-Return-a-more-useful-date-time-on-parser-failure-in-.patch
0111-QCalendar-increase-coverage-by-tests.patch
0112-Bounds-check-time-zone-offsets-when-parsing.patch
0113-Network-self-test-make-it-work-with-docker-container.patch
0114-QSslConfiguration-improve-code-coverage.patch
0115-Add-new-way-to-mess-up-projects-with-QMAKE_INSTALL_R.patch
0116-Install-3rd-party-headers-and-meta-for-static-builds.patch
0117-Create-qtlibjpeg-for-jpeg-image-plugin.patch
0118-QStandardPaths-Don-t-change-permissions-of-XDG_RUNTI.patch
0119-tst_QSslCertificate-improve-code-coverage.patch
0120-Let-QXcbConnection-getTimestamp-properly-exit-when-X.patch
0121-QDtls-cookie-verifier-make-sure-a-server-can-re-use-.patch
0122-QMacStyle-remove-vertical-adjustment-for-inactive-ta.patch
0123-Revert-xcb-add-xcb-util-dependency-for-xcb-image.patch
0124-Containers-call-constructors-even-for-primitive-type.patch
0125-Android-print-tailored-warning-if-qml-dependency-pat.patch
0126-Cosmetic-stroker-avoid-overflows-for-non-finite-coor.patch
0127-QSslCipher-improve-its-code-coverage-and-auto-tests.patch
0128-tst_qsslkey-handle-QT_NO_SSL-properly.patch
0129-Add-the-Qt-6.0-deprecation-macros.patch
0130-wasm-fix-mouse-double-click.patch
0131-Android-avoid-reflection-with-ClipData-addItem.patch
0132-QHeaderView-fix-spurious-sorting.patch
0133-Fix-exception-with-Android-5.x.patch
0134-Http2-Remove-errored-out-requests-from-queue.patch
0135-Http2-don-t-call-ensureConnection-when-there-s-no-re.patch
0136-Fix-QTranslator-load-search-order-not-following-uiLa.patch
0137-Avoid-signed-overflow-in-moc.patch
0138-Android-Kill-calls-to-deprecated-func-in-API-29.patch
0139-Doc-Improve-_CAST_FROM_ASCII-documentation.patch
0140-Improve-documented-function-argument-names.patch
0141-Fix-QImage-setPixelColor-on-RGBA64_Premultiplied.patch
0142-macOS-Make-sure-that-the-reserved-characters-are-not.patch
0143-Enable-testing-for-whether-a-calendar-registered-its.patch
0144-tests-add-a-shortcut-to-quit-app-in-allcursors.patch
0145-QSslSocket-Don-t-call-transmit-in-unencrypted-mode.patch
0146-QStringView-operator-operator-operator-currently-Qt6.patch
0147-QCborStreamReader-move-the-readStringChunk-code-to-t.patch
0148-Improve-the-documentation-for-QElapsedTimer-restart-.patch
0149-QSslSocket-verify-do-not-alter-the-default-configura.patch
0150-Fix-tst_QFontDatabase-aliases-failure-with-ambiguous.patch
0151-QStyleAnimation-make-sure-the-last-frame-of-animatio.patch
0152-PCRE-update-to-10.36.patch
0153-tst_QCborValue-adjust-the-size-of-the-minimum-string.patch
0154-macOS-Always-allow-interacting-with-popup-windows-du.patch
0155-macOS-Add-missing-QT_MANGLE_NAMESPACE.patch
0156-QSplashScreen-draw-pixmap-with-SmoothTransfrom.patch
0157-Android-Qml-accessibility-fixes.patch
0158-Http2-set-the-reply-s-error-code-and-string-on-error.patch
0159-Try-again-to-fix-Clang-s-Wconstant-logical-operand-w.patch
0160-Revert-Android-print-tailored-warning-if-qml-depende.patch
0161-QUrl-fix-parsing-of-empty-IPv6-addresses.patch
0162-tst_QSslError-improve-the-code-coverage-as-pointed-a.patch
0163-macOS-Disable-WA_QuitOnClose-on-menu-item-widget-con.patch
0164-QString-fix-count-QRegularExpression.patch
0165-QString-lastIndexOf-fix-off-by-one-for-zero-length-m.patch
0166-secureudpclient-a-speculative-fix-for-non-reproducib.patch
0167-Android-don-t-use-avx-and-avx2-when-building-for-And.patch
0168-Fuzzing-Provide-link-to-oss-fuzz.patch
0169-Blacklist-tst_QMdiArea-updateScrollBars-on-macos.patch
0170-Fix-build-with-GCC-11-include-limits.patch
0171-Build-fixes-for-GCC-11.patch
0172-Partially-revert-813a928c7c3cf98670b6043149880ed5c95.patch
0173-Fix-removing-columns-when-QSortFilterProxyModel-has-.patch
0174-Fix-get-out-of-bounds-index-in-QSortFilterProxyModel.patch
0175-Fix-handling-of-surrogates-in-QBidiAlgorithm.patch
0176-Avoid-undefined-color-values-in-corrupt-xpm-image.patch
0177-Gracefully-reject-requests-for-absurd-font-sizes.patch
0178-Don-t-own-unique-name-for-QDBusTrayIcon.patch
0179-QAbstractItemModelTester-fix-false-positive-when-mod.patch
0180-Fix-QAbstractItemModelTester-false-positive.patch
0181-Deprecate-QMutex-in-recursive-mode.patch
0182-Fix-QAbstractItemModelTester-false-positive.patch
0183-Fix-crash-on-serializing-default-constructed-QTimeZo.patch
0184-Fix-QTreeModel-calling-beginRemoveRows-twice.patch
0185-QConcatenateTablesProxyModel-skip-dataChanged-in-hid.patch
0186-QComboBox-fix-select-all-columns-in-the-view.patch
0187-QTableView-honor-spans-when-calculating-height-width.patch
0188-TableView-Trigger-the-resizing-of-editors-resizing-a.patch
0189-Fix-no-mapping-for-SysReq-key.patch
0190-qdbus-add-support-for-aay-QByteArrayList.patch
0191-QRandom-drop-a-usage-of-std-is_literal_type.patch
0192-fix-Optimize-the-performance-of-the-inotify-file-sys.patch
0193-Remove-the-unnecessary-template-parameter-from-the-c.patch
0194-Fix-memory-leak-when-using-small-caps-font.patch
0195-Make-sure-_q_printerChanged-is-called-even-if-only-p.patch
0196-fix-Alt-shortcut-on-non-US-layouts.patch

View File

@@ -16,6 +16,7 @@ from pytest_socket import disable_socket
from aqt.archives import QtPackage
from aqt.exceptions import ArchiveDownloadError, ArchiveExtractionError
from aqt.helper import Settings
from aqt.installer import Cli, installer
@@ -536,9 +537,44 @@ def test_install_nonexistent_archives(monkeypatch, capsys, cmd, xml_file: Option
assert actual == expected, "{0} != {1}".format(actual, expected)
def test_install_keyboard_interrupt(monkeypatch, capsys):
def mock_keyboard_interrupt(*args):
raise KeyboardInterrupt()
@pytest.mark.parametrize(
"make_exception, expect_end_msg, settings_file",
(
(
"RuntimeError()",
"===========================PLEASE FILE A BUG REPORT===========================\n"
"You have discovered a bug in aqt.\n"
"Please file a bug report at https://github.com/miurahr/aqtinstall/issues.\n"
"Please remember to include a copy of this program's output in your report.",
"../aqt/settings.ini",
),
(
"KeyboardInterrupt()",
"Caught KeyboardInterrupt, terminating installer workers\n" "Installer halted by keyboard interrupt.",
"../aqt/settings.ini",
),
(
"MemoryError()",
"Caught MemoryError, terminating installer workers\n"
"Out of memory when downloading and extracting archives in parallel.\n"
"==============================Suggested follow-up:==============================\n"
"* Please reduce your 'concurrency' setting (see "
"https://aqtinstall.readthedocs.io/en/stable/configuration.html#configuration)",
"../aqt/settings.ini",
),
(
"MemoryError()",
"Caught MemoryError, terminating installer workers\n"
"Out of memory when downloading and extracting archives.\n"
"==============================Suggested follow-up:==============================\n"
"* Please free up more memory.",
"data/settings_no_concurrency.ini",
),
),
)
def test_install_pool_exception(monkeypatch, capsys, make_exception, expect_end_msg, settings_file):
def mock_installer_func(*args):
raise eval(make_exception)
host, target, ver, arch = "windows", "desktop", "6.1.0", "win64_mingw81"
updates_url = "windows_x86/desktop/qt6_610/Updates.xml"
@@ -548,15 +584,13 @@ def test_install_keyboard_interrupt(monkeypatch, capsys):
mock_get_url, mock_download_archive = make_mock_geturl_download_archive(archives, arch, host, updates_url)
monkeypatch.setattr("aqt.archives.getUrl", mock_get_url)
monkeypatch.setattr("aqt.installer.getUrl", mock_get_url)
monkeypatch.setattr("aqt.installer.installer", mock_keyboard_interrupt)
monkeypatch.setattr("aqt.installer.installer", mock_installer_func)
Settings.load_settings(str(Path(__file__).parent / settings_file))
cli = Cli()
cli._setup_settings()
assert cli.run(cmd) == 1
out, err = capsys.readouterr()
assert err.rstrip().endswith(
"Caught KeyboardInterrupt, terminating installer workers\nInstaller halted by keyboard interrupt."
)
assert err.rstrip().endswith(expect_end_msg)
def test_install_installer_archive_extraction_err(monkeypatch):