Sync addons-source@maintenance/gramps60 with upstream (2026-07-10)#60
Sync addons-source@maintenance/gramps60 with upstream (2026-07-10)#60eduralph wants to merge 10 commits into
Conversation
feat: sort items by month and day
…py runner Adds a repo-root test suite for addons, runnable via `make.py <series> test`: - tests/gramps_test_env.py — GrampsTestCase / GrampsDbTestCase base classes that boot the real Gramps plugin manager and registry once per process. - tests/test_plugin_registration.py — registers every addon through Gramps' PluginRegister, validates id/name/version and the target Gramps series, loads each addon in a crash-isolated subprocess, and smoke-tests import/export entry points. - tests/test_plugin_load_gate.py — regression test that the load check gates (self.fail) on a genuine load failure rather than only logging it. - tests/test_addon_dependency_loading.py — exercises Gramps' depends_on mechanism over the tree: every declared dependency resolves to a registered plugin, the dependency graph is acyclic, and each declared dependency is load-bearing — proven by an isolated subprocess import so sys.modules caching can't mask a missing/undeclared dependency. Verified against Gramps 6.0 and 6.1. - make.py — new `test` command that runs the suite via unittest discovery, gated on GRAMPSPATH like the other commands. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Follow-ups from repeated adversarial review of the suite: - Load test: display/GTK/environment failures are classified consistently (anchored signatures, unit-tested) and advisory by default; a genuine hard failure — or an import hang, detected via a REGISTRY_READY marker rather than a slow registry scan — always gates. One slow addon load can no longer abort the whole test. - Strict mode (GRAMPS_ADDON_TEST_STRICT=1) turns the smoke run into a full gate: dependency skips, environment failures and unverified addon dependencies become hard failures; only slow registry-scan timeouts stay advisory. Covered by synthetic regression tests that don't boot the registry. - Import/export smoke tests fail (not silently skip) when a dependency-satisfied plugin fails to load. - Unlisted-addon manifest: source-based and per-registration (parses each .gpr.py with ast), so an accidental include_in_listing=False — even on one plugin inside a mixed directory like TMGimporter — fails the test. - Neutralise blocking modal dialogs an addon may pop at import (e.g. lxml without python3-lxml) so a load can't hang or scatter UI. - Silence expected locale / PyGI noise so results are readable. - Failure messages lead with the real failures; make.py's test docstring and the load-test docstring document default (smoke) vs strict (full gate) modes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…heck Gramps accepts a comma-separated list of acceptable versions in a plugin's requires_gi: Requirements.check_gi() in gramps/gen/utils/requirements.py splits on "," and takes the first version that resolves. _check_dependencies() passed the raw string straight to gi.require_version(), which raises ValueError — so the two addons in the tree that use the form, GraphView (GooCanvas "2.0,3.0") and PhotoTaggingGramplet (GExiv2 "0.10,0.12,0.14,0.16"), were reported as having an unmet dependency on hosts that in fact satisfy it. Advisory in the default smoke run; a false hard failure under GRAMPS_ADDON_TEST_STRICT=1. Neither .gpr.py is wrong. Reported by Gary Griffin on PR 980. Delegate to Gramps' own Requirements rather than re-implement the checks, so the test agrees with what Gramps does at runtime. That also drops two smaller deviations: requires_mod was import_module()'d where Gramps uses find_spec (no import side effects), and requires_exe hand-rolled a PATH scan where Gramps' search_for also tries name + ".exe" for Windows. The gi.repository import probe is kept, now run after check_gi() has pinned the matching version, so a typelib that resolves but fails to import stays a dependency skip instead of a hard failure blamed on the addon. TestDependencyCheck covers the comma form (red before this change: reports a spurious gi:Gtk-3.0,4.0), the single-version form, an absent namespace, and an absent module/executable. It boots no registry and needs no display. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
neutralize_gui_dialogs() replaced gramps.gui.dialog's dialogs with silent
no-ops, so an addon that constructs one at import time — e.g. lxmlGramplet on a
host without python3-lxml — loaded clean and the suite said nothing. That kept
the load from hanging on a modal (Gramps' ErrorDialog.__init__ calls .run()),
but it also hid the addon bug the stub exists to survive: before the stub the
same addon was reported as "hung during import", after it, as nothing at all.
Raised by Gary Griffin on PR 980 ("Is this now hidden?"). It was.
Each stub now records its construction, on gramps_test_env.dialogs_raised and as
a DIALOG_MARKER line on stdout. The isolated load already captures the
subprocess' stdout, so test_load_all_addon_modules scans it and reports a dialog
at import as a hard failure in both modes, naming the addon and the dialog
class. The marker is matched only at the start of a line, so a traceback quoting
it cannot forge a hit.
The load-failure header now reads "failed to load cleanly ... real defects"
rather than "failed to load ... real load failures": an addon caught this way
did load, and the message must not claim otherwise.
Verified against the real addon: with lxml unimportable, lxml/lxmlGramplet.py
raises ErrorDialog at import (dialogs_raised == ['ErrorDialog'], hard failure);
the same probe against PR 981's lxmlGramplet raises none. The gate itself is
covered synthetically by test_dialog_raised_at_import_gates, which is red
without the stdout scan even though the load exits 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lxmlGramplet raised ErrorDialog(...).run() at module *import* time when gzip or lxml was unavailable. That is a blocking modal shown before any GUI action: it stalls plugin loading until dismissed, and fires (or aborts) when Gramps is imported without a display — e.g. under the CLI or a test harness, where a missing python3-lxml made the module hang or scatter dialogs. The gramplet already degrades gracefully — it falls back to xml.etree when lxml is absent — so the missing dependency is not fatal. Set the availability flags silently at import (log instead of a dialog), and show the notice in init(), when the gramplet is actually opened and the GUI is running. No .gpr.py change: the addon version is incremented automatically at publish time. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4034278c94
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if len(self.gui.data) == 3: | ||
| self.sort_mode = self.gui.data[0] | ||
| self.ignore_tag = self.gui.data[1] | ||
| self.only_tag = self.gui.data[2] | ||
| else: |
There was a problem hiding this comment.
Preserve existing Birthday tag filters on load
When a user already has this gramplet saved from the previous version, self.gui.data still contains the two values that used to be written ([ignore_tag, only_tag]). This branch treats any length other than 3 as a fresh configuration and clears both tag filters, so upgrading silently loses saved Birthday filter settings until the user reselects them; please handle len(self.gui.data) == 2 as legacy data and default only the new sort option.
Useful? React with 👍 / 👎.
| capture_output=True, | ||
| text=True, | ||
| timeout=_LOAD_TIMEOUT, | ||
| env={**os.environ, "PYTHONPATH": ADDONS_ROOT}, |
There was a problem hiding this comment.
Pass GRAMPSPATH through isolated load subprocesses
With the documented GRAMPSPATH=/path/to/gramps python3 make.py gramps60 test flow and no globally installed gramps package, the parent test process can import Gramps because make.py inserts GRAMPSPATH into sys.path, but this child process replaces PYTHONPATH with only ADDONS_ROOT and imports gramps before adding any checkout path. That makes every addon load look like a hard failure with No module named 'gramps' in a valid source-checkout test environment; include the existing PYTHONPATH/GRAMPSPATH or a resolved Gramps root in the subprocess environment/path.
Useful? React with 👍 / 👎.
a9129d9 to
bbf54c5
Compare
bbf54c5 to
8064e7a
Compare
Automated nightly sync from
gramps-project/addons-source@maintenance/gramps60. Generated by .github/workflows/upstream-sync.yml on the testbed.