Browse Source

pyinstaller build fixes

master
SomberNight 2 years ago
parent
commit
6c213aca17
No known key found for this signature in database
GPG Key ID: B33B5F232C6271E9
  1. 3
      contrib/build-wine/build-electrum-git.sh
  2. 91
      contrib/build-wine/deterministic.spec
  3. 6
      contrib/build-wine/prepare-wine.sh
  4. 3
      contrib/osx/make_osx.sh
  5. 88
      contrib/osx/osx.spec
  6. 16
      electrum/gui/common_qt/__init__.py
  7. 8
      electrum/gui/common_qt/plugins.py
  8. 14
      electrum/gui/qml/__init__.py
  9. 12
      electrum/plugin.py
  10. 8
      electrum/plugins/trustedcoin/common_qt.py

3
contrib/build-wine/build-electrum-git.sh

@ -50,6 +50,9 @@ pushd $WINEPREFIX/drive_c/electrum
# see https://github.com/pypa/pip/issues/2195 -- pip makes a copy of the entire directory # see https://github.com/pypa/pip/issues/2195 -- pip makes a copy of the entire directory
info "Pip installing Electrum. This might take a long time if the project folder is large." info "Pip installing Electrum. This might take a long time if the project folder is large."
$WINE_PYTHON -m pip install --no-build-isolation --no-dependencies --no-warn-script-location . $WINE_PYTHON -m pip install --no-build-isolation --no-dependencies --no-warn-script-location .
# pyinstaller needs to be able to "import electrum", for which we need libsecp256k1:
# (or could try "pip install -e" instead)
cp electrum/libsecp256k1-*.dll "$WINEPREFIX/drive_c/python3/Lib/site-packages/electrum/"
popd popd

91
contrib/build-wine/deterministic.spec

