Skip to content

Only swallow format errors while probing a database string - #827

Merged
andlaus merged 1 commit into
cantools:masterfrom
Poseidonas:narrow-format-probing-exceptions
Oct 22, 2026
Merged

andlaus merged 1 commit into
cantools:masterfrom
Poseidonas:narrow-format-probing-exceptions

Conversation

@Poseidonas

Copy link
Copy Markdown
Contributor

Fixes #814.

load_string() probes each supported format in turn and keeps the failure from each one so that it can report all five reasons together if none of them works. Every attempt was wrapped in a bare except Exception, so an error that says nothing about the format was recorded as if it did.

The reporter's case, reproduced unchanged on c1dcd39:

cantools.database.errors.UnsupportedDatabaseFormatError: DBC: "[Errno 1] Incorrect function"

The file parses fine. The OSError comes from a logging handler that fails while cantools emits its own "Overwriting message ..." warning, and the traceback pointing at it is lost.

After the change the same script raises the OSError itself, with the handler still in the traceback.

Which exceptions actually mean "wrong format"

Rather than guess, I fed every parser each of the other formats plus junk and an empty string, and recorded what came out — 30 combinations:

exception occurrences
ElementTree.ParseError 12
cantools.errors.Error 6
textparser.ParseError 5
ValueError 2
KeyError 2

The last two are wider than I would like. They come from three places that reject a wrong root tag with a plain builtin:

  • arxml/__init__.py:57 — ValueError("No XML namespace specified or illegal root tag name ...")
  • kcd.py:475 — ValueError("Expected root element tag ...")
  • cdd.py:244 — KeyError("Could not find ECUDOC root element!")

Making those raise ParseError would be tidier and would let the probe narrow to cantools' own errors plus the two parser ones. I left it alone because those exceptions are visible to anyone calling add_arxml_string() and friends directly, and several tests assert on them — that seemed like a separate decision rather than something to fold into a bugfix. Happy to do it if you would prefer that shape.

Verification

  • Auto-detection unchanged: loaded all 131 database files under tests/ with no explicit format, before and after. Same outcome for every file, including the 12 that already failed.
  • Junk, empty string and binary input still raise UnsupportedDatabaseFormatError.
  • Two tests added. The first fails on master with UnsupportedDatabaseFormatError and passes with the change; the second guards the other direction, that a genuinely bad format is still reported as one.
  • ruff and mypy clean. Suite goes 415 → 417 passing. test_cache_env_var fails on my machine both before and after — unrelated to this change.

@andlaus

andlaus commented Aug 26, 2026

Copy link
Copy Markdown
Member

Before I have a deeper look at this, I would like to see all format parsers to only raise cantools.errors.ParseError if it could not be loaded (separate PR for this please).

@Poseidonas

Copy link
Copy Markdown
Contributor Author

Makes sense — with every loader raising ParseError, the probe only has one thing to catch. I looked at the current state: DBC already does (10 of 12 sites), KCD has 1 ValueError, SYM 3, ARXML about 26 across the system and ECU-extract loaders, plus the textparser/ElementTree errors that escape the tokenisers. I'll open that as a separate PR first — each loader raising ParseError with the same message it raises today — and rebase #827 on it. One question: should an ARXML variant the loader does not support (today NotImplementedError) also become ParseError, or stay as it is since it is not a parse failure?

@andlaus

andlaus commented Aug 26, 2026

Copy link
Copy Markdown
Member

should an ARXML variant the loader does not support (today NotImplementedError) also become ParseError, or stay as it is since it is not a parse failure?

it should stay a NotImplementedError: the file format could be loaded and partially understood (version could be recognized). This is a semantic difference compared to the file not adhering to the format's syntax.

@andlaus

andlaus commented Oct 21, 2026

Copy link
Copy Markdown
Member

I'm too much of a coward to merge this in its current form: I would like to see a additional "catchall" handlers which prints a warning message featuring a traceback instructing people to open a bug report for cantools which includes said traceback.

@Poseidonas
Poseidonas force-pushed the narrow-format-probing-exceptions branch from a29369d to 5865d98 Compare October 21, 2026 10:02
Comment thread tests/test_database.py
def emit(self, record):
raise OSError(5, 'Input/output error')

