Skip to content
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

Merged
merged 4 commits into from
Sep 7, 2022
Merged

Conversation

anna-parker
Copy link
Contributor

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:

  • more consistent labeling of error classes: rename errors due to incorrect input data or incorrect use of treetime as one of the three predefined TreeTime error classes: MissingDataError, UnknownMethodError and NotReadyError
  • add new TreeTimeError class for errors that might be due to input data not matching with assumptions or unknown behavior (potential bug): TreeTimeOtherError.
  • call 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
  • when run from another application, such as augur return all errors as TreeTimeErrors but with distinction between input/usage errors and potential treetime bugs (these are output as TreeTimeOtherErrors). 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:

image

Example for known MissingDataError:

image

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 a TreeTimeOtherError and another where the error is called a MissingDataError.

Testing

  • install augur and treetime in local environment
  • cause an Exception in treetime (either call this a ValueError or a TreeTimeError), I added a line in _exp_lt
        """
        Parameters
        ----------

         t : float
            time to propagate

        Returns
        --------

         exp_lt : numpy.array
            Array of values exp(lambda(i) * t),
            where (i) - alphabet index (the eigenvalue number).
        """
        log_val = self.mu * t * self.eigenvals
        log_val = np.ones(len(log_val))*750 ##cause an exception
        if any(i > 10 for i in log_val):
            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.")

        return np.exp(log_val)
  • install modified treetime in local environment (pip install .)
  • call treetime.run() in a test script where it is not in a try - catch statement, for example: run python test/treetime_algorithms.py

Copy link
Member

@corneliusroemer corneliusroemer left a 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 :)

treetime/__init__.py Outdated Show resolved Hide resolved
@@ -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"""
Copy link
Member

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

treetime/__init__.py Outdated Show resolved Hide resolved
@@ -9,13 +12,17 @@ class MissingDataError(TreeTimeError):
pass
Copy link
Member

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!")
Copy link
Member

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}'.")
Copy link
Member

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. "
Copy link
Member

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"?

rneher and others added 2 commits September 7, 2022 09:48
Co-authored-by: Cornelius Roemer <[email protected]>
Co-authored-by: Cornelius Roemer <[email protected]>
@rneher rneher merged commit 0848fdf into master Sep 7, 2022
@@ -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):
Copy link
Contributor Author

@anna-parker anna-parker Sep 7, 2022

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:
image

For "known" TreeTimeErrors they will get thrown as follows (with information after the traceback):
image

Copy link
Contributor

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.

Copy link
Contributor

@victorlin victorlin left a 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):
Copy link
Contributor

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?

Suggested change
class TreeTimeOtherError(Exception):
class TreeTimeUnknownError(Exception):

Copy link
Member

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):
Copy link
Contributor

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.

Comment on lines +68 to +69
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)
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor grammar:

Suggested change
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)

Comment on lines +58 to +63
except TreeTimeError as err:
print(f"ERROR: {err} \n", file=sys.stderr)
if augur:
raise err
else:
sys.exit(2)
Copy link
Contributor

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:

Suggested change
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())
Copy link
Contributor

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:

Suggested change
print(traceback.format_exc())
print(traceback.format_exc(), file=sys.stderr)

@anna-parker
Copy link
Contributor Author

@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 :-)

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.

4 participants