diff options
| author | Danilo M. <danix@danix.xyz> | 2026-10-21 10:03:31 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-10-21 10:03:31 +0200 |
| commit | 6b0f67655b40fef221f9c84cfb6c0bcedf34e3bb (patch) | |
| tree | b4f66f2e0f327d9da5c40566518225606a517135 | |
| parent | 8a63cde8346c6ff30dd79cf7862e28ce6dff56fc (diff) | |
| download | sb-builder-6b0f67655b40fef221f9c84cfb6c0bcedf34e3bb.tar.gz sb-builder-6b0f67655b40fef221f9c84cfb6c0bcedf34e3bb.zip | |
Repository, Packages: no stale detail pane; scan survives races
A reload or filter that drops the current row left its pane and link up,
and the link then crashed on current() being None (Packages had the same).
A .txz slackrepo replaces between glob and stat is skipped, and any
exception in the scan thread reaches the stats line instead of leaving it
at loading.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| -rw-r--r-- | sb_builder/packages.py | 5 | ||||
| -rw-r--r-- | sb_builder/repository.py | 10 | ||||
| -rw-r--r-- | tests/test_gui.py | 5 |
3 files changed, 15 insertions, 5 deletions
diff --git a/sb_builder/packages.py b/sb_builder/packages.py index 60e0425..a3d7126 100644 --- a/sb_builder/packages.py +++ b/sb_builder/packages.py @@ -50,7 +50,7 @@ def vercmp(a, b): letter. Past the shorter version, a pre-release word makes the longer one older (1.0rc1 < 1.0), anything else newer (1.0.1, 4.3.5b > their base). """ - # ponytail: single-letter pre-releases (1.0b1) count as newer than 1.0; + # ponytail: single-letter pre-releases (1.0b1) and 1.0.0 count as newer than 1.0; # snapshots (20240101_ab12cd) order by date, their hash part arbitrarily ra, rb = _runs(a), _runs(b) for x, y in zip(ra, rb): @@ -364,7 +364,7 @@ class PackagesTab(QWidget): self.count.setText(f"inventory failed: {err}") return self.model.set_rows(load_rows(data)) - self.update_count() + self.refilter() def filter_to(self, name): """Show only one chip's packages (from a Dashboard click).""" @@ -379,6 +379,7 @@ class PackagesTab(QWidget): self.proxy.update(self.query.text(), on - set(STATUSES) - {"orphaned"}, on & set(STATUSES), "orphaned" in on) self.update_count() + self.show_detail(self.table.currentIndex(), None) # a reset or filter can drop it def update_count(self): self.count.setText(f"{self.proxy.rowCount():,} shown · {len(self.model.checked)} selected") diff --git a/sb_builder/repository.py b/sb_builder/repository.py index 29f4291..38c6ecc 100644 --- a/sb_builder/repository.py +++ b/sb_builder/repository.py @@ -106,7 +106,10 @@ def scan(path): m = PKGFILE.match(os.path.basename(f)) if not m: continue - st = os.stat(f) + try: + st = os.stat(f) + except FileNotFoundError: + continue # slackrepo replaced it since the glob item = os.path.relpath(os.path.dirname(f), path) # category/pkg info = meta.get(os.path.basename(f)) rows.append({**m.groupdict(), "category": item.split("/")[0], "file": f, @@ -283,7 +286,7 @@ class RepositoryTab(QWidget): def work(): try: self.scanned.emit(gen, scan(path), "") - except OSError as e: + except Exception as e: # the thread's boundary: never stuck at loading self.scanned.emit(gen, None, str(e)) threading.Thread(target=work, daemon=True).start() @@ -292,12 +295,13 @@ class RepositoryTab(QWidget): return # an older Refresh, overtaken self.model.set_rows(data["rows"] if data else []) self.stats.setText(err or summary(data)) - self.update_count() + self.refilter() def refilter(self): on = {n for n, b in self.chips.items() if b.isChecked()} self.proxy.update(self.query.text(), on - {"unpublished"}, "unpublished" in on) self.update_count() + self.show_detail(self.table.currentIndex(), None) # a reset or filter can drop it def update_count(self): self.count.setText(f"{self.proxy.rowCount():,} shown") diff --git a/tests/test_gui.py b/tests/test_gui.py index 7d0b7dd..a12efdd 100644 --- a/tests/test_gui.py +++ b/tests/test_gui.py @@ -655,6 +655,11 @@ class SmokeTest(unittest.TestCase): tab.link.linkActivated.emit("#") self.assertIs(w.tabs.currentWidget(), w.packages_tab) self.assertEqual(w.packages_tab.query.text(), "foo") + tab.table.setCurrentIndex(tab.proxy.index(0, 0)) + tab.reload() # the reset drops the current row: no stale pane or link + self.wait(lambda: tab.stats.text() != "loading…") + self.assertTrue(tab.link.isHidden()) + self.assertEqual(tab.detail.text(), "") def test_repository_missing(self): w, _remote = self.window() |