logger = logging.getLogger('cantools')

@andlaus andlaus Oct 21, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a comment of how the "unexpected" exception is triggered would be good. AFAICS, you "misappropriate" logging.warn() to raise an OSError instead of logging? (it is a neat kludge, but it should IMO be explained in the code.)

@andlaus

andlaus commented Oct 21, 2026

Copy link
Copy Markdown
Member

except for the missing comment in the unit test this is good to go IMO

@Poseidonas

Copy link
Copy Markdown
Contributor Author

Added, thanks. Each probe keeps its except Exception, so nothing it catches today escapes — the handler now emits a warning first, carrying the traceback and asking for an issue:

probing for dbc raised OSError, which does not say whether the string is in
that format. The string was reported as an unsupported database instead.
Please open an issue at https://github.com/cantools/cantools/issues with the
traceback below.

The diff is additive, 104 lines and no deletions, so a parser raising something the probe does not recognise behaves exactly as it does on master and is merely visible now. That also inverts what the PR did before: the unforeseen exception no longer reaches the caller. I read your request that way because the risk worth covering is _FORMAT_PROBE_ERRORS being incomplete. If you meant the warning alongside letting it through, say so and I will re-raise after warning instead.

443 tests pass, ruff and mypy clean. The test for an unrelated error now asserts the warning rather than the exception, and a second one asserts that a string which is simply not a database warns about nothing. Both fail without the handler. traceback.format_exception(exc) is checked on 3.10, the oldest version the project supports.

The branch was 81 commits behind, so it is rebased on master.

@andlaus

andlaus commented Oct 22, 2026

Copy link
Copy Markdown
Member

@Poseidonas: can you add a comment in the unit test explaining how the "unforeseen" exception gets triggered?

load_string() tries each supported format in turn and remembers why every
one of them failed, so that it can report all five reasons together. Each
attempt was wrapped in a bare `except Exception`, which also caught errors
that say nothing about the format: cantools#814 reports an OSError raised by a
logging handler while cantools emits a warning of its own, surfacing as
`UnsupportedDatabaseFormatError: DBC: "[Errno 1] Incorrect function"` on a
file that parses fine.

Each probe now recognises what a parser raises when the content is not in
its format. That set was established by feeding each parser every other
format plus junk and an empty string, and recording what came out:
cantools' own Error, textparser.ParseError and ElementTree.ParseError, plus
ValueError and KeyError, which arxml, kcd and cdd use for a wrong root tag.
The last two are wider than one would like, but they are what those parsers
raise today, and narrowing them further would change exceptions callers can
already see.

Anything else keeps the behaviour it has today, reaching the combined
UnsupportedDatabaseFormatError, and additionally emits a warning carrying
the traceback and asking for a bug report. A parser raising something
unforeseen is then visible instead of being reported as a bad format.

Auto-detection is unaffected: loading all 131 database files under tests/
without an explicit format gives the same result for every one of them.

Signed-off-by: George Vasiliades <1221374+Poseidonas@users.noreply.github.com>
@Poseidonas
Poseidonas force-pushed the narrow-format-probing-exceptions branch from 5865d98 to a22278b Compare October 22, 2026 08:20
@Poseidonas

Copy link
Copy Markdown
Contributor Author

Added.

@andlaus

andlaus commented Oct 22, 2026

Copy link
Copy Markdown
Member

looks good now, merging. Thanks for the contribution!

@andlaus
andlaus merged commit a417da4 into cantools:master Oct 22, 2026
20 checks passed
Poseidonas added a commit to Poseidonas/cantools that referenced this pull request Oct 22, 2026
The dbc loader moved into a package and gained two checks for leftover
VAL_ and SIG_VALTYPE_ entries which raise ValueError; they now raise
ParseError like the rest. load_string() keeps the probe warning that
came with cantools#827 rather than logging, so the tests for the three probe
outcomes assert on the warning.

Signed-off-by: George Vasiliades <1221374+Poseidonas@users.noreply.github.com>
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.

Logging handler errors during load are mislabeled as "UnsupportedDatabaseFormatError: DBC: ..."

2 participants