-
-
Notifications
You must be signed in to change notification settings - Fork 308
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
style: Fix raise-without-from-inside-except (B904) #4032
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -555,7 +555,7 @@ def __init__(self, cmd, *args, **kargs): | |||||
except OSError as e: | ||||||
print("OSError error({0}): {1}".format(e.errno, e.strerror)) | ||||||
str_err = "Error running: `%s --interface-description`." | ||||||
raise GrassError(str_err % self.name) | ||||||
raise GrassError(str_err % self.name) from e | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Maybe here, and in similar cases too? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The code around here is weird, we shouldn't be using print for this there. (It was out of scope of the PR, so I wasn't changing everything, in order to be reviewable). But None makes sense, as OSError is only to know what that we couldn't find the module (or fails), it's not the real exception. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can get PermissionError or FileNotFoundError which is likely a relevant info. The |
||||||
# get the xml of the module | ||||||
self.xml = get_cmd_xml.communicate()[0] | ||||||
# transform and parse the xml into an Element class: | ||||||
|
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -173,7 +173,9 @@ def safe_receive(self, message): | |||||
except (EOFError, OSError, FatalError) as e: | ||||||
# The pipe was closed by the checker thread because | ||||||
# the server process was killed | ||||||
raise FatalError("Exception raised: " + str(e) + " Message: " + message) | ||||||
raise FatalError( | ||||||
"Exception raised: " + str(e) + " Message: " + message | ||||||
) from e | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This one I'm not sure we'd want this, because there are multiple types of errors caught. So the traceback will be able to tell us which one is the direct cause of the others |
||||||
|
||||||
def stop(self): | ||||||
"""Stop the check thread, the libgis server and close the pipe | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -859,10 +859,10 @@ def connection(self): | |
return psycopg2.connect(db) | ||
except ImportError: | ||
er = "You need to install psycopg2 to connect with this table." | ||
raise ImportError(er) | ||
raise ImportError(er) from None | ||
else: | ||
str_err = "Driver is not supported yet, pleas use: sqlite or pg" | ||
raise TypeError(str_err) | ||
raise TypeError(str_err) from None | ||
Comment on lines
860
to
+865
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's always possible to use the feature if it is available and let it go if not supported. Or do a fallback of some sort, that adds the text to the message instead. |
||
|
||
def table(self): | ||
"""Return a Table object. | ||
|
@@ -1204,7 +1204,7 @@ def execute(self, sql_code=None, cursor=None, many=False, values=None): | |
"The SQL statement is not correct:\n%r,\n" | ||
"values: %r,\n" | ||
"SQL error: %s" % (sqlc, values, str(exc)) | ||
) | ||
) from exc | ||
|
||
def exist(self, cursor=None): | ||
"""Return True if the table already exist in the DB, False otherwise | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -867,8 +867,8 @@ def _parse_opts(lines): | |
break | ||
try: | ||
var, val = line.split(b"=", 1) | ||
except ValueError: | ||
raise SyntaxError("invalid output from g.parser: {}".format(line)) | ||
except ValueError as err: | ||
raise SyntaxError("invalid output from g.parser: {}".format(line)) from err | ||
echoix marked this conversation as resolved.
Show resolved
Hide resolved
|
||
try: | ||
var = decode(var) | ||
val = decode(val) | ||
|
@@ -877,7 +877,7 @@ def _parse_opts(lines): | |
"invalid output from g.parser ({error}): {line}".format( | ||
error=error, line=line | ||
) | ||
) | ||
) from error | ||
if var.startswith("flag_"): | ||
flags[var[5:]] = bool(int(val)) | ||
elif var.startswith("opt_"): | ||
|
@@ -1888,7 +1888,7 @@ def _set_location_description(path, location, text): | |
fd.write(os.linesep) | ||
fd.close() | ||
except OSError as e: | ||
raise ScriptError(repr(e)) | ||
raise ScriptError(repr(e)) from e | ||
|
||
|
||
def _create_location_xy(database, location): | ||
|
@@ -1939,7 +1939,7 @@ def _create_location_xy(database, location): | |
|
||
os.chdir(cur_dir) | ||
except OSError as e: | ||
raise ScriptError(repr(e)) | ||
raise ScriptError(repr(e)) from e | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This basically looks like a pointless try except, as the original Error is raised, so why using try-except here, if not the scripterror simplifies the error message presented to the user... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ScriptError seems to be the error which is raised by related functions, so I think is is for consistency. With that said, OSError seems like a fine exception to be raised from project creation functions (which would remove the need for try-except). |
||
|
||
|
||
# interface to g.version | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -502,7 +502,7 @@ def get_interface_description(cmd): | |
"Unable to fetch interface description for command '<{cmd}>'." | ||
"\n\nDetails: <{det}>" | ||
).format(cmd=cmd, det=e) | ||
) | ||
) from e | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here is another (of several) examples, where the original Error messages is included in the error message that is raised at the end. So, I guess we also here could replace the formating with exception chaining? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe this one with a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The motivation for these Perhaps |
||
|
||
desc = convert_xml_to_utf8(cmdout) | ||
return desc.replace( | ||
|
@@ -530,7 +530,7 @@ def parse_interface(name, parser=processTask, blackList=None): | |
_("Cannot parse interface description of<{name}> module: {error}").format( | ||
name=name, error=error | ||
) | ||
) | ||
) from error | ||
task = parser(tree, blackList=blackList).get_task() | ||
# if name from interface is different than the originally | ||
# provided name, then the provided name is likely a full path needed | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to understand the issue. Not sure if I got it completly right. My interpretation would be, that authors did not want excepting-chaining when they included the previous exception text with
format
in the raised exception...So
from e
would print e twice in this case, no? So probably,from None
is the right choice when the original Error message is included in the raised exception? Not sure when one should raise a python error instead of a eg. gs.fatal(())_ ... Maybe @wenzeslaus has some thoughts on exception chaining?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think exception chaining was used at all before. We're way more precise about our python usage now than when the python code started, so I'm pretty sure not everything was thoroughly though out before.
But for this case, maybe forgetting the
.format(e)
and using exception chaining for the details is in the same spririt?The spirit (as I understand), was to have better error messages that what python was giving at the time. Now, especially in the latests versions, have been giving better error messages, so it's less frightening and more useful. I think there's value of having the real errors showed up (as we will be having that in the logs in the issues the users will file), and we might want to have all that extra context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, it's only a hint for an import error. So that's kinda easy and has no real implications for the logic of the code itself. It's only a nice-to-have hint.
So, I'm suggesting exception chaining without the format(e) here, it will give something similar to the example of python docs:
giving (except for the line numbers and file names, I wrote it by hand):
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or maybe we'd want to use exception notes: https://docs.python.org/3/tutorial/errors.html#enriching-exceptions-with-notes?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both exception chaining and exception notes seem OK to me. However, I tend to think that all tracebacks also could be considered bugs, and when we catch an exception with try except, a simple (error-) message should be given that is addressed to the user and not to a developer who should be able to read traceback...
Maybe this discussion would make a good startingpoint to improve the exception-handling section in the style-guide?!?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As for dependencies, see also https://trac.osgeo.org/grass/ticket/2895
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Even then, that ticket is starting to get old, now everything should be in a pyproject.toml. For addons, I think each should be an individual "real" python package, each with a pyproject.toml, and could be fetched only for that subdirectory and work perfectly (and allow code tools to understand that structure).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making addons python packages is also the way QGIS is going, I think. However, we also have C-addons and addons using R...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't go with the TODO finally. Still working on the next PR, the unsafe fixes are a little longer to go through, and I have to do more manual tuning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@neteler do you have any opions/thoughts on raised exceptions, exception chaining vs. GRASS messages (fatal/error)?
See also my comment here: #4032 (comment)