v0.6.1: read version from manifest.json (single source of truth)
Read the plugin version at runtime from bal/manifest.json via importlib.resources (zip-safe, works from inside a zip on Windows). Removes the four-file duplication (VERSION, __init__.py, plugin_base.py, manifest.json) and deletes the bal/VERSION file. - bal/core/plugin_base.py: new get_version() function, BalPlugin.version is now a @property, removed hardcoded __version__ - bal/__init__.py: removed __version__, replaced with a comment - bal/core/willexecutors.py: user-agent uses get_version() - bal/gui/qt/dialogs.py, widgets.py: call sites use .version property - tests/test_version_source.py: 7 tests incl. zip-safe subprocess check - HANDOFF.md: updated to single-source-of-truth documentation - CHANGELOG.md: entries 43-46 - bal/manifest.json: version bumped to 0.6.1 PR #4 (branch bitcoinafterlife-patch-5).
This commit is contained in:
@@ -1 +0,0 @@
|
||||
0.5.18
|
||||
@@ -36,4 +36,7 @@ The plugin supports Electrum 4.7.2 and 4.8.0 with PyQt6. Electrum 4.8.0 removed
|
||||
available and adapts, so both releases keep working.
|
||||
"""
|
||||
|
||||
__version__ = "0.5.18"
|
||||
# The plugin version is NOT defined here. It lives only in ``bal/manifest.json``
|
||||
# (the single source of truth) and is read at runtime via ``get_version()`` in
|
||||
# ``bal/core/plugin_base.py`` (exposed as the ``BalPlugin.version`` property).
|
||||
# Keeping a hardcoded ``__version__`` here would just be a stale duplicate.
|
||||
|
||||
@@ -21,6 +21,7 @@ serialised together with the wallet file.
|
||||
This module performs **no** GUI work and imports nothing from PyQt / electrum.gui.
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
import platform
|
||||
from datetime import date, datetime, timedelta
|
||||
@@ -33,6 +34,45 @@ from electrum.transaction import tx_from_any
|
||||
_logger = get_logger(__name__)
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Plugin version - single source of truth
|
||||
# --------------------------------------------------------------------------- #
|
||||
# The version lives ONLY in bal/manifest.json (the file Electrum itself reads).
|
||||
# We used to hardcode it in four files and keep them in sync with a pre-commit
|
||||
# hook; reading it from the manifest removes that duplication.
|
||||
#
|
||||
# importlib.resources is used on purpose: it reads a data file bundled inside
|
||||
# the ``bal`` package and works identically whether the plugin runs from an
|
||||
# extracted directory or from INSIDE a zip (Electrum loads external plugins via
|
||||
# zipimport). It never builds a path by hand, so there is no os.path.join
|
||||
# backslash issue on Windows inside a zip.
|
||||
_VERSION_CACHE = None
|
||||
|
||||
|
||||
def get_version():
|
||||
"""Return the plugin version from ``bal/manifest.json`` (cached).
|
||||
|
||||
Zip-safe and independent of the current working directory. Falls back to
|
||||
``"unknown"`` if the manifest cannot be read, so importing the plugin never
|
||||
fails just because of version lookup.
|
||||
"""
|
||||
global _VERSION_CACHE
|
||||
if _VERSION_CACHE is None:
|
||||
try:
|
||||
import importlib.resources
|
||||
|
||||
data = (
|
||||
importlib.resources.files("bal")
|
||||
.joinpath("manifest.json")
|
||||
.read_text(encoding="utf-8")
|
||||
)
|
||||
_VERSION_CACHE = json.loads(data)["version"]
|
||||
except Exception as e: # noqa: BLE001 - never break import over version
|
||||
_logger.error(f"failed to read version from manifest.json: {e}")
|
||||
_VERSION_CACHE = "unknown"
|
||||
return _VERSION_CACHE
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Wallet-DB registration
|
||||
# --------------------------------------------------------------------------- #
|
||||
@@ -120,9 +160,6 @@ class BalPlugin(BasePlugin):
|
||||
layer (or unit tests) can use the plugin logic without importing Qt.
|
||||
"""
|
||||
|
||||
_version = None
|
||||
__version__ = "0.5.18" # AUTOMATICALLY GENERATED DO NOT EDIT
|
||||
|
||||
# Command used to open an .ics calendar file, per operating system.
|
||||
default_app = {
|
||||
"Linux": "xdg-open",
|
||||
@@ -138,18 +175,11 @@ class BalPlugin(BasePlugin):
|
||||
# Default geometry hint for some dialogs (kept from the original code).
|
||||
SIZE = (159, 97)
|
||||
|
||||
@property
|
||||
def version(self):
|
||||
"""Return the plugin version, read once from the ``VERSION`` file."""
|
||||
if not self._version:
|
||||
try:
|
||||
f = ""
|
||||
with open("{}/VERSION".format(self.plugin_dir), "r") as fi:
|
||||
f = str(fi.read())
|
||||
self._version = f.strip()
|
||||
except Exception as e:
|
||||
_logger.error(f"failed to get version: {e}")
|
||||
self._version = "unknown"
|
||||
return self._version
|
||||
"""Plugin version, read from ``bal/manifest.json`` (single source of
|
||||
truth). See :func:`get_version`."""
|
||||
return get_version()
|
||||
|
||||
def __init__(self, parent, config, name):
|
||||
self.logger = get_logger(__name__)
|
||||
|
||||
@@ -23,7 +23,7 @@ from electrum.i18n import _
|
||||
from electrum.logging import get_logger
|
||||
from electrum.network import Network
|
||||
|
||||
from .plugin_base import BalPlugin
|
||||
from .plugin_base import BalPlugin, get_version
|
||||
|
||||
# Per-request timeout (seconds) for interactive operations (ping / info /
|
||||
# list download). These fail fast (no retries) so a dead server does not
|
||||
@@ -272,7 +272,7 @@ class Willexecutors:
|
||||
raise Exception("You are offline.")
|
||||
_logger.debug(f"<-- {method} {url} {data}")
|
||||
headers = {}
|
||||
headers["user-agent"] = f"BalPlugin v:{BalPlugin.__version__}"
|
||||
headers["user-agent"] = f"BalPlugin v:{get_version()}"
|
||||
headers["Content-Type"] = "text/plain"
|
||||
if not handle_response:
|
||||
handle_response = Willexecutors.handle_response
|
||||
|
||||
@@ -1533,7 +1533,7 @@ class BalBuildWillDialog(BalDialog):
|
||||
"BEGIN:VCALENDAR",
|
||||
"VERSION:2.0",
|
||||
"PRODID:-//Bitcoin After Life//Electrum Plugin/"
|
||||
f"{BalPlugin.__version__}",
|
||||
f"{self.bal_window.bal_plugin.version}",
|
||||
]
|
||||
|
||||
total = len(offsets)
|
||||
|
||||
@@ -1153,7 +1153,7 @@ class WillSettingsWidget(QWidget):
|
||||
lines = [
|
||||
"BEGIN:VCALENDAR",
|
||||
"VERSION:2.0",
|
||||
f"PRODID:-//Bitcoin After Life//Electrum Plugin/{BalPlugin.__version__}",
|
||||
f"PRODID:-//Bitcoin After Life//Electrum Plugin/{self.bal_window.bal_plugin.version}",
|
||||
]
|
||||
|
||||
# One separate VEVENT per reminder offset (its own date in the calendar).
|
||||
@@ -1284,7 +1284,7 @@ class WillSettingsWidget(QWidget):
|
||||
"BEGIN:VCALENDAR",
|
||||
"VERSION:2.0",
|
||||
"PRODID:-//Bitcoin After Life//Electrum Plugin/"
|
||||
f"{BalPlugin.__version__}",
|
||||
f"{self.bal_window.bal_plugin.version}",
|
||||
]
|
||||
|
||||
total = len(offsets)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "bal",
|
||||
"fullname": "Bitcoin After Life",
|
||||
"version": "0.5.18",
|
||||
"version": "0.6.1",
|
||||
"description": "Provides free and decentralized Bitcoin inheritance support. Build time-locked 'will' transactions that transfer funds to your heirs if you stop refreshing them (dead-man's switch), optionally relayed by will-executor servers.",
|
||||
"author": "Svatantrya",
|
||||
"licence": "MIT",
|
||||
|
||||
Reference in New Issue
Block a user