Skip to content

fix: self-assignment no-ops and mutable default argument in usfmerge.py#1158

Open
megahirt wants to merge 1 commit into
sillsdev:masterfrom
megahirt:fix/python-bug18-19-usfmerge
Open

fix: self-assignment no-ops and mutable default argument in usfmerge.py#1158
megahirt wants to merge 1 commit into
sillsdev:masterfrom
megahirt:fix/python-bug18-19-usfmerge

Conversation

@megahirt
Copy link
Copy Markdown
Contributor

@megahirt megahirt commented May 1, 2026

Summary

  • Fixes two self-assignment no-ops in the PARUSERSYNC merge branch that should be copying fields from acc[bi] into acc[i] (bug 18)
  • Fixes a mutable default argument {} in score() that causes scores to accumulate across calls (bug 19)

Both are in python/lib/ptxprint/usfmerge.py.


Bug 18 — Self-assignment no-ops in PARUSERSYNC merge

What is broken

self.acc[i].verse = self.acc[i].verse   # no-op
self.acc[i].pnum  = self.acc[i].pnum    # no-op

Both lines assign a field to itself. The surrounding context (the PARUSERSYNC branch) is copying the first element of acc[bi] into acc[i] and marking acc[bi] for deletion — a pattern where acc[i] should inherit bi's position data. The analogous code elsewhere in the function (lines 571–572) correctly reads from self.acc[bi].

Why it matters

The intended copy/merge operation silently does nothing. After the merge, acc[i] retains its own stale verse and pnum values instead of inheriting them from acc[bi]. This produces incorrect chunk positioning when PARUSERSYNC markers are present.

How it was fixed

Changed both right-hand sides from self.acc[i] to self.acc[bi], mirroring the correct pattern used earlier in the same function.


Bug 19 — Mutable default argument {} in score()

What is broken

def score(self, results={}):

The {} default is created once at class-definition time and shared across every call that omits the results argument. Scores written into results on one call persist into the next call, so subsequent invocations see stale data from previous runs.

Why it matters

This is the classic Python mutable-default-argument bug. Alignment scores accumulate across multiple score() calls instead of starting fresh each time, producing incorrect scores for any book processed after the first.

How it was fixed

Changed the signature to results=None and added the standard guard at the top of the function body:

def score(self, results=None):
    if results is None:
        results = {}

This creates a fresh dict on each call that doesn't supply one explicitly.


Files changed

  • python/lib/ptxprint/usfmerge.py

Bug reports

bugs/python/bug-18-self-assignment-noop/ and bugs/python/bug-19-mutable-default-arg/ (local only, not committed)

@megahirt megahirt added the Python This issue needs work on the Python code, no XeTeX involved. label May 1, 2026
@markpenny
Copy link
Copy Markdown
Collaborator

@mhosken and @davidg-sil Here's another one where it sounds like the fist is plausible, but I'd rather YOU make the call.

@mhosken
Copy link
Copy Markdown
Collaborator

mhosken commented May 11, 2026

Yes to bug #19, @davidg-sil for bug 18

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Python This issue needs work on the Python code, no XeTeX involved.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants