Skip to content

Instantly share code, notes, and snippets.

@dankrause
Last active March 15, 2023 13:51
Show Gist options
  • Select an option

  • Save dankrause/85d5388dfcb8868aa070375df8645e4f to your computer and use it in GitHub Desktop.

Select an option

Save dankrause/85d5388dfcb8868aa070375df8645e4f to your computer and use it in GitHub Desktop.

Revisions

  1. dankrause revised this gist Feb 15, 2022. 1 changed file with 12 additions and 2 deletions.
    14 changes: 12 additions & 2 deletions exception_handler.py
    Original file line number Diff line number Diff line change
    @@ -19,9 +19,16 @@ def __exit__(self, exc_class, exc_instance, traceback):


    class ExceptionHandlerMap(_ExceptionHandlerBase):
    def add_handler(self, cls, callback):
    self._handler_map[cls] = callback

    def add_handlers(self, handler_map):
    self._handler_map.update(handler_map)

    def also_handle(self, handler_map):
    handler_map.update(self._handler_map)
    return type(self)(handler_map)
    new_handler = type(self)(self._handler_map.copy())
    new_handler.add_handlers(handler_map)
    return new_handler


    class ExceptionHandler(_ExceptionHandlerBase):
    @@ -30,6 +37,9 @@ def __init__(self, callback, *classes):
    self._callback = callback
    self._classes = classes

    def add_class(self, cls):
    self._handler_map[cls] = self._callback

    def instead_catch(self, *classes):
    return type(self)(self._callback, *classes)

  2. dankrause revised this gist Feb 15, 2022. 1 changed file with 1 addition and 4 deletions.
    5 changes: 1 addition & 4 deletions exception_handler.py
    Original file line number Diff line number Diff line change
    @@ -19,12 +19,9 @@ def __exit__(self, exc_class, exc_instance, traceback):


    class ExceptionHandlerMap(_ExceptionHandlerBase):
    def instead_handle(self, handler_map):
    return type(self)(handler_map)

    def also_handle(self, handler_map):
    handler_map.update(self._handler_map)
    return self.instead_handle(handler_map)
    return type(self)(handler_map)


    class ExceptionHandler(_ExceptionHandlerBase):
  3. dankrause revised this gist Feb 15, 2022. 1 changed file with 0 additions and 3 deletions.
    3 changes: 0 additions & 3 deletions exception_handler.py
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,3 @@
    from cgitb import handler


    class _ExceptionHandlerBase:
    def __init__(self, handler_map):
    self._handler_map = handler_map
  4. dankrause revised this gist Feb 15, 2022. 1 changed file with 63 additions and 31 deletions.
    94 changes: 63 additions & 31 deletions exception_handler.py
    Original file line number Diff line number Diff line change
    @@ -1,26 +1,46 @@
    class ExceptionHandler:
    def __init__(self, callback, *classes):
    self._classes = [
    c for c in classes if isinstance(c, type) and issubclass(c, Exception)
    ]
    self._callback = callback
    self._wrapped_func = None
    from cgitb import handler

    def __call__(self, func):
    self._wrapped_func = func

    class _ExceptionHandlerBase:
    def __init__(self, handler_map):
    self._handler_map = handler_map

    def __call__(self, func):
    def wrapper(*args, **kwargs):
    with self:
    self._wrapped_func(*args, **kwargs)
    func(*args, **kwargs)

    return wrapper

    def __enter__(self):
    pass

    def __exit__(self, exc_class, exc_instance, traceback):
    if exc_class in self._classes:
    return self._callback(exc_instance)
    if exc_class in self._handler_map:
    return self._handler_map[exc_class](exc_instance)
    return False


    class ExceptionHandlerMap(_ExceptionHandlerBase):
    def instead_handle(self, handler_map):
    return type(self)(handler_map)

    def also_handle(self, handler_map):
    handler_map.update(self._handler_map)
    return self.instead_handle(handler_map)


    class ExceptionHandler(_ExceptionHandlerBase):
    def __init__(self, callback, *classes):
    super().__init__({cls: callback for cls in classes})
    self._callback = callback
    self._classes = classes

    def instead_catch(self, *classes):
    return type(self)(self._callback, *classes)

    def also_catch(self, *classes):
    return self.instead_catch(*(*classes, *self._classes))

    @classmethod
    def catches(cls, *classes):
    @@ -39,20 +59,26 @@ class BarException(Exception):
    pass


    # we can use the "catches" class method as a decorator to create an exception handler
    @ExceptionHandler.catches(FooException)
    def foo_handler(e):
    print(f"Caught {type(e).__name__}: {e}")
    return True # we return True to swallow the exception, False to let it raise
    def handle_foo(e):
    print(f"handle_foo caught {type(e).__name__}: {e}")
    return True


    def handle_bar(e):
    print(f"handle_bar caught {type(e).__name__}: {e}")
    return True


    def handle_foo_and_bar(e):
    print(f"Caught {type(e).__name__}: {e}")
    # we can use the "catches" class method as a decorator to create an exception handler
    @ExceptionHandler.catches(FooException, BarException)
    def foo_bar_handler(e):
    print(f"handle_foo_and_bar caught {type(e).__name__}: {e}")
    return True


    # we can create an exception handler using the class directly
    foo_bar_handler = ExceptionHandler(handle_foo_and_bar, FooException, BarException)
    foo_handler = ExceptionHandler(handle_foo, FooException, BarException)
    bar_handler = ExceptionHandler(handle_bar, FooException, BarException)


    # we can use the exception handler as a decorator to handle exceptions within the function
    @@ -69,18 +95,24 @@ def raise_bar(text):
    raise_foo("in raise_foo function")
    raise_bar("in raise_bar function")


    # we can also use the exception handler as a context manager to handle exceptions within it's block
    with foo_bar_handler:
    raise FooException("inside context manager")

    with foo_handler:
    raise BarException("This will not be handled")

    # output:
    # Caught FooException: in raise_foo function
    # Caught BarException: in raise_bar function
    # Caught FooException: inside context manager
    # Traceback (most recent call last):
    # File "exception_handler.py", line 77, in <module>
    # raise BarException("This will not be handled")
    # __main__.BarException: This will not be handled
    # we can replace which exceptions it catches for this invocation
    with foo_handler.instead_catch(BarException):
    raise BarException("This WILL be handled")

    # we can append which exceptions it catches for this invocation
    with foo_handler.also_catch(BarException):
    raise BarException("This WILL be handled")

    # we can create an object that has different callbacks for different exception classes
    handler_map = ExceptionHandlerMap({FooException: handle_foo, BarException: handle_bar})

    with handler_map:
    raise FooException("this is handled by the ExceptionHandlerMap")

    with handler_map:
    raise BarException("this is also handled by the ExceptionHandlerMap")
  5. dankrause created this gist Feb 11, 2022.
    86 changes: 86 additions & 0 deletions exception_handler.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,86 @@
    class ExceptionHandler:
    def __init__(self, callback, *classes):
    self._classes = [
    c for c in classes if isinstance(c, type) and issubclass(c, Exception)
    ]
    self._callback = callback
    self._wrapped_func = None

    def __call__(self, func):
    self._wrapped_func = func

    def wrapper(*args, **kwargs):
    with self:
    self._wrapped_func(*args, **kwargs)

    return wrapper

    def __enter__(self):
    pass

    def __exit__(self, exc_class, exc_instance, traceback):
    if exc_class in self._classes:
    return self._callback(exc_instance)

    @classmethod
    def catches(cls, *classes):
    def wrapper(callback):
    return cls(callback, *classes)

    return wrapper


    # Example usage:
    class FooException(Exception):
    pass


    class BarException(Exception):
    pass


    # we can use the "catches" class method as a decorator to create an exception handler
    @ExceptionHandler.catches(FooException)
    def foo_handler(e):
    print(f"Caught {type(e).__name__}: {e}")
    return True # we return True to swallow the exception, False to let it raise


    def handle_foo_and_bar(e):
    print(f"Caught {type(e).__name__}: {e}")
    return True


    # we can create an exception handler using the class directly
    foo_bar_handler = ExceptionHandler(handle_foo_and_bar, FooException, BarException)


    # we can use the exception handler as a decorator to handle exceptions within the function
    @foo_handler
    def raise_foo(text):
    raise FooException(text)


    @foo_bar_handler
    def raise_bar(text):
    raise BarException(text)


    raise_foo("in raise_foo function")
    raise_bar("in raise_bar function")

    # we can also use the exception handler as a context manager to handle exceptions within it's block
    with foo_bar_handler:
    raise FooException("inside context manager")

    with foo_handler:
    raise BarException("This will not be handled")

    # output:
    # Caught FooException: in raise_foo function
    # Caught BarException: in raise_bar function
    # Caught FooException: inside context manager
    # Traceback (most recent call last):
    # File "exception_handler.py", line 77, in <module>
    # raise BarException("This will not be handled")
    # __main__.BarException: This will not be handled