70 lines
2.8 KiB
Python
70 lines
2.8 KiB
Python
"""
|
|
bal.cmdline
|
|
===========
|
|
|
|
Compatibility shim for Electrum's plugin loader (command-line front-end).
|
|
|
|
Electrum loads a plugin with ``gui_name='cmdline'`` by importing the
|
|
``cmdline`` module of the plugin package and looking for a ``Plugin`` class.
|
|
The real implementation lives in the ``bal.cli`` sub-package, so this module
|
|
re-exports ``Plugin`` from ``bal.cli.plugin``.
|
|
|
|
Like ``qt.py``, this file is not a one-line relative import because the very
|
|
same code may be loaded as an *external* plugin from a ``.zip``, where Electrum
|
|
imports the package under the synthetic top-level name
|
|
``electrum_external_plugins.bal`` and never registers the intermediate parent
|
|
packages. See the module docstring of ``bal.qt`` for the full rationale. The
|
|
shim resolves the run-time package name, backfills the missing parents into
|
|
``sys.modules`` and imports the real implementation via
|
|
:func:`importlib.import_module`.
|
|
|
|
Unlike ``qt.py``, this module MUST never import PyQt (the daemon loads it in a
|
|
headless process).
|
|
"""
|
|
|
|
import importlib
|
|
import sys
|
|
|
|
|
|
def _ensure_parent_packages(pkg_name: str) -> None:
|
|
"""Make sure every ancestor package of *pkg_name* is in ``sys.modules``.
|
|
|
|
When loaded from a zip as an external plugin, Electrum only executes the
|
|
plugin package ``__init__`` and the ``cmdline`` module. The synthetic root
|
|
package (e.g. ``electrum_external_plugins``) and any intermediate packages
|
|
may be missing from ``sys.modules``, which breaks relative/absolute
|
|
sub-module imports. We backfill them here using this module's own loader
|
|
so that ``importlib`` can find sibling sub-packages.
|
|
"""
|
|
parts = pkg_name.split(".")
|
|
# Walk from the top-most ancestor down to (but not including) pkg_name.
|
|
for i in range(1, len(parts)):
|
|
ancestor = ".".join(parts[:i])
|
|
if ancestor in sys.modules:
|
|
continue
|
|
try:
|
|
importlib.import_module(ancestor)
|
|
except Exception:
|
|
# The synthetic root (e.g. 'electrum_external_plugins') often has no
|
|
# real spec. Create a minimal namespace package stub so that the
|
|
# import machinery can still resolve its children.
|
|
import types
|
|
|
|
module = types.ModuleType(ancestor)
|
|
module.__path__ = [] # mark as a (namespace) package
|
|
sys.modules[ancestor] = module
|
|
|
|
|
|
# The package this module belongs to. Could be 'electrum.plugins.bal' (internal)
|
|
# or 'electrum_external_plugins.bal' (external zip), depending on how Electrum
|
|
# loaded us.
|
|
_PKG = __package__ or "bal"
|
|
|
|
_ensure_parent_packages(_PKG)
|
|
|
|
# Import the real implementation using the fully-qualified, run-time package
|
|
# name so it works regardless of the synthetic prefix Electrum assigned.
|
|
_plugin_module = importlib.import_module(_PKG + ".cli.plugin")
|
|
|
|
Plugin = _plugin_module.Plugin # noqa: F401 (re-exported for Electrum)
|