Repository navigation
style: Fix raise-without-from-inside-except (B904) - #4032
Conversation
|
@ninsbl That's it for now, I don't have any more queued changes from the last week/weekends that are ready to be converted to a PR. I have one that needs to be applied after the pydispatch code synced from upstream, and another one that is maybe incomplete (it isn't done by ruff, it is manual from the actual pylint failures as ruff doesn't have that rule implemented yet, and since I never got to the point that the first pylint-related step in CI passes, I don't see if there's more. So, it'll take a couple days to have some more changes ready to review. |
9dd14c6 to
d1f0e0e
Compare
ninsbl
left a comment
There was a problem hiding this comment.
I think we touch upon some general questions here, where I would love to hear the opinion of e.g. @wenzeslaus or @petrasovaa ...
|
If we are to address each of these for the best solution in context, it would make sense to expand the scope of the PR and use exception notes where it makes more sense. |
Raising exceptions versus printing error messagesPrinting error messages is for command line tools. Python functions should generally raise exceptions. The Python code in tools (modules; both in core and addons) should generally print an error message calling Python functions in the library should raise exceptions to give the caller freedom to react to them in whatever way is appropriate. This is what is expected from a Python library as exceptions are the standard way in Python of reporting errors and a single function in a library does not have enough context to do anything more specific. Tracebacks may be perceived by users as errors and are less readable then plain error messages, so it is desirable for the command line tool to catch exceptions and print error messages instead. This may also allow for switch from Python and function oriented wording to one more appropriate for a command line tool. However, not all possible exceptions need to be caught. Unlikely and unexpected exceptions unrelated to the standard execution of the program, e.g., programmer's errors, are best left uncaught so that full information in a debugging-friendly format, i.e., traceback, is displayed. The situation in the GUI is similar, although the dividing line is harder to spot and the details would be for a separate discussion. This library-tool distinction gets complicated with how the library functions in the |
Rule contents: Within an `except` clause, raise exceptions with `raise ... from err` or `raise ... from None` to distinguish them from errors in exception handling
…rom-inside-except
|
I'm preparing an review-update here to finish things up, but it will depend on #7684. |
… clause The raise is in the function body, not in an except clause, so B904 does not apply to it. The 'from None' there would hide the original exception if connection() is called while another exception is being handled.
This raise appeared on main after this branch was created and is the only remaining B904 violation, so the rule cannot be enabled without it. The KeyError is an implementation detail of the lookup, so it is suppressed.
The last fallback branch only indexes and assigns strings, so it cannot raise ValueError and the handler was dead code re-raising an empty ValueError.
…tract The two adjacent handlers used 'err' and 'e' for the same kind of error. The rest of the file and g.extension use 'error'.
…rom-inside-except # Conflicts: # gui/wxpython/iscatt/plots.py # gui/wxpython/timeline/frame.py # gui/wxpython/tplot/frame.py # python/grass/gunittest/loader.py # python/grass/pygrass/vector/table.py # python/grass/utils/download.py
…rom-inside-except
|
There was a lot of related PRs to finalize this (in addition to the commits I made): Split out of this one (merged):
Minimal Python version (the exception notes above need Python 3.11):
|
echoix
left a comment
There was a problem hiding this comment.
@wenzeslaus I can't approve my own PR, so this is an approval of what's remaining
This PR I want a little bit more attentive review.
Ruff rule: https://docs.astral.sh/ruff/rules/raise-without-from-inside-except/
The flake8 bugbear README also refers to the exception chaining tutorial here: https://docs.python.org/3/tutorial/errors.html#exception-chaining, quite useful to understand what exception chaining is (and is a very good thing for an application like ours when exceptions are rethrown to have more useful details for a user instead of scaring them off).
I wasn't completely sure to understand the existing patterns of exceptions, especially on how they were used.
For the ImportError one, there is no problem, it's obvious that it's good now.
Places where we wouldn't want exception chaining, and also not having the "During handling of the above exception, another exception occurred:" message when raising another exception in an except, we can use the "from None" idiom, and only the exception raised (inside the except) will be shown. Like in the python docs:
Updated scope (by @wenzeslaus)
The branch has been merged up to current
main(not rebased, to keep the sharedhistory intact). The non-style changes that were originally part of this PR were
split into separate PRs so they could be reviewed and merged on their own; all
four are now merged:
exception.add_note()Python 2
e.messageleftover)Those merged versions supersede the corresponding hunks that were here. This PR
enables the
B904rule and adds thefrom ...chaining across the rest of thecodebase. Two hunks in files the split PRs only partly touched remain here:
from erroronextract_zip'sBadZipfileindownload.py, andfrom excintable.pyexecute().Exceptions are chained with
from errorto preserve the original, including inthe places where the original is also passed into the message of the new
exception, so the detail appears both in the message and in the chained
traceback; reducing that duplication can be done later.
from Noneis used wherethe original would be noise: where it is an implementation detail of the raising
code (for example,
IndexErrorbecomingStopIteration) or where the newexception gives a clearer replacement message (for example, an invalid value for
a database column). In wxGUI, the original error is passed into the new exception
and
from Noneis used, because that exception is caught immediately and only itsmessage is shown, so no traceback is produced.
The three behavior fixes from the original PR are retained: the rdigit
ScriptErrorfix, the removed unreachablereturnin the toolbars, and theremoved dead
try/exceptin__ll_parts(see the commit message).Commit message
Suggested squash commit message: