Skip to content

Sync gramps@maintenance/gramps61 with upstream (2026-06-10)#17

Open
eduralph wants to merge 49 commits into
maintenance/gramps61from
sync/upstream-maintenance-gramps61-auto
Open

Sync gramps@maintenance/gramps61 with upstream (2026-06-10)#17
eduralph wants to merge 49 commits into
maintenance/gramps61from
sync/upstream-maintenance-gramps61-auto

Conversation

@eduralph

Copy link
Copy Markdown
Owner

Automated nightly sync from gramps-project/gramps@maintenance/gramps61. Generated by .github/workflows/upstream-sync.yml on the testbed.

dsblank and others added 3 commits June 9, 2026 00:05
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The original report noted that date columns sort without grouping
dates with qualifiers like about, after, before so they appear in
an arbitrary order.

This happens because get_sort_value() returns only the Julian Day
Number with no modifier or quality information.

This change adds Date.get_sort_key() which creates a sort key
based on the JDN adjusted with scores for modifiers and quality

It also updates date sort columns in the list view treemodels
(peoplemodel, familymodel, eventmodel, mediamodel, citationbasemodel)
to call get_sort_key() instead of get_sort_value().

The key is 'JDN * 12 + modifier_score * 3 + quality_score'

Modifier scores:

  0 - MOD_BEFORE
  1 - MOD_NONE, MOD_FROM, MOD_TO, MOD_SPAN, MOD_RANGE
  2 - MOD_ABOUT
  3 - MOD_AFTER

Quality scores:

  0 - QUAL_NONE
  1 - QUAL_ESTIMATED
  2 - QUAL_CALCULATED

For year-only after-dates the position advances to the first JDN of
the following year, which is correct for all supported calendars.
This ensures 'after 1823' sorts after '1823-12-31'.

The max sort key for 31 Dec 9999 is 64481819 (JDN 5373484 * 12)
which fits in the format string "%09d" used by treemodels.

get_sort_value() is unchanged and continues to be used for date
arithmetic, calendar conversion and span calculations throughout the rest
of the codebase.

Fixes #7761.
Restores support for a user-supplied tile URL in the embedded geography
view. The custom URL is entered in Geography preferences using #Z/#X/#Y
as coordinate placeholders. Example providers include MapTiler NLS
historical Ordnance Survey maps (uk-osgb1888, uk-osgb63k1885, etc.).

OsmGpsMap requires a non-null map-source enum value to honour a custom
repo_uri; using map_source=0 (NULL) causes it to render gray tiles and
ignore the URL entirely. Fix this by passing OPENSTREETMAP_RENDERER,
whose built-in URI is NULL (service defunct), so OsmGpsMap keeps the
supplied repo_uri and fetches tiles from it.

Also fixes the image-format lookup for custom URLs, debounces the
url entry field so it only fires on Enter or focus-out rather than
on every keystroke, and widens the URL input field to span the full
grid width.
@eduralph eduralph force-pushed the sync/upstream-maintenance-gramps61-auto branch 5 times, most recently from 8076d69 to 2ac1386 Compare June 15, 2026 09:51
SourceAirbender and others added 21 commits June 15, 2026 11:09
* Bulk import.
* Merge window.
* Sign-in from status bar icon.
* Enable/disable integration.
* Progress fedback.
* CSS segregation.
* Bug fixes.

