-
Notifications
You must be signed in to change notification settings - Fork 58
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
Improve Error Handling in TreeTime #206
Conversation
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.
Still a bit confused what the error hierarchy is supposed to be :)
@@ -9,13 +12,17 @@ class MissingDataError(TreeTimeError): | |||
pass | |||
|
|||
class UnknownMethodError(TreeTimeError): | |||
"""MissingDataError class raised when tree or alignment are missing""" | |||
"""MissingDataError class raised when an unknown method is called""" |
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.
Are methods added dynamically? In that case it may be helpful to explain this.
"alignment are missing" sounds like this error would be raised if you haven't loaded an alignment but call a method that gets added only during alignment
@@ -9,13 +12,17 @@ class MissingDataError(TreeTimeError): | |||
pass |
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 parameters are missing? If I understand usage below correctly?
@@ -77,7 +77,7 @@ def __init__(self, *args, dates=None, debug=False, real_dates=True, precision_ff | |||
""" | |||
super(ClockTree, self).__init__(*args, **kwargs) | |||
if dates is None: | |||
raise ValueError("ClockTree requires date constraints!") | |||
raise MissingDataError("ClockTree requires date constraints!") |
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.
If this is how you want to use MissingDataError one should probably change the description of the MissingDataError to encompass missing parameters
@@ -203,7 +203,7 @@ def _set_precision_fft(self, precision_fft, precision_branch='auto'): | |||
" fft_grid_points=%.3e"%(precision_fft), 2) | |||
self.fft_grid_size = precision_fft | |||
elif precision_fft!='auto': | |||
raise ValueError(f"ClockTree: precision_fft needs to be either 'auto' or an integer, got '{precision_fft}'.") | |||
raise UnknownMethodError(f"ClockTree: precision_fft needs to be either 'auto' or an integer, got '{precision_fft}'.") |
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.
This should be a new type of error? Something like: Wrong parameter passed - not unknown method. This has nothing to do with a method?
raise ValueError("Error in computing exp(Q * t): Q has positive eigenvalues or the branch length t \n" | ||
"is too large. This is most likely caused by incorrect input data. If this error persists \n" | ||
"please let us know by filing an issue at: https://github.com/neherlab/treetime/issues") | ||
raise ValueError("Error in computing exp(Q * t): Q has positive eigenvalues or the branch length t is too large. " |
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.
Isn't this the type of error that should be wrapped by Augur? If so, it should maybe be "InputDataError"? Or is this what you use ValueError for now? Though I'd think ValueError would need to be TreetimeError - or is this supposed to be a "TreetimeOtherError"?
Co-authored-by: Cornelius Roemer <[email protected]>
Co-authored-by: Cornelius Roemer <[email protected]>
@@ -51,7 +51,29 @@ def __init__(self, *args,**kwargs): | |||
super(TreeTime, self).__init__(*args, **kwargs) | |||
|
|||
|
|||
def run(self, root=None, infer_gtr=True, relaxed_clock=None, n_iqd = None, | |||
def run(self, augur=False, **kwargs): |
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.
@victorlin Hi, I modified how the run
function is called to make sure that the Errors get printed at the correct location when running in TreeTime
and in augur
- however @corneliusroemer mentioned that using the argument augur=true
is not the best coding practice - is there a better way to do this? Perhaps we could use @rneher raise_uncaught_exception = true
instead as an argument to make it less specific and then add some commentary? Thanks for any input :-)
For example, for "unknown" errors (which will get thrown as TreeTimeOtherErrors
), the error traceback in augur
(PR: nextstrain/augur#1033) is as follows, where a warning message with information how to report issues comes after the full traceback:
For "known" TreeTimeErrors they will get thrown as follows (with information after the traceback):
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 agree, naming this parameter as augur
seems too specific – it's possible that other Python scripts would want to use this too. raise_uncaught_exceptions
(plural) is nice self-describing name.
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.
Post-merge requested changes
pass | ||
|
||
class NotReadyError(TreeTimeError): | ||
"""NotReadyError class raised when results are requested before inference""" | ||
pass | ||
|
||
class TreeTimeOtherError(Exception): |
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.
"Other" seems a bit ambiguous... "other" than what?
class TreeTimeOtherError(Exception): | |
class TreeTimeUnknownError(Exception): |
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 like unknown
@@ -51,7 +51,29 @@ def __init__(self, *args,**kwargs): | |||
super(TreeTime, self).__init__(*args, **kwargs) | |||
|
|||
|
|||
def run(self, root=None, infer_gtr=True, relaxed_clock=None, n_iqd = None, | |||
def run(self, augur=False, **kwargs): |
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 agree, naming this parameter as augur
seems too specific – it's possible that other Python scripts would want to use this too. raise_uncaught_exceptions
(plural) is nice self-describing name.
print("ERROR in TreeTime.run: An error has occurred which is not properly handled in TreeTime. If this error persists, please let us know " | ||
"by filing a new issue including the original command and the error above at: https://github.com/neherlab/treetime/issues \n", file=sys.stderr) |
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.
Minor grammar:
print("ERROR in TreeTime.run: An error has occurred which is not properly handled in TreeTime. If this error persists, please let us know " | |
"by filing a new issue including the original command and the error above at: https://github.com/neherlab/treetime/issues \n", file=sys.stderr) | |
print("ERROR in TreeTime.run: An error occurred which was not properly handled in TreeTime. If this error persists, please let us know " | |
"by filing a new issue including the original command and the error above at: https://github.com/neherlab/treetime/issues \n", file=sys.stderr) |
except TreeTimeError as err: | ||
print(f"ERROR: {err} \n", file=sys.stderr) | ||
if augur: | ||
raise err | ||
else: | ||
sys.exit(2) |
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.
No need to print the error if it is being raised:
except TreeTimeError as err: | |
print(f"ERROR: {err} \n", file=sys.stderr) | |
if augur: | |
raise err | |
else: | |
sys.exit(2) | |
except TreeTimeError as err: | |
if augur: | |
raise err | |
else: | |
print(f"ERROR: {err} \n", file=sys.stderr) | |
sys.exit(2) |
sys.exit(2) | ||
except BaseException as err: | ||
import traceback | ||
print(traceback.format_exc()) |
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.
Traceback should be printed to stderr to as well:
print(traceback.format_exc()) | |
print(traceback.format_exc(), file=sys.stderr) |
@victorlin thanks so much for your suggestions - I have added them to another branch https://github.com/neherlab/treetime/tree/feat/victor-error-handling - will do a PR after checking they integrate with your suggestions in augur :-) |
Related Issue and PR:
nextstrain/augur#1032
nextstrain/augur#1033
Building on @victorlin's suggestions in : 44e7c56...2632d96 I have made a couple minor changes to TreeTime's error handling process:
MissingDataError
,UnknownMethodError
andNotReadyError
TreeTimeError
class for errors that might be due to input data not matching with assumptions or unknown behavior (potential bug):TreeTimeOtherError
.treetime.run
in a wrapper to catch all errors with notification on how to report issues. i.e.ERROR in TreeTime.run: An error has occurred which is not properly handled in TreeTime. If this error persists, please let us know " "by filing a new issue including the original command and the error above at: https://github.com/neherlab/treetime/issues
augur
return all errors asTreeTimeError
s but with distinction between input/usage errors and potential treetime bugs (these are output asTreeTimeOtherError
s). On standard run print traceback for unknown errors / potential bugs, print warning and then exit treetime (avoid double printing of traceback or traceback after final warning).Example for unknown
ValueError
:Example for known
MissingDataError
:I used the same example as in the PR above to create an error. I made two test cases, one where the error is called a
ValueError
which should be handled as aTreeTimeOtherError
and another where the error is called aMissingDataError
.Testing
ValueError
or aTreeTimeError
), I added a line in_exp_lt
(pip install .
)treetime.run()
in a test script where it is not in atry
-catch
statement, for example: runpython test/treetime_algorithms.py