@ -4,47 +4,40 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules, coll
import sys, os import sys, os
PYPKG="electrum"
MAIN_SCRIPT="run_electrum"
PROJECT_ROOT = "C:/electrum"
ICONS_FILE=f"{PROJECT_ROOT}/{PYPKG}/gui/icons/electrum.ico"
cmdline_name = os.environ.get("ELECTRUM_CMDLINE_NAME") cmdline_name = os.environ.get("ELECTRUM_CMDLINE_NAME")
if not cmdline_name: if not cmdline_name:
raise Exception('no name') raise Exception('no name')
home = 'C:\\electrum\\'
# see https://github.com/pyinstaller/pyinstaller/issues/2005 # see https://github.com/pyinstaller/pyinstaller/issues/2005
hiddenimports = [] hiddenimports = []
hiddenimports += collect_submodules('pkg_resources') # workaround for https://github.com/pypa/setuptools/issues/1963 hiddenimports += collect_submodules('pkg_resources') # workaround for https://github.com/pypa/setuptools/issues/1963
hiddenimports += collect_submodules('trezorlib') hiddenimports += collect_submodules(f"{PYPKG}.plugins")
hiddenimports += collect_submodules('safetlib')
hiddenimports += collect_submodules('btchip') # device plugin: ledger
hiddenimports += collect_submodules('ledger_bitcoin') # device plugin: ledger
hiddenimports += collect_submodules('keepkeylib')
hiddenimports += collect_submodules('websocket')
hiddenimports += collect_submodules('ckcc')
hiddenimports += collect_submodules('bitbox02')
hiddenimports += ['electrum.plugins.jade.jade']
hiddenimports += ['electrum.plugins.jade.jadepy.jade']
hiddenimports += ['PyQt5.QtPrintSupport'] # needed by Revealer
binaries = [] binaries = []
# Workaround for "Retro Look": # Workaround for "Retro Look":
binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]] binaries += [b for b in collect_dynamic_libs('PyQt5') if 'qwindowsvista' in b[0]]
# add libsecp256k1, libusb, etc:
binaries += [(f"{PROJECT_ROOT}/{PYPKG}/*.dll", '.')]
binaries += [('C:/tmp/libsecp256k1-2.dll', '.')]
binaries += [('C:/tmp/libusb-1.0.dll', '.')]
binaries += [('C:/tmp/libzbar-0.dll', '.')]
datas = [ datas = [
(home+'electrum/*.json', 'electrum'), (f"{PROJECT_ROOT}/{PYPKG}/*.json", PYPKG),
(home+'electrum/lnwire/*.csv', 'electrum/lnwire'), (f"{PROJECT_ROOT}/{PYPKG}/lnwire/*.csv", f"{PYPKG}/lnwire"),
(home+'electrum/wordlist/english.txt', 'electrum/wordlist'), (f"{PROJECT_ROOT}/{PYPKG}/wordlist/english.txt", f"{PYPKG}/wordlist"),
(home+'electrum/wordlist/slip39.txt', 'electrum/wordlist'), (f"{PROJECT_ROOT}/{PYPKG}/wordlist/slip39.txt", f"{PYPKG}/wordlist"),
(home+'electrum/locale', 'electrum/locale'), (f"{PROJECT_ROOT}/{PYPKG}/locale", f"{PYPKG}/locale"),
(home+'electrum/plugins', 'electrum/plugins'), (f"{PROJECT_ROOT}/{PYPKG}/plugins", f"{PYPKG}/plugins"),
(home+'electrum/gui/icons', 'electrum/gui/icons'), (f"{PROJECT_ROOT}/{PYPKG}/gui/icons", f"{PYPKG}/gui/icons"),
] ]
datas += collect_data_files('trezorlib') datas += collect_data_files(f"{PYPKG}.plugins")
datas += collect_data_files('trezorlib') # TODO is this needed? and same question for other hww libs
datas += collect_data_files('safetlib') datas += collect_data_files('safetlib')
datas += collect_data_files('btchip') datas += collect_data_files('btchip')
datas += collect_data_files('keepkeylib') datas += collect_data_files('keepkeylib')
@ -52,29 +45,19 @@ datas += collect_data_files('ckcc')
datas += collect_data_files('bitbox02') datas += collect_data_files('bitbox02')
# We don't put these files in to actually include them in the script but to make the Analysis method scan them for imports # We don't put these files in to actually include them in the script but to make the Analysis method scan them for imports
a = Analysis([home+'run_electrum', a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}",
home+'electrum/gui/qt/main_window.py', f"{PROJECT_ROOT}/{PYPKG}/gui/qt/main_window.py",
home+'electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py', f"{PROJECT_ROOT}/{PYPKG}/gui/qt/qrreader/qtmultimedia/camera_dialog.py",
home+'electrum/gui/text.py', f"{PROJECT_ROOT}/{PYPKG}/gui/text.py",
home+'electrum/util.py', f"{PROJECT_ROOT}/{PYPKG}/util.py",
home+'electrum/wallet.py', f"{PROJECT_ROOT}/{PYPKG}/wallet.py",
home+'electrum/simple_config.py', f"{PROJECT_ROOT}/{PYPKG}/simple_config.py",
home+'electrum/bitcoin.py', f"{PROJECT_ROOT}/{PYPKG}/bitcoin.py",
home+'electrum/dnssec.py', f"{PROJECT_ROOT}/{PYPKG}/dnssec.py",
home+'electrum/commands.py', f"{PROJECT_ROOT}/{PYPKG}/commands.py",
home+'electrum/plugins/cosigner_pool/qt.py',
home+'electrum/plugins/trezor/qt.py',
home+'electrum/plugins/safe_t/client.py',
home+'electrum/plugins/safe_t/qt.py',
home+'electrum/plugins/keepkey/qt.py',
home+'electrum/plugins/ledger/qt.py',
home+'electrum/plugins/coldcard/qt.py',
home+'electrum/plugins/jade/qt.py',
#home+'packages/requests/utils.py'
], ],
binaries=binaries, binaries=binaries,
datas=datas, datas=datas,
#pathex=[home+'lib', home+'gui', home+'plugins'],
hiddenimports=hiddenimports, hiddenimports=hiddenimports,
hookspath=[]) hookspath=[])
@ -125,11 +108,11 @@ exe_standalone = EXE(
a.scripts, a.scripts,
a.binaries, a.binaries,
a.datas, a.datas,
name=os.path.join('build\\pyi.win32\\electrum', cmdline_name + ".exe"), name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}.exe"),
debug=False, debug=False,
strip=None, strip=None,
upx=False, upx=False,
icon=home+'electrum/gui/icons/electrum.ico', icon=ICONS_FILE,
console=False) console=False)
# console=True makes an annoying black box pop up, but it does make Electrum output command line commands, with this turned off no output will be given but commands can still be used # console=True makes an annoying black box pop up, but it does make Electrum output command line commands, with this turned off no output will be given but commands can still be used
@ -138,11 +121,11 @@ exe_portable = EXE(
a.scripts, a.scripts,
a.binaries, a.binaries,
a.datas + [('is_portable', 'README.md', 'DATA')], a.datas + [('is_portable', 'README.md', 'DATA')],
name=os.path.join('build\\pyi.win32\\electrum', cmdline_name + "-portable.exe"), name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}-portable.exe"),
debug=False, debug=False,
strip=None, strip=None,
upx=False, upx=False,
icon=home+'electrum/gui/icons/electrum.ico', icon=ICONS_FILE,
console=False) console=False)
##### #####
@ -152,22 +135,22 @@ exe_inside_setup_noconsole = EXE(
pyz, pyz,
a.scripts, a.scripts,
exclude_binaries=True, exclude_binaries=True,
name=os.path.join('build\\pyi.win32\\electrum', cmdline_name), name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}.exe"),
debug=False, debug=False,
strip=None, strip=None,
upx=False, upx=False,
icon=home+'electrum/gui/icons/electrum.ico', icon=ICONS_FILE,
console=False) console=False)
exe_inside_setup_console = EXE( exe_inside_setup_console = EXE(
pyz, pyz,
a.scripts, a.scripts,
exclude_binaries=True, exclude_binaries=True,
name=os.path.join('build\\pyi.win32\\electrum', cmdline_name+"-debug"), name=os.path.join("build", "pyi.win32", PYPKG, f"{cmdline_name}-debug.exe"),
debug=False, debug=False,
strip=None, strip=None,
upx=False, upx=False,
icon=home+'electrum/gui/icons/electrum.ico', icon=ICONS_FILE,
console=True) console=True)
coll = COLLECT( coll = COLLECT(
@ -179,6 +162,6 @@ coll = COLLECT(
strip=None, strip=None,
upx=True, upx=True,
debug=False, debug=False,
icon=home+'electrum/gui/icons/electrum.ico', icon=ICONS_FILE,
console=False, console=False,
name=os.path.join('dist', 'electrum')) name=os.path.join('dist', PYPKG))

6
contrib/build-wine/prepare-wine.sh

@ -53,9 +53,9 @@ $WINE_PYTHON -m pip install --no-build-isolation --no-dependencies --no-binary :
# copy already built DLLs # copy already built DLLs
cp "$DLL_TARGET_DIR"/libsecp256k1-*.dll $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libsecp to its destination" cp "$DLL_TARGET_DIR"/libsecp256k1-*.dll $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libsecp to its destination"
cp "$DLL_TARGET_DIR/libzbar-0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libzbar to its destination" cp "$DLL_TARGET_DIR/libzbar-0.dll" $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libzbar to its destination"
cp "$DLL_TARGET_DIR/libusb-1.0.dll" $WINEPREFIX/drive_c/tmp/ || fail "Could not copy libusb to its destination" cp "$DLL_TARGET_DIR/libusb-1.0.dll" $WINEPREFIX/drive_c/electrum/electrum/ || fail "Could not copy libusb to its destination"
info "Building PyInstaller." info "Building PyInstaller."

3
contrib/osx/make_osx.sh

@ -223,6 +223,9 @@ python3 -m pip install --no-build-isolation --no-dependencies --no-binary :all:
info "Building $PACKAGE..." info "Building $PACKAGE..."
python3 -m pip install --no-build-isolation --no-dependencies \ python3 -m pip install --no-build-isolation --no-dependencies \
--no-warn-script-location . > /dev/null || fail "Could not build $PACKAGE" --no-warn-script-location . > /dev/null || fail "Could not build $PACKAGE"
# pyinstaller needs to be able to "import electrum", for which we need libsecp256k1:
# (or could try "pip install -e" instead)
cp "$PROJECT_ROOT/electrum"/libsecp256k1.*.dylib "$VENV_DIR/lib/python$PY_VER_MAJOR/site-packages/electrum/"
# strip debug symbols of some compiled libs # strip debug symbols of some compiled libs
# - hidapi (hid.cpython-39-darwin.so) in particular is not reproducible without this # - hidapi (hid.cpython-39-darwin.so) in particular is not reproducible without this

88
contrib/osx/osx.spec

@ -4,83 +4,67 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules, coll
import sys, os import sys, os
PACKAGE='Electrum' PACKAGE_NAME='Electrum.app'
PYPKG='electrum' PYPKG='electrum'
MAIN_SCRIPT='run_electrum' MAIN_SCRIPT='run_electrum'
ICONS_FILE=PYPKG + '/gui/icons/electrum.icns' PROJECT_ROOT = os.path.abspath(".")
ICONS_FILE=f"{PROJECT_ROOT}/{PYPKG}/gui/icons/electrum.icns"
VERSION = os.environ.get("ELECTRUM_VERSION") VERSION = os.environ.get("ELECTRUM_VERSION")
if not VERSION: if not VERSION:
raise Exception('no version') raise Exception('no version')
electrum = os.path.abspath(".") + "/"
block_cipher = None block_cipher = None
# see https://github.com/pyinstaller/pyinstaller/issues/2005 # see https://github.com/pyinstaller/pyinstaller/issues/2005
hiddenimports = [] hiddenimports = []
hiddenimports += collect_submodules('pkg_resources') # workaround for https://github.com/pypa/setuptools/issues/1963 hiddenimports += collect_submodules('pkg_resources') # workaround for https://github.com/pypa/setuptools/issues/1963
hiddenimports += collect_submodules('trezorlib') hiddenimports += collect_submodules(f"{PYPKG}.plugins")
hiddenimports += collect_submodules('safetlib')
hiddenimports += collect_submodules('btchip') # device plugin: ledger
hiddenimports += collect_submodules('ledger_bitcoin') # device plugin: ledger binaries = []
hiddenimports += collect_submodules('keepkeylib') # Workaround for "Retro Look":
hiddenimports += collect_submodules('websocket') binaries += [b for b in collect_dynamic_libs('PyQt5') if 'macstyle' in b[0]]
hiddenimports += collect_submodules('ckcc') # add libsecp256k1, libusb, etc:
hiddenimports += collect_submodules('bitbox02') binaries += [(f"{PROJECT_ROOT}/{PYPKG}/*.dylib", ".")]
hiddenimports += ['electrum.plugins.jade.jade']
hiddenimports += ['electrum.plugins.jade.jadepy.jade']
hiddenimports += ['PyQt5.QtPrintSupport'] # needed by Revealer
datas = [ datas = [
(electrum + PYPKG + '/*.json', PYPKG), (f"{PROJECT_ROOT}/{PYPKG}/*.json", PYPKG),
(electrum + PYPKG + '/lnwire/*.csv', PYPKG + '/lnwire'), (f"{PROJECT_ROOT}/{PYPKG}/lnwire/*.csv", f"{PYPKG}/lnwire"),
(electrum + PYPKG + '/wordlist/english.txt', PYPKG + '/wordlist'), (f"{PROJECT_ROOT}/{PYPKG}/wordlist/english.txt", f"{PYPKG}/wordlist"),
(electrum + PYPKG + '/wordlist/slip39.txt', PYPKG + '/wordlist'), (f"{PROJECT_ROOT}/{PYPKG}/wordlist/slip39.txt", f"{PYPKG}/wordlist"),
(electrum + PYPKG + '/locale', PYPKG + '/locale'), (f"{PROJECT_ROOT}/{PYPKG}/locale", f"{PYPKG}/locale"),
(electrum + PYPKG + '/plugins', PYPKG + '/plugins'), (f"{PROJECT_ROOT}/{PYPKG}/plugins", f"{PYPKG}/plugins"),
(electrum + PYPKG + '/gui/icons', PYPKG + '/gui/icons'), (f"{PROJECT_ROOT}/{PYPKG}/gui/icons", f"{PYPKG}/gui/icons"),
] ]
datas += collect_data_files('trezorlib') datas += collect_data_files(f"{PYPKG}.plugins")
datas += collect_data_files('trezorlib') # TODO is this needed? and same question for other hww libs
datas += collect_data_files('safetlib') datas += collect_data_files('safetlib')
datas += collect_data_files('btchip') datas += collect_data_files('btchip')
datas += collect_data_files('keepkeylib') datas += collect_data_files('keepkeylib')
datas += collect_data_files('ckcc') datas += collect_data_files('ckcc')
datas += collect_data_files('bitbox02') datas += collect_data_files('bitbox02')
# Add libusb so Trezor and Safe-T mini will work
binaries = [(electrum + "electrum/libusb-1.0.dylib", ".")]
binaries += [(electrum + "electrum/libsecp256k1.2.dylib", ".")]
binaries += [(electrum + "electrum/libzbar.0.dylib", ".")]
# Workaround for "Retro Look":
binaries += [b for b in collect_dynamic_libs('PyQt5') if 'macstyle' in b[0]]
# We don't put these files in to actually include them in the script but to make the Analysis method scan them for imports # We don't put these files in to actually include them in the script but to make the Analysis method scan them for imports
a = Analysis([electrum+ MAIN_SCRIPT, a = Analysis([f"{PROJECT_ROOT}/{MAIN_SCRIPT}",
electrum+'electrum/gui/qt/main_window.py', f"{PROJECT_ROOT}/{PYPKG}/gui/qt/main_window.py",
electrum+'electrum/gui/qt/qrreader/qtmultimedia/camera_dialog.py', f"{PROJECT_ROOT}/{PYPKG}/gui/qt/qrreader/qtmultimedia/camera_dialog.py",
electrum+'electrum/gui/text.py', f"{PROJECT_ROOT}/{PYPKG}/gui/text.py",
electrum+'electrum/util.py', f"{PROJECT_ROOT}/{PYPKG}/util.py",
electrum+'electrum/wallet.py', f"{PROJECT_ROOT}/{PYPKG}/wallet.py",
electrum+'electrum/simple_config.py', f"{PROJECT_ROOT}/{PYPKG}/simple_config.py",
electrum+'electrum/bitcoin.py', f"{PROJECT_ROOT}/{PYPKG}/bitcoin.py",
electrum+'electrum/dnssec.py', f"{PROJECT_ROOT}/{PYPKG}/dnssec.py",
electrum+'electrum/commands.py', f"{PROJECT_ROOT}/{PYPKG}/commands.py",
electrum+'electrum/plugins/cosigner_pool/qt.py',
electrum+'electrum/plugins/trezor/qt.py',
electrum+'electrum/plugins/safe_t/client.py',
electrum+'electrum/plugins/safe_t/qt.py',
electrum+'electrum/plugins/keepkey/qt.py',
electrum+'electrum/plugins/ledger/qt.py',
electrum+'electrum/plugins/coldcard/qt.py',
electrum+'electrum/plugins/jade/qt.py',
], ],
binaries=binaries, binaries=binaries,
datas=datas, datas=datas,
hiddenimports=hiddenimports, hiddenimports=hiddenimports,
hookspath=[]) hookspath=[])
# http://stackoverflow.com/questions/19055089/pyinstaller-onefile-warning-pyconfig-h-when-importing-scipy-or-scipy-signal # http://stackoverflow.com/questions/19055089/pyinstaller-onefile-warning-pyconfig-h-when-importing-scipy-or-scipy-signal
for d in a.datas: for d in a.datas:
if 'pyconfig' in d[0]: if 'pyconfig' in d[0]:
@ -106,7 +90,7 @@ exe = EXE(
debug=False, debug=False,
strip=False, strip=False,
upx=True, upx=True,
icon=electrum+ICONS_FILE, icon=ICONS_FILE,
console=False, console=False,
target_arch='x86_64', # TODO investigate building 'universal2' target_arch='x86_64', # TODO investigate building 'universal2'
) )
@ -116,9 +100,9 @@ app = BUNDLE(
a.binaries, a.binaries,
a.zipfiles, a.zipfiles,
a.datas, a.datas,
version = VERSION, version=VERSION,
name=PACKAGE + '.app', name=PACKAGE_NAME,
icon=electrum+ICONS_FILE, icon=ICONS_FILE,
bundle_identifier=None, bundle_identifier=None,
info_plist={ info_plist={
'NSHighResolutionCapable': 'True', 'NSHighResolutionCapable': 'True',

16
electrum/gui/common_qt/__init__.py

@ -0,0 +1,16 @@
# Copyright (C) 2023 The Electrum developers
# Distributed under the MIT software license, see the accompanying
# file LICENCE or http://www.opensource.org/licenses/mit-license.php
import sys
# FIXME: remove when both desktop and mobile are Qt6
def get_qt_major_version() -> int:
_GUI_QT_VERSION = getattr(sys, '_GUI_QT_VERSION', None)
if _GUI_QT_VERSION is None:
# used by pyinstaller when building (analysis phase)
_GUI_QT_VERSION = 5
if _GUI_QT_VERSION in (5, 6):
return _GUI_QT_VERSION
raise Exception(f"unexpected {_GUI_QT_VERSION=}")

8
electrum/gui/common_qt/plugins.py

@ -1,9 +1,13 @@
import sys import sys
if getattr(sys, '_GUI_QT_VERSION') == 5: # FIXME: remove when both desktop and mobile are Qt6 from . import get_qt_major_version
if (qt_ver := get_qt_major_version()) == 5:
from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject from PyQt5.QtCore import pyqtSignal, pyqtProperty, QObject
else: elif qt_ver == 6:
from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject from PyQt6.QtCore import pyqtSignal, pyqtProperty, QObject
else:
raise Exception(f"unexpected {qt_ver=}")
from electrum.logging import get_logger from electrum.logging import get_logger

14
electrum/gui/qml/__init__.py

@ -6,13 +6,19 @@ from typing import TYPE_CHECKING
try: try:
import PyQt6 import PyQt6
except Exception: except Exception as e:
sys.exit("Error: Could not import PyQt6. On Linux systems, you may try 'sudo apt-get install python3-pyqt6'") from electrum import GuiImportError
raise GuiImportError(
"Error: Could not import PyQt6. On Linux systems, "
"you may try 'sudo apt-get install python3-pyqt6'") from e
try: try:
import PyQt6.QtQml import PyQt6.QtQml
except Exception: except Exception as e:
sys.exit("Error: Could not import PyQt6.QtQml. On Linux systems, you may try 'sudo apt-get install python3-pyqt6.qtquick'") from electrum import GuiImportError
raise GuiImportError(
"Error: Could not import PyQt6.QtQml. On Linux systems, "
"you may try 'sudo apt-get install python3-pyqt6.qtquick'") from e
from PyQt6.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR) from PyQt6.QtCore import (Qt, QCoreApplication, QLocale, QTranslator, QTimer, QT_VERSION_STR, PYQT_VERSION_STR)
from PyQt6.QtGui import QGuiApplication from PyQt6.QtGui import QGuiApplication

12
electrum/plugin.py

@ -80,7 +80,13 @@ class Plugins(DaemonThread):
""" """
if cls._all_found_plugins is None: if cls._all_found_plugins is None:
cls._all_found_plugins = dict() cls._all_found_plugins = dict()
for loader, name, ispkg in pkgutil.iter_modules([cls.pkgpath]): iter_modules = list(pkgutil.iter_modules([cls.pkgpath]))
for loader, name, ispkg in iter_modules:
# FIXME pyinstaller binaries are packaging each built-in plugin twice:
# once as data and once as code. To honor the "no duplicates" rule below,
# we exclude the ones packaged as *code*, here:
if loader.__class__.__qualname__ == "FrozenImporter":
continue
full_name = f'electrum.plugins.{name}' full_name = f'electrum.plugins.{name}'
spec = importlib.util.find_spec(full_name) spec = importlib.util.find_spec(full_name)
if spec is None: # pkgutil found it but importlib can't ?! if spec is None: # pkgutil found it but importlib can't ?!
@ -94,7 +100,9 @@ class Plugins(DaemonThread):
except Exception as e: except Exception as e:
raise Exception(f"Error pre-loading {full_name}: {repr(e)}") from e raise Exception(f"Error pre-loading {full_name}: {repr(e)}") from e
d = module.__dict__ d = module.__dict__
assert name not in cls._all_found_plugins if name in cls._all_found_plugins:
_logger.info(f"Found the following plugin modules: {iter_modules=}")
raise Exception(f"duplicate plugins? for {name=}")
cls._all_found_plugins[name] = d cls._all_found_plugins[name] = d
return cls._all_found_plugins return cls._all_found_plugins

8
electrum/plugins/trustedcoin/common_qt.py

@ -4,10 +4,14 @@ import base64
import sys import sys
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
if getattr(sys, '_GUI_QT_VERSION') == 5: # FIXME: remove when both desktop and mobile are Qt6 from electrum.gui.common_qt import get_qt_major_version
if (qt_ver := get_qt_major_version()) == 5:
from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot from PyQt5.QtCore import pyqtSignal, pyqtProperty, pyqtSlot
else: elif qt_ver == 6:
from PyQt6.QtCore import pyqtSignal, pyqtProperty, pyqtSlot from PyQt6.QtCore import pyqtSignal, pyqtProperty, pyqtSlot
else:
raise Exception(f"unexpected {qt_ver=}")
from electrum.i18n import _ from electrum.i18n import _
from electrum.bip32 import BIP32Node from electrum.bip32 import BIP32Node

Loading…
Cancel
Save