Co-Authored-By: Doug Blank <doug.blank@gmail.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Currently translated at 96.0% (7260 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/ru/
Currently translated at 98.6% (7455 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/it/

Translated using Weblate (Italian)

Currently translated at 98.6% (7454 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/it/

Translated using Weblate (Italian)

Currently translated at 98.6% (7454 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/it/

Translated using Weblate (Italian)

Currently translated at 95.0% (7183 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/it/
Currently translated at 98.2% (7426 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/ba/
Currently translated at 96.8% (7319 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/zh_Hans/
Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/da/
Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/he/

Translated using Weblate (Hebrew)

Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/he/

Translated using Weblate (Hebrew)

Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/he/

Translated using Weblate (Hebrew)

Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/he/
Currently translated at 96.0% (7260 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/ru/
Currently translated at 61.5% (4651 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/zh_Hant_HK/
Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/sk/

Translated using Weblate (Slovak)

Currently translated at 100.0% (7555 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/sk/
Currently translated at 4.1% (311 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/ro/
FanChartWidget.set_generations and FanChart2WayWidget.set_generations
initialised self.data[i] with a shared (None,) * 4 tuple per slot,
leaving the userdata element as None. When _fill_data_structures
short-circuited (no rootpersonh on startup, after rename, etc.) the
slots were never replaced, so prepare_background_box's
BACKGROUND_GRAD_AGE and BACKGROUND_GRAD_PERIOD paths called
userdata.append(...) on None and crashed with:

    AttributeError: 'NoneType' object has no attribute 'append'

Give each slot its own empty userdata list at init time so the
invariant holds regardless of _fill_data_structures' execution.
Adds a regression test under gramps/gui/widgets/test/ that drives
set_generations via __new__-bypass so it runs headless.

Fixes #13395.
When the database is closed the framework switches the active model
to an empty state by clearing self.tree, so the root entry
self.tree[None] no longer exists.  TreeBaseModel.reverse_order
looked it up unconditionally, so the next column-header click in
the People view (or any other list view backed by a TreeBaseModel)
raised:

    KeyError: None

at treebasemodel.py:reverse_order, surfacing to the user as an
unhandled exception.

Short-circuit reverse_order when the tree has no root entry: there
is nothing to reverse in a cleared state, and the user's expected
behaviour is "no action because no tree is open".  Adds a
regression test under gramps/gui/views/treemodels/test/.

Fixes #13214.
Bug 4658: when the "Limit dates to years only" report option is on,
Family Lines Graph rendered "before 1923" as "1923" -- the modifier
(before / after / about), quality (estimated / calculated), and the
second stop of compound dates (range / span) were all dropped. The
sister Relationship Graph plugin shares the same defect via the
ported code path that gburto01 flagged in the bug's 2011-02-27 note.

Root cause: ``Date(date.get_year())`` invokes the Date constructor's
int-source branch, which builds a fresh date with MOD_NONE / QUAL_NONE
and a single year, discarding the modifier and any compound second
stop.

Fix: introduce a private ``_year_only_date`` helper in each plugin
that copies the original date (preserving modifier and quality) and
truncates month and day to 0, with the second stop of compound dates
truncated to year-only on its own. Replace all four ``Date(year)``
call sites (gvfamilylines.py x 3, gvrelgraph.py x 1). The helper is
intentionally duplicated rather than placed in a new shared module,
to keep the change localised; a ``test_helpers_agree`` test guards
against the copies drifting.

Regression test added at gramps/plugins/graph/test/year_only_date_test.py
covers modifier preservation (BEFORE/AFTER/ABOUT), quality
preservation (ESTIMATED/CALCULATED), month/day truncation, compound
range/span second-stop preservation, and parity between the two
plugin helpers.

End-to-end check with the GrampsLocale displayer confirms the
reporter's case:

  before 1923            -> before 1923            (was: 1923)
  between 1923 and 1925  -> between 1923 and 1925  (was: 1923)
  estimated about 1923   -> estimated about 1923   (was: 1923)

Fixes #4658.
Bug 6556: graphviz-based reports (Family Lines Graph, Relationship
Graph, etc.) failed silently when the chosen output file could not
be written -- typically because another process (a viewer on
Windows) held a lock on it, but also under permission denied / disk
full / missing binary. The user saw no error and no file, and had
no way to learn the report had not been generated. Text and
graphical reports already surfaced this via "Could not create %s"
ReportError; only the graphviz path was silent.

Root cause: graphdoc.py shells out to ``dot`` (and to ``gs`` for the
multi-page PDF path) via ``os.system(...)`` at 11 sites, and
discards the return code. Any failure to write the output is
invisible to the rest of the function.

Fix: route every ``os.system`` invocation through a new
``_check_rc(rc, target)`` helper that raises
``ReportError(_("Could not create %s") % target)`` on a non-zero
return code. The wording matches the message uniformly used by the
other docgen backends (rtfdoc, svgdrawdoc, odfdoc), so downstream
error-handling code paths and the user-visible dialog see the same
text regardless of which output format the user picked.

The 11 call sites span 8 graphdoc classes:
  GVPsDoc, GVSvgDoc, GVSvgzDoc, GVPngDoc, GVJpegDoc, GVGifDoc,
  GVPdfGvDoc, and GVPdfGsDoc (which has 4 sequential os.system
  calls: dot -> tmp ps, then gs in single-page / per-piece /
  merge variants).

Each call site keeps its existing cleanup-then-check ordering so
temp files generated before the failure point still get removed
where they were already being removed. The minimum-delta approach
preserves the file's existing cleanup semantics (which were already
inconsistent in the single-page PdfGs path -- that pre-existing
weakness is intentionally out of scope here).

Out of scope: the reporter's additional ask in note ~0038649 for a
pre-flight "try open output for write" check before report
generation begins. That is a feature ask; this commit fixes the
bug-class part of the report (silent failure -> visible error).

Test: new gramps/gen/plug/docgen/test/graphdoc_test.py with two
groups:
  - CheckRcTest: four cases asserting the helper is silent on rc=0,
    raises ReportError on non-zero, names the target in the
    message, and treats *any* non-zero (incl. POSIX-encoded
    high-byte exits like 256 and signal-encoded negatives) as
    failure.
  - GraphdocStaticAssertions: two source-grep guards -- no bare
    ``os.system(...)`` callsites remain in graphdoc.py, and every
    ``rc = os.system(...)`` assignment is paired with a
    ``_check_rc(rc, ...)`` call. This catches a regression that
    would silently reopen bug 6556 if a future edit added an
    os.system call without the check.

Verified end-to-end:

  _check_rc(0,   "/tmp/ok.svg")     -> silent       (success path)
  _check_rc(256, "/tmp/locked.svg") -> ReportError: "Could not create /tmp/locked.svg"

Fixes #6556.
Bug 10443: the man page's ENVIRONMENT VARIABLES section listed
LANG only. Users who set LANG=xx_YY.UTF-8 to switch languages and
hit a system where that alone isn't enough -- a common gettext
fallback chain hazard -- had no way to learn from the documentation
that they also needed to set LANGUAGE. The reporter found this
empirically after digging through "gramps -v" output, and asked
that the docs be updated to save other users the same time.

LANGUAGE has been a fully-supported, documented-in-source override
for years (gramps/gen/utils/grampslocale.py:250-256: colon-separated
list of language codes, splits on ":", overrides LANG and
LC_MESSAGES, logs "Overiding locale setting ... with LANGUAGE
setting ..."). The CLI help text in gramps/cli/argparser.py:140
already shows the LANGUAGE=de_DE; LANG=de_DE.UTF-8 idiom in its
report-generation example. Only the man page (and the wiki, which
is outside the source tree) hadn't caught up.

Add a LANGUAGE entry to data/man/en.rst's ENVIRONMENT VARIABLES
section, immediately after LANG, explaining the semantics and the
canonical "set both" idiom the reporter found works:

    LANG=ru_RU.UTF-8 LANGUAGE=ru gramps

Backtick style matches the surrounding file (double for variable
names referenced inline, single for value literals).

Out of scope:

  * Regenerating data/man/gramps.1.in via sphinx-build. That file
    is generated from en.rst and is regenerated in batches by
    maintainers (last regen: 8159aca "Generate gramps.1.in
    file", Feb 2022; en.rst has seen 4 unpaired commits since).
    Following the existing rst-only edit pattern -- maintainer can
    re-run sphinx-build -b man at the next batch regeneration.

  * Localized man pages under data/man/{cs,fr,nl,pl,pt_BR,sv}/.
    Those follow the Weblate translation workflow once en.rst is
    updated; translator-side change, not a same-PR concern.

  * The wiki pages the reporter and Sam888 actually flagged
    (Howto:Change_the_language_of_reports#How_to_install_a_locale
    and the two Gramps_5.0_Wiki_Manual_-_Command_Line /
    _-_Settings pages). Those are wiki edits outside the source
    tree; a separate community task.

Test: this is documentation, no functional code path changes. No
unit test applies. Manual verification: the prose accurately
describes the behavior implemented at grampslocale.py:250-256 (the
authoritative source) and gives the exact form the reporter
empirically found works.

Fixes #10443.
format_long_month_year, format_short_month_year, format_long_month and
format_short_month looked up the inflect key in FORMATS_long_month_year
/ FORMATS_short_month_year with a bare subscript, so an inflection key
absent from those dicts raised KeyError instead of degrading.

display_formatted() builds the inflect key from a translatable string
(e.g. _("", "about-date")), which the surrounding comments ask
translators to leave untranslated or render as the English keyword the
dicts are keyed by ("about"). The Finnish fi.po instead translates
"about-date" to the native word "noin", so an "about <Month> <Year>"
date shown under a long-month format crashed with KeyError: 'noin' --
in the Detailed Ancestral Report, the Relationship View and elsewhere.

Look the key up with dict.get(), falling back to the uninflected ("")
format, so a mistranslated locale degrades to a readable date instead
of aborting.

Fixes #14100.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`HourGlassReport.traverse_down` linked focal → family → children but
never added the family's *other* parent. `traverse_up` already handles
both — its father-section and mother-section each add the parent node
and an edge to the family. Descending was asymmetric and every
descendant generation's spouse was invisible: e.g. starting from
example.gramps' Lewis Anderson Garner (I00044) with maxdescend=2 the
graph rendered Lewis → F00017 → eight children but never named
Luella Jacques Martel (I00045) nor any of the children's spouses.

In `traverse_down`, after adding the family node and the focal-to-family
edge, derive the other parent's handle (the one that isn't `person`),
add that person to the graph, and link them to the family with the
same arrow style as the focal-to-family edge. Mirrors the per-parent
shape of `traverse_up`, with `__used_people` keeping the spouse from
being re-added if cousin-marriage paths bring them through later.

- `gramps/plugins/graph/gvhourglass.py:163` — `traverse_down` body (now
  includes the spouse-add block at lines 178-202)
- `gramps/plugins/graph/gvhourglass.py:193` — `traverse_up` (existing
  symmetric pattern this matches)

`gramps/plugins/test/reports_test.py` adds
`test_hourglass_graph_includes_spouse_mantis_9628`. It invokes the
report on data.gramps with center=I0027 (Smith Ingeman), maxdescend=1,
maxascend=0; the expected spouse is Ericsdotter Marta (I0025).

  * Pre-fix: fails with `'Ericsdotter' not found in '<DOT>'` — DOT
    contains only Ingeman, the family node, and the child Martin.
  * Post-fix: `ok` — Marta appears in the DOT as a person node linked
    to F0001.

Fixes #9628.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GEDCOM 5.5.1 requires three-letter month abbreviations
(``7 NOV 1959``), but tools such as ancestry.com export bare numeric
``D/M/YYYY`` dates the spec does not define.  ``libgedcom.GedcomDateParser``
hardcodes ``dhformat = "%m/%d/%y"`` (en_US locale, ``libgedcom.py:925``),
so a bare numeric date is unconditionally read MM/DD/YYYY.  When the
source actually uses DD/MM/YYYY and the day is ≤ 12 (both readings
syntactically valid), the importer silently swaps day and month and
emits no warning — ``GEDCOM import report: No errors detected``
despite a wrong date being written to the database.  Day > 12 already
falls back to text-only storage (visually flagged in the Events view
as bold), so only the day ≤ 12 case is silently dangerous.

This was reported in 2016 and re-confirmed in 2022/2023 across
multiple discourse threads; multiple developers have discussed
narrower-scope fixes (warn-only, source-detect ancestry, switch in
import dialog) but none have shipped.  No code-side parse-direction
change is possible without breaking US users who currently rely on
the existing MM/DD/YYYY default for legitimately-shaped numeric input.

The smallest defensible change: emit an advisory warning per
ambiguous bare-numeric date during import, no parse-direction change.
US users see no behaviour change; European users importing
ancestry.com-shaped GEDCOMs get a per-event audit list with both the
original GEDCOM text and the chosen interpretation.

- ``BARE_NUMERIC_DATE`` regex added next to the other module-level
  GEDCOM patterns (``libgedcom.py:894``).
- ``GedcomParser.__add_warn`` (new) wraps ``__add_msg`` with the
  ``number_of_errors -= 1`` pattern already used at the end of
  ``__finish_import`` for the unknown-references note; the summary
  line stays "No errors detected" when the warnings are advisory.
- ``GedcomParser.__warn_if_ambiguous_numeric_date`` (new) is called
  from ``__event_date`` after the date object is attached to the
  event.  Suppresses on ``MOD_TEXTONLY`` (already visually flagged)
  and on non-numeric inputs.  ``line`` is intentionally not passed
  through ``__add_warn`` because ``__add_msg`` truncates the problem
  string to 66 columns when a line is supplied, which drops the
  actionable half of the message — and the warning text already
  contains the original GEDCOM date string verbatim.

Scope is limited to ``__event_date`` (the only date sink the bug
report cites).  ``__name_date`` / ``__lds_ord_date`` / ``__addr_date``
/ ``__citation_date`` / ``__media_date`` could call the same helper
in a follow-up if a maintainer asks; left out here to keep the diff
focused on the cited symptom.

- ``gramps/plugins/lib/libgedcom.py:925`` — the hardcoded
  ``dhformat = "%m/%d/%y"`` that drives the silent swap.
- ``gramps/plugins/lib/libgedcom.py:3797`` — existing precedent for
  the ``__add_msg`` then ``number_of_errors -= 1`` pattern used to
  attach an advisory note without polluting the error count.
- ``gramps/gen/lib/date.py:590`` — ``Date.MOD_TEXTONLY = 6`` (the
  fallback used when the parser can't make sense of the input,
  recognised in the helper to suppress the warning).

``gramps/plugins/importer/test/importgedcom_ambiguous_date_test.py``
imports an Ancestry-shaped GEDCOM fixture via ``import_as_dict`` with
a ``CapturingUser`` that intercepts the summary dialog text.  Four
assertions:

1. ``7/11/1959`` (day ≤ 12, the silent-swap case) triggers an
   "Ambiguous numeric date" warning naming the original text and
   the chosen interpretation.
2. ``5 MAR 1899`` (spec-compliant) stays silent.
3. ``25/3/1934`` (day > 12, text-only fallback) stays silent.
4. The summary still reads "GEDCOM import report: No errors detected".

Pre-fix, test 1 fails ("'Ambiguous numeric date' not found in 'GEDCOM
import report: No errors detected'"); the other three pass.  Post-fix
all four pass.

Fixes #9298.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Commit 48a6cbf fixed a regression in
the RelationshipPathBetween filter rule's init_list method that landed
in 1280aa4, where
`for person_handle in firstList and secondList` evaluates as
`secondList` via Python's short-circuit `and`, causing
`firstMap[person_handle]` to raise KeyError on handles reachable from
the second root but not the first. The fix shipped without a direct
test for the rule (only the RelationshipPathBetweenBookmarks wrapper
was tested), so the regression could reappear unnoticed.

This test exercises the rule with two example.gramps persons that
share no ancestor (I0044, the home person, and I2127, a member of the
unrelated Δεληπέτρου family). On the pre-fix code it failed with
KeyError at _relationshippathbetween.py:130, matching the line and
exception class in Mantis 13830's traceback. With the fix in place it
passes, returning the two endpoints unchanged.

Issue #13830.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The test_add_persons_one_fetch_raises_other_still_imported test
intentionally raises a RuntimeError to simulate a network failure,
but did not patch LOG, causing the LOG.warning(..., exc_info=True)
call in add_persons to emit a full traceback to stderr on every test
run. Patch LOG in that test, matching the pattern already used by the
companion test_add_persons_fetch_exception_is_logged_not_raised test.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@eduralph eduralph force-pushed the sync/upstream-maintenance-gramps61-auto branch from 2ac1386 to da6a4fb Compare June 16, 2026 09:15
eduralph and others added 12 commits June 16, 2026 22:32
GalleryTab.clean_up() did not disconnect the iconlist
'selection-changed' signal handler before super().clean_up() deleted
the iconlist attribute via track_ref_for_deletion. A late
selection-changed emission - which fires when the parent dialog tears
down on Cancel, e.g. when the Forms addon hosts a GalleryTab - then
re-entered _selection_changed -> get_selected() ->
self.iconlist.get_selected_items() and crashed with AttributeError:
'GalleryTab' object has no attribute 'iconlist'.

The original 2012 disconnect (r20849) was removed in 2023 to silence a
GTK warning when the iconlist had already been disposed. Restore the
disconnect, guarded by GObject.signal_handler_is_connected so it stays
silent in the dispose-first case.

Fixes #13326.
The disconnect restored for bug 13326 is gated by
GObject.signal_handler_is_connected so that the case commit
37395da was working around (iconlist already disposed by GTK
before clean_up) stays silent. Add a second test case that destroys
the iconlist widget before clean_up and asserts no Python warning of
the shape "instance '...' has no handler with id '...'" is recorded.
Pre-fix the unguarded disconnect emits exactly that warning;
post-fix the test passes.

Issue #13326.
gramps-ci.yml runs the unit-test suite without Xvfb and exports
GDK_BACKEND=-. Constructing a Gtk.IconView against a NULL screen
"succeeds" but segfaults when those orphan widgets are garbage
collected at process exit, taking the whole suite down with exit
code 139.

Detect the no-display case via DISPLAY / GDK_BACKEND / Gtk.init_check
at module import and skip the entire TestCase class cleanly. The
two regression tests still run under xvfb-run or on a real desktop.

Issue #13326.
The previous contract appended a label whenever an addon dict carried
a "rm" / "rg" / "re" key, regardless of whether the value was a non-
empty list. Present-but-empty keys - e.g. an addon listing with
`"re": []` because its .gpr.py declares `requires_exe=[]` - produced
a labelled section paired with an empty table. Two consequences for
existing consumers:

* gramps core's AddonManager Requirements dialog
  (`EnhancedAddonStatus.__on_requires_clicked` in
  `gramps/gui/plug/_windows.py`) renders the empty table as a
  labelled section with no rows. The PostgreSQL Enhanced listing
  surfaces this today: clicking Requires shows an empty
  "Executables" section after the real Python-modules section.

* The Plugin Manager Enhanced addon's row-click handler indexes
  `req_lst[0]` on the table and crashed with IndexError on the
  same shape (Mantis 13979, fixed addon-side in addons-source
  PR 916). This change tightens the producer side so any other
  future consumer is shielded too.

Build the table first and append the label + table only if the table
has entries. Behaviour for non-empty keys is preserved exactly (same
labels, same table contents, same rm / rg / re order).

Verified on `maintenance/gramps61`:

| input addon dict                                | before                                                                | after                              |
|-------------------------------------------------|-----------------------------------------------------------------------|------------------------------------|
| `{}`                                            | `[]`                                                                  | `[]`                               |
| `{"re": []}`                                    | `["Executables", []]`                                                 | `[]`                               |
| `{"rm": []}`                                    | `["Python modules", []]`                                              | `[]`                               |
| `{"rg": []}`                                    | `["GObject introspection modules", []]`                               | `[]`                               |
| `{"rm": ["psycopg"], "re": []}`                 | `["Python modules", [["psycopg", "X"]], "Executables", []]`           | `["Python modules", [["psycopg", "X"]]]` |
| `{"rm": [], "rg": [], "re": []}`                | three empty pairs                                                     | `[]`                               |

Known consumers of `Requirements.info` in gramps core: one call site
in `gramps/gui/plug/_windows.py:349` (the Requirements InfoDialog).
The addons-source Plugin Manager Enhanced addon
(`PluginManager/PluginManager.py`) has the only out-of-tree consumer
in the addons-source repo. Both are improved by the change; the
addon already guards empty tables defensively, so this is belt-and-
suspenders for it.

Ships `gramps/gen/utils/test/requirements_test.py` (8 cases): the
six pre-change shapes (none now emit empty pairs) plus two positive
cases asserting populated-section behaviour is unchanged (label,
table contents, rm/rg/re ordering).

Context for Issue #13979 - that crash is resolved by addons-source
PR 916; this change is the complementary core-side hardening, not a
substitute. No tracker action is taken on 13979 here.
SelectionWidget._button_release_event's "update current selection" else
branch (the path that fires when self.current is set and self.grabber ==
INSIDE) called self.current.set_coords(*self.selection) unconditionally.
Under the Mantis 13059 / 12659 reproducer -- resize a box edge, release,
then single-click (no drag) inside the same box -- that branch lands
with self.selection is None, because the prior resize-release left
self.grabber == INSIDE and the no-motion click never rebuilt selection.
*None is not iterable, raising TypeError before set_coords is even
invoked.

Guard both set_coords(*self.selection) call sites in the release handler
with "if self.selection is not None:". The safe no-op for a no-motion
click is to leave the region's stored coordinates alone -- the user
clicked without moving, the region shouldn't move. Conservative guard
mirroring the defensive-guard pattern from the 13966 / 13326
teardown-family fixes; no restructuring of the widget.

Both call sites guarded for defense in depth:
- line 770, grabber-edge branch -- _modify_selection always populates
  self.selection today, so this is a future-regression guard, not a
  known bug path.
- line 779, INSIDE-click branch -- the actual 13059 path.

Regression test ships with the fix at
gramps/gui/widgets/test/selectionwidget_test.py (headless unittest,
__new__-bypass to avoid needing a live GTK display). Three cases:

- test_click_after_resize_with_none_selection_does_not_raise -- the
  13059 path (grabber == INSIDE, selection == None). Pre-fix raises
  TypeError; post-fix does not, and the region's coords are unchanged.
- test_grabber_edge_release_with_none_selection_does_not_raise --
  same shape on the grabber-edge branch.
- test_click_with_valid_selection_still_updates_region -- sanity that
  a valid selection still updates the region's coords and emits
  region-modified.

This also addresses 12659 (the 2022 "could not reproduce" duplicate of
this issue) -- the reproducer on 13059 fires through 12659's path too.

Fixes #13059, #12659.
The dialog shown when ``AddonManager.install_addon`` cannot register a
plugin said only "The addon will be unavailable in your current
configuration" -- it named neither the failing addon nor any of the
common causes. The Mantis 13736 reporter and the linked Discourse
threads describe the canonical scenario: a 5.x ``gramps.ini`` carried
"addons-projects" entries into a 6.x install, those projects index
addons-XX.json catalogues whose ``gramps_target_version`` no longer
matches, ``valid_plugin_version`` rejects them at registration time,
and the user is left with no path from "unavailable" to "fix the
project URL". bamaustin's note on 13736 traces the dialog to
``gramps/gui/plug/_windows.py``.

Improve the dialog text to:

- Name the ``addon_id`` that failed to register, so users with
  multiple projects know which install attempt produced the error.
- Surface the most common cause (target-version mismatch via a stale
  project URL) and point at the exact GUI path that fixes it (Edit ->
  Preferences -> Addon Manager -> Projects).
- Quote the running Gramps ``major_version`` so users can compare it
  against the project URL directly without having to open About.

Pure message-text change. No behaviour change to plugin registration
itself; ``valid_plugin_version`` still does the underlying check.

Regression test ``gramps/gui/plug/test/windows_test.py`` (new;
headless ``unittest``, ``__new__``-bypass on ``AddonManager``,
``OkDialog`` patched out so the dialog text is captured rather than
rendered). Two cases:

- ``test_failure_dialog_names_the_addon_and_points_at_projects``
  forces ``get_plugin`` to return ``None`` and asserts the dialog's
  message contains the addon id, the running ``major_version``, and
  the "Projects" panel pointer.
- ``test_failure_dialog_not_shown_on_success`` sanity-checks the
  happy path: when ``get_plugin`` returns pdata, the dialog is not
  raised and ``load_plugin`` + the ``plugins-reloaded`` emit fire.

Fixes #13736.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The Top Surnames gramplet counted each person under every group name from
their primary and alternate names, and stored that person as the
representative for each surname.  The representative could therefore be a
person whose primary surname differed, when they matched only through an
alternate name.  The Same Surnames quick view re-derives the surname from the
representative's primary name, so double-clicking such a surname opened a
report for the wrong surname.  This most often affected the top surname, which
is the most likely to appear as an alternate name on people whose primary
surname differs.

Prefer a representative whose primary surname matches the group name.  The
per-person tally is moved to a module-level record_surnames() helper, leaving
the gramplet's incremental yielding unchanged, so the selection logic can be
tested without GTK or a database.  A regression test covers surname counting,
representative selection regardless of iteration order, and the fallback for
surnames that appear only as alternate names.

Fixes #11101.
Add Visual Studio Code's configuration folder to gitignore, allowing
developers to have local files which don't get added to commits.

For now the entire folder is ignored, but it would make sense to
provide a list of recommended extensions, launch configurations, etc.
and exclude those files from gitignore.
@eduralph eduralph force-pushed the sync/upstream-maintenance-gramps61-auto branch from da6a4fb to fd07571 Compare June 17, 2026 08:53
Mārtiņš B(...) and others added 12 commits June 17, 2026 23:29
Currently translated at 0.4% (34 of 7555 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/lv/
Updated by "Update PO files to match POT (msgmerge)" hook in Weblate.

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/
Currently translated at 100.0% (7644 of 7644 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/sk/
Currently translated at 73.9% (5652 of 7644 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/ja/
Currently translated at 99.7% (7627 of 7644 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/fr/
Currently translated at 100.0% (7644 of 7644 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/sv/
Currently translated at 98.6% (7544 of 7644 strings)

Translation: Gramps/Program
Translate-URL: https://hosted.weblate.org/projects/gramps-project/gramps/he/
The Mac localization helper passed open("/dev/null") as the stderr
argument to subprocess.Popen. Popen's context manager closes only the
pipes it creates itself, not a file object supplied by the caller, so
that handle leaked and raised a ResourceWarning on every unittest run
on macOS. Using subprocess.DEVNULL leaves no Python file object to leak.

Fixes #14243.
The str_incr generator in latexdoc.py produces the column identifiers
used for spanning cells in LaTeX tables. Its increment loop iterated the
character list's elements instead of its indices, so indexing the list
with a string raised TypeError: list indices must be integers or slices,
not str. The first value is yielded before the loop runs, so the crash
appeared on the second multicolumn id, which a styled note containing
subscript and strikeout produces.

Iterate the indices instead of the elements, preserving the existing
carry behaviour. A headless unit test exercises str_incr.

Fixes #13418.
@eduralph eduralph force-pushed the sync/upstream-maintenance-gramps61-auto branch 2 times, most recently from b76aa9a to 9970f9c Compare June 19, 2026 08:58
@eduralph eduralph force-pushed the sync/upstream-maintenance-gramps61-auto branch from 9970f9c to 6578c69 Compare June 20, 2026 07:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.