-
Notifications
You must be signed in to change notification settings - Fork 300
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
Clean up scopes and exception handling for new tasks #543
Changes from all commits
147920a
8ef3d14
34e0fb5
8105b91
093e28e
0d24acd
1fa6920
df302f8
08db5a4
5588c00
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 |
---|---|---|
@@ -1,7 +1,6 @@ | ||
from sys import exc_info as _exc_info | ||
from traceback import format_tb as _format_tb | ||
|
||
from six import reraise as _reraise | ||
from wrapt import decorator as _decorator | ||
|
||
from flytekit.common.exceptions import base as _base_exceptions | ||
|
@@ -77,7 +76,7 @@ def error_code(self): | |
return "{}:Unknown".format(self._context) | ||
|
||
@property | ||
def kind(self): | ||
def kind(self) -> int: | ||
""" | ||
:rtype: int | ||
""" | ||
|
@@ -138,45 +137,51 @@ def _is_base_context(): | |
@_decorator | ||
def system_entry_point(wrapped, instance, args, kwargs): | ||
""" | ||
Decorator for wrapping functions that enter a system context. This should decorate every method a user might | ||
call. This will allow us to add differentiation between what is a user error and what is a system failure. | ||
Furthermore, we will clean the exception trace so as to make more sense to the user--allowing them to know if they | ||
should take action themselves or pass on to the platform owners. We will dispatch metrics and such appropriately. | ||
The reason these two (see the user one below) decorators exist is to categorize non-Flyte exceptions at arbitrary | ||
locations. For example, while there is a separate ecosystem of Flyte-defined user and system exceptions | ||
(see the FlyteException hierarchy), and we can easily understand and categorize those, if flytekit comes upon | ||
a random ``ValueError`` or other non-flytekit defined error, how would we know if it was a bug in flytekit versus an | ||
error with user code or something the user called? The purpose of these decorators is to categorize those (see | ||
the last case in the nested try/catch below. | ||
|
||
Decorator for wrapping functions that enter a system context. This should decorate every method that may invoke some | ||
user code later on down the line. This will allow us to add differentiation between what is a user error and | ||
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.
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. See comment above. |
||
what is a system failure. Furthermore, we will clean the exception trace so as to make more sense to the | ||
user -- allowing them to know if they should take action themselves or pass on to the platform owners. | ||
We will dispatch metrics and such appropriately. | ||
""" | ||
try: | ||
_CONTEXT_STACK.append(_SYSTEM_CONTEXT) | ||
if _is_base_context(): | ||
# If this is the first time either of this decorator, or the one below is called, then we unwrap the | ||
# exception. The first time these decorators are used is currently in the entrypoint.py file. The scoped | ||
# exceptions are unwrapped because at that point, we want to return the underlying error to the user. | ||
try: | ||
return wrapped(*args, **kwargs) | ||
except FlyteScopedException as ex: | ||
_reraise(ex.type, ex.value, ex.traceback) | ||
raise ex.value | ||
else: | ||
try: | ||
return wrapped(*args, **kwargs) | ||
except FlyteScopedException: | ||
# Just pass-on the exception that is already wrapped and scoped | ||
_reraise(*_exc_info()) | ||
except FlyteScopedException as scoped: | ||
raise scoped | ||
except _user_exceptions.FlyteUserException: | ||
# Re-raise from here. | ||
_reraise( | ||
FlyteScopedUserException, | ||
FlyteScopedUserException(*_exc_info()), | ||
_exc_info()[2], | ||
) | ||
raise FlyteScopedUserException(*_exc_info()) | ||
except Exception: | ||
# This is why this function exists - arbitrary exceptions that we don't know what to do with are | ||
# interpreted as system errors. | ||
# System error, raise full stack-trace all the way up the chain. | ||
_reraise( | ||
FlyteScopedSystemException, | ||
FlyteScopedSystemException(*_exc_info(), kind=_error_model.ContainerError.Kind.RECOVERABLE), | ||
_exc_info()[2], | ||
) | ||
raise FlyteScopedSystemException(*_exc_info(), kind=_error_model.ContainerError.Kind.RECOVERABLE) | ||
finally: | ||
_CONTEXT_STACK.pop() | ||
|
||
|
||
@_decorator | ||
def user_entry_point(wrapped, instance, args, kwargs): | ||
""" | ||
See the comment for the system_entry_point above as well. | ||
|
||
Decorator for wrapping functions that enter into a user context. This will help us differentiate user-created | ||
failures even when it is re-entrant into system code. | ||
|
||
|
@@ -188,35 +193,24 @@ def user_entry_point(wrapped, instance, args, kwargs): | |
try: | ||
_CONTEXT_STACK.append(_USER_CONTEXT) | ||
if _is_base_context(): | ||
# See comment at this location for system_entry_point | ||
try: | ||
return wrapped(*args, **kwargs) | ||
except FlyteScopedException as ex: | ||
_reraise(ex.type, ex.value, ex.traceback) | ||
raise ex.value | ||
else: | ||
try: | ||
return wrapped(*args, **kwargs) | ||
except FlyteScopedException: | ||
# Just pass on the already wrapped and scoped exception | ||
_reraise(*_exc_info()) | ||
except FlyteScopedException as scoped: | ||
raise scoped | ||
except _user_exceptions.FlyteUserException: | ||
_reraise( | ||
FlyteScopedUserException, | ||
FlyteScopedUserException(*_exc_info()), | ||
_exc_info()[2], | ||
) | ||
raise FlyteScopedUserException(*_exc_info()) | ||
except _system_exceptions.FlyteSystemException: | ||
_reraise( | ||
FlyteScopedSystemException, | ||
FlyteScopedSystemException(*_exc_info()), | ||
_exc_info()[2], | ||
) | ||
raise FlyteScopedSystemException(*_exc_info()) | ||
except Exception: | ||
# Any non-platform raised exception is a user exception. | ||
# This is why this function exists - arbitrary exceptions that we don't know what to do with are | ||
# interpreted as user exceptions. | ||
# This will also catch FlyteUserException re-raised by the system_entry_point handler | ||
_reraise( | ||
FlyteScopedUserException, | ||
FlyteScopedUserException(*_exc_info()), | ||
_exc_info()[2], | ||
) | ||
raise FlyteScopedUserException(*_exc_info()) | ||
finally: | ||
_CONTEXT_STACK.pop() |
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.
omg, this is weird?
why not a simple
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 is effectively what the decorator does. The reason the decorator exists is mostly for two reasons:
I think it's preferable to get rid of these scoped exceptions actually, I just don't want to go through everything and update them right now. Maybe at 1.0