fix(ui): erros de parametro do detector voltam a chegar ao usuario - #465
Merged
Conversation
Clicking "Apply" in the detector panel with an out-of-range value did nothing at all: no dialog, no status line. The panel caught pydantic's ValidationError, while DetectorSetupCoordinator raised DetectorSetupCoordinatorError — at the time a bare Exception subclass. Unrelated types, so the except never matched, and nothing else caught it either. A census of coordinators/ found 31 raise sites and zero handlers in src/; all of them escaped into Tk's default report_callback_exception, which writes a traceback to a stderr the packaged app does not have. One hierarchy. CoordinatorError now derives from ZebTrackError and DetectorSetupCoordinatorError from CoordinatorError, so `except ZebTrackError` at a UI boundary catches any application failure. One ValidationError. zebtrack/exceptions.py redefined the entire hierarchy that zebtrack/core/exceptions.py already declared, including a second ValidationError used by nobody. Two same-named classes make `except` on one silently miss the other — the same bug in miniature. The module is now a re-export shim; core/exceptions.py is canonical. Two kinds of failure, two types. A value out of range raises ValidationError, whose message is written for the researcher and rendered verbatim (hence `_()` and a pt_BR pair). Anything else keeps DetectorSetupCoordinatorError, whose message names services and plugins and belongs in the log, answered by a generic dialog. Collapsing the two is what left the panel unable to answer either. The three call sites of hardware_vm.update_detector_parameters (model_diagnostics_panel, event_dispatcher, gui.py) carry the same two-clause boundary. ui/tk_exception_handler installs a replacement for Tk's report_callback_exception that logs ui.callback.unhandled via structlog and shows a dialog, wired in app_runner.run_app() right after the root window exists. It is a net, not a boundary: every entry it logs is a bug report against a call site. UIStateController.update_detector_parameters is deleted rather than repaired — nothing in src/ called it, and the coordinator consumed the ValueError before its `except ValueError` could fire. Unreachable twice over, while making the flow look covered in a grep. project_model_configuration_panel had a milder version of the same and now catches ZebTrackError. Out of scope, still open: track_buffer has three disagreeing bounds (settings.py ge=10 le=1000, coordinator >= 0, service >= 1). Picking one is a domain decision; the bounds are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a UI regression where invalid detector parameter values (e.g., out-of-range thresholds) could fail silently by ensuring all coordinator failures participate in a single application exception hierarchy and by adding explicit UI “error boundaries” (plus a Tk callback safety net) so errors reliably reach the user and/or logs.
Changes:
- Unifies exception handling by making
CoordinatorError(andDetectorSetupCoordinatorError) derive fromZebTrackError, and makingzebtrack.exceptionsa pure re-export shim ofzebtrack.core.exceptions. - Restores user-visible feedback for rejected detector parameters across the three relevant UI call sites, distinguishing user input (
ValidationError) from operational failures (DetectorSetupCoordinatorError/ otherZebTrackError). - Adds a last-resort Tk callback exception handler (logs + dialog) plus targeted tests and i18n catalog updates for the new UI-visible strings.
Reviewed changes
Copilot reviewed 18 out of 19 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/ui/test_tk_exception_handler.py | Tests the Tk callback safety net (logs + dialog, never re-raises). |
| tests/ui/test_detector_parameter_ui_boundary.py | Pins down the three UI boundaries so rejected params always show a dialog and never escape to Tk. |
| tests/test_detector_parameter_error_boundary.py | Verifies exception hierarchy unification and the ValidationError vs operational-failure split at the coordinator boundary. |
| src/zebtrack/ui/tk_exception_handler.py | Implements the Tk report_callback_exception replacement safety net. |
| src/zebtrack/ui/gui.py | Adds a boundary to the legacy _on_apply_roi_settings stub so it can’t fail silently. |
| src/zebtrack/ui/components/project_model_configuration_panel.py | Fixes an unreachable handler by catching ZebTrackError (not pydantic’s ValidationError) and logging failures. |
| src/zebtrack/ui/components/model_diagnostics_panel.py | Adds the two-clause boundary (ValidationError vs other ZebTrackError) for the Apply button flow. |
| src/zebtrack/ui/components/event_dispatcher.py | Adds the same boundary inside the DETECTOR_UPDATE_PARAMETERS subscriber, routing failures to UI_SHOW_ERROR. |
| src/zebtrack/locales/zebtrack.pot | Updates msgids/source references for the new UI-visible strings. |
| src/zebtrack/locales/pt_BR/LC_MESSAGES/zebtrack.po | Adds pt_BR translations for the new UI-visible strings. |
| src/zebtrack/locales/_pairs/pr4-error-boundary.json | Adds translation pairs for the new boundary/net strings. |
| src/zebtrack/exceptions.py | Converts zebtrack.exceptions into a re-export shim to prevent duplicate same-named exception classes. |
| src/zebtrack/core/app_runner.py | Installs the Tk callback exception handler immediately after root creation. |
| src/zebtrack/coordinators/ui_state_coordinator.py | Removes the dead/unreachable update_detector_parameters boundary and documents the rationale. |
| src/zebtrack/coordinators/detector_setup_coordinator.py | Makes coordinator errors catchable (DetectorSetupCoordinatorError derives from CoordinatorError) and raises user-facing ValidationError for rejected inputs. |
| src/zebtrack/coordinators/base_coordinator.py | Makes CoordinatorError derive from ZebTrackError to unify catchability at UI boundaries. |
| docs/reference/system_integration.md | Documents the error-boundary model (one hierarchy, two failure types) and the Tk safety net. |
| CHANGELOG.md | Adds an Unreleased entry describing the bug, fix strategy, and remaining out-of-scope domain decision. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This was referenced Aug 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
O bug
Clicar "Aplicar" no painel do detector com um valor fora da faixa não fazia nada: sem diálogo, sem linha de status. O painel capturava o
ValidationErrordo pydantic; oDetectorSetupCoordinatorlevantavaDetectorSetupCoordinatorError, na época subclasse deExceptionpura. Tipos sem parentesco — oexceptnunca casava, e nada mais capturava.Um censo de
coordinators/achou 31 pontos deraisee zero handlers emsrc/. Todos escapavam para oreport_callback_exceptionpadrão do Tk, que escreve num stderr que o app empacotado não tem.A correção
Uma hierarquia.
CoordinatorErrorpassa a herdar deZebTrackError, eDetectorSetupCoordinatorErrordeCoordinatorError— era o único erro de coordinator ainda derivando direto deException.except ZebTrackErrornuma fronteira de UI agora pega qualquer falha da aplicação.Um
ValidationError.zebtrack/exceptions.pyredefinia a hierarquia inteira quezebtrack/core/exceptions.pyjá declarava — inclusive uma segunda classeValidationError, usada por ninguém. Duas classes de mesmo nome fazem oexceptde uma ignorar a outra em silêncio: o mesmo bug em miniatura. O módulo virou shim de reexportação;core/exceptions.pyé o canônico.Duas naturezas de falha, dois tipos — é a parte a preservar ao adicionar fronteiras em outros lugares:
ValidationErrorstr(exc)literal — por isso agora com_()e par pt_BRDetectorSetupCoordinatorErrorTrês call sites, três fronteiras:
model_diagnostics_panel,event_dispatcher(assinante deDETECTOR_UPDATE_PARAMETERS, responde comUI_SHOW_ERROR) egui.py.Rede de segurança:
ui/tk_exception_handlersubstitui oreport_callback_exceptiondo Tk por um handler que registraui.callback.unhandledvia structlog e mostra diálogo. Instalado emapp_runner.run_app()logo após a criação da raiz. É rede, não fronteira: cada ocorrência no log é um relatório de bug contra um call site.Handler que não pode disparar é pior que handler nenhum.
UIStateController.update_detector_parametersembrulhava o coordinator emexcept ValueError→UI_SHOW_ERROR; nada emsrc/o chamava, e o coordinator já consumia oValueErrorantes. Inalcançável em dobro, e ainda fazia o fluxo parecer coberto num grep. Removido em vez de consertado.Fora de escopo, ainda aberto
track_buffertem três faixas discordantes:settings.py(ge=10, le=1000),DetectorSetupCoordinator(>= 0),DetectorService(>= 1). Reconfirmei que continuam divergentes após #463 — aquele PR corrigiu só o tipo do erro, não as faixas. Escolher uma é decisão de domínio; os limites ficaram intactos.Verificação
ruff check+ruff format --check— limposmypy .— limpo (702 arquivos)pytest -q— 4222 passed, 5 skipped (+20 novos)pytest -m gui -n0 -q— 1087 passed, 1 skipped (+8 novos).poe o.moestá compilado — verificado resolvendo cada uma viagettextem runtimedocs/reference/system_integration.md§5.13🤖 Generated with Claude Code