diff --git a/features/index.html b/features/index.html index 3347412..e9bfd4e 100644 --- a/features/index.html +++ b/features/index.html @@ -353,6 +353,13 @@ handle_exc_class + + +
Functions the same as require_condition
.
This option describes the type of exception that will be handled by this context -manager. Any instance of the option's exception (or any of it's derived exception +manager. Any instance of the option's exception (or any of its derived exception classes) will be caught. This is very useful if you only want to handle a certain category of exceptions and let the others rise up un-altered:
with handle_errors("Something went wrong", handle_exc_class=MyProjectError):
@@ -918,6 +932,15 @@ handle_exc_class
handle_exc_class
option will not be handled at all. It is worth noting that the
do_except
task will not be executed if another exception type occurs. However,
the do_else
and do_finally
tasks will be executed normally.
+ignore_exc_class
+This option describes a type of exception that should not be handled by this
+context manager. Any instance of the option's exception (or any of its derived
+exception classes) will be raised immediately by handle_errors
and will be not
+be handled or processed.
+This is useful if you want a specific variant of your handle_exc_class
to not
+be handled by handle_errors
. For example, if you want to use
+handle_exc_class=Exception
but you do not want handle_errors
to handle
+RuntimeError
, then, you would set ignore_exc_class=RuntimeError
.
do_except
Often, it is useful to do some particular things when an exception is caught. Most
frequently this includes logging the exception. The do_except
optional argument
diff --git a/reference/index.html b/reference/index.html
index fc46324..cf5c9f4 100644
--- a/reference/index.html
+++ b/reference/index.html
@@ -1033,6 +1033,9 @@
raise_kwargs: Mapping[str, Any] | None = None,
handle_exc_class: type[Exception]
| Tuple[type[Exception], ...] = Exception,
+ ignore_exc_class: type[Exception]
+ | Tuple[type[Exception], ...]
+ | None = None,
do_finally: Callable[[], None] = noop,
do_except: Callable[[DoExceptParams], None] = noop,
do_else: Callable[[], None] = noop,
@@ -1139,6 +1142,25 @@
Exception
+
+ ignore_exc_class
+
+ type[Exception] | Tuple[type[Exception], ...] | None
+
+
+
+ Defines an exception or set of exception types that should not be handled at all.
+ Any matching exception types will be immediately re-raised. They will not be handled by
+ the handle_errors
context manager at all. This is useful if you want a specific variant of
+ your handle_exc_class
to not be handled by handle_errors
. For example, if you want to use
+ handle_exc_class=Exception
but you do not want handle_errors
to handle RuntimeError
.
+ Then, you would set ignore_exc_class=RuntimeError
.
+
+
+
+ None
+
+
do_finally
diff --git a/search/search_index.json b/search/search_index.json
index f66e3b6..8eebff1 100644
--- a/search/search_index.json
+++ b/search/search_index.json
@@ -1 +1 @@
-{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"That's not flying, it's falling with style
Take Exceptions to infinity...and beyond with py-buzz
!
"},{"location":"#overview","title":"Overview","text":"Have you ever found yourself writing the same code over and over to provide error handling in your python projects? I certainly did. In fact, I found that I often needed to re-implement the same patterns in almost every project. These patterns included:
- checking conditions and raising errors on failure
- checking that values are defined and raising errors if they are not
- catching exceptions and wrapping them in clearer exception types with better error messages
- checking many conditions and reporting which ones failed
This led me to create an exception toolkit called py-buzz
that provides powerful helper tools for each of the cases listed above. The py-buzz
package intends to make your error handling easy, expressive, and robust.
Because py-buzz
requires only Python itself, it's a very light-weight package that you can use in any project with very little overhead.
py-buzz
provides functionality or two different main use-cases. Either use-case allows you to focus on clear and concise error handling in your project without a lot of repetitive code:
"},{"location":"#helper-functions","title":"Helper Functions","text":"This set of functions can be used with any exception types. So, if you already have a set of custom exceptions or simply wish to use existing exceptions, you can import the py-buzz functions like require_condition
, handle_errors
, enforce_defined
, and use them in your code immediately.
"},{"location":"#buzz-base-class","title":"Buzz
base class","text":"This class is meant to be used as a base class for custom exceptions that you can use in your project. Buzz
includes all of the helper functions as class methods that will use your custom exception type.
"},{"location":"#quickstart","title":"Quickstart","text":""},{"location":"#requirements","title":"Requirements","text":" - Python 3.8 or greater
"},{"location":"#installation","title":"Installation","text":"This will install the latest release of py-buzz from pypi via pip:
pip install py-buzz\n
"},{"location":"#usage","title":"Usage","text":"Just import!
from buzz import require_condition\n\nrequire_condition(check_something(), \"The check failed!\")\n
For more examples of usage, see the Features page.
"},{"location":"features/","title":"Features","text":""},{"location":"features/#main-features","title":"Main Features","text":"There are a few main features of py-buzz
that are noteworthy:
"},{"location":"features/#raise-exception-on-condition-failure","title":"Raise exception on condition failure","text":"The py-buzz
package provides a function that checks a condition and raises an exception if it fails. This is nice, because you often find your self writing a lot of if <whatever>: raise Exception(<message>)
throughout your code base. It's just a little easier with py-buzz
:
# Vanilla python\nif not some_condition():\n raise Exception(\"some_condition failed\")\n\n# With py-buzz\nrequire_condition(some_condition(), \"some_condition failed\")\n
This is mostly just a bit of syntactic sugar, but it can make your code a bit more palatable. This is especially true in functions that need to check a lot of conditions before prior to executing their core logic.
You may also specify the exception type that should be raised by passing it to the raise_exc_class
parameter:
require_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n)\n
In this case, a MyProjectError
would be raised if the condition fails.
There are a few special keyword argument parameters for the require_condition()
function:
"},{"location":"features/#raise_exc_class","title":"raise_exc_class","text":"This just specifies the type of exception to raise if the condition fails. It defaults to Exception
.
"},{"location":"features/#raise_args","title":"raise_args","text":"With this parameter, you can specify any positional arguments that should be passed to the raised exception after the message. Here is an example:
class MyProjectError(Exception):\n def __init__(self, message, init_arg1, init_arg2):\n self.init_arg1 = init_arg1\n self.init_arg2 = init_arg2\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n raise_args=[\"foo\", \"bar\"],\n)\n
If the condition fails, require_condition
will rais a MyProjectError
initialized with positional args init_arg1 == \"foo\"
and init_arg2 == \"bar\"
.
"},{"location":"features/#raise_kwargs","title":"raise_kwargs","text":"Like the raise_args
parameter, this one passes along a dictionary of keyword arguments to the newly raised exception:
class MyProjectError(Exception):\n def __init__(self, message, init_kwarg1=None, init_kwarg2=None):\n self.init_kwarg1 = init_kwarg1\n self.init_kwarg2 = init_kwarg2\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n raise_kwargs=dict(\"foo\", \"bar\"),\n)\n
If the condition fails, require_condition
will rais a MyProjectError
initialized with keyword arguments init_kwarg1 == \"foo\"
and init_kwarg2 == \"bar\"
.
"},{"location":"features/#exc_builder","title":"exc_builder","text":"If the exception to be raised with the raise_exc_class
option is a non-standard exception type that does not take a string message as its first argument, you will need to use an alternative exception builder that knows how to map the exception parts to the correct place.
For example, FastAPI's HTTPException
takes a status_code
as its first positional argument and expects that any message details are passed as a keyword argument named details
.
In this case, you need to define a builder function to construct the exception and pass it to the exc_builder
option:
class WeirdArgsError(Exception):\n def __init__(self, weird_arg, detail=\"\"):\n self.weird_arg = weird_arg\n self.detail = detail\n\ndef weird_builder(exc_class, message, *args, **kwargs):\n return exc_class(*args, detail=message, **kwargs)\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=WeirdArgsError,\n raise_kwargs=dict(\"foo\", \"bar\"),\n exc_builder=weird_builder,\n)\n
"},{"location":"features/#raise-exception-if-value-is-not-defined","title":"Raise exception if value is not defined","text":"The py-buzz
package provides a function that checks a value and raises an exception if it is not defined. This is especially useful for both checking if a variable passed to a function is defined and also to satisfy static type checkers when you want to call a method on the object.
# Vanilla python\n def vanilla(val: Optional[str]):\n if val is None:\n raise Exception(\"Received an undefined value!\")\n return val.upper()\n\n # With py-buzz\n def buzzy(val: Optional[str]):\n val = enforce_defined(val)\n return val.upper()\n
This is also mostly just syntactic sugar, but it save you a few lines of code and is still very expressive. It might also be useful if you need to supply some more context in your error:
def neopolitan(val: Optional[str]):\n val = enforce_defined(\n val,\n \"Received an undefined value!\"\n raise_exc_class=MyProjectError,\n raise_args=[\"foo\", \"bar\"],\n raise_kwargs=dict(baz=\"QUX\"),\n )\n return val\n
In this case, a MyProjectError
with be raised with positional arguments of \"foo\"
and \"bar\"
and a keyword argument of baz=\"QUX\"
if the value passed in is not defined.
By default, enforce_defined()
raises an exception with a basic message saying that the value was not defined. However, you may pass in a custom message with the message
keyword argument. Like require_condition()
, the enforce_defined()
function also accepts the raise_exc_class
, raise_args
, raise_kwargs
, and exc_builder
keyword arguments.
"},{"location":"features/#exception-handling-context-manager","title":"Exception handling context manager","text":"The py-buzz
package also provides a context manager that catches any exceptions that might be raised while executing a bit of code. The caught exceptions are re-packaged and raised as another exception type. The message attahed to the new expression captures the initial exception's message:
# Vanilla python\ntry:\n this_could_fail()\n this_could_also_fail()\n this_will_definitely_fail()\nexcept Exception as err:\n raise RuntimeError(f\"Something didn't work -- Error: {err}\")\n\n# With py-buzz\nwith handle_errors(\"Something didn't work\", raise_exc_class=RuntimeError):\n this_could_fail()\n this_could_also_fail()\n this_will_definitely_fail()\n
This actually can save a bit of code and makes things a bit cleaner. It is also a implements pattern that tends to get repeated over and over again. If you need to do very complicated things while handling an exception, you should use a standard try- catch block. However, there are some extra bells and whistles on handle_errors
that can be used by passing additional keyword arguments to the function.
"},{"location":"features/#raise_exc_class_1","title":"raise_exc_class","text":"This parameter is the same as for require_condition()
. However, if you pass None
it will not raise a new exception. Instead, handle_errors
will process the do_except
, do_else
, and do_finally
methods and then continue. This effectively absorbs any exceptions that occur in the context. However the context is immediately exited after the first raised exception.
"},{"location":"features/#raise_args_1","title":"raise_args","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_kwargs_1","title":"raise_kwargs","text":"Functions the same as require_condition
.
"},{"location":"features/#exc_builder_1","title":"exc_builder","text":"Functions the same as require_condition
.
"},{"location":"features/#handle_exc_class","title":"handle_exc_class","text":"This option describes the type of exception that will be handled by this context manager. Any instance of the option's exception (or any of it's derived exception classes) will be caught. This is very useful if you only want to handle a certain category of exceptions and let the others rise up un-altered:
with handle_errors(\"Something went wrong\", handle_exc_class=MyProjectError):\n some_function_that_could_raise_mine_or_other_errors()\n
Exception instances that do not fall within the inheritance tree of the handle_exc_class
option will not be handled at all. It is worth noting that the do_except
task will not be executed if another exception type occurs. However, the do_else
and do_finally
tasks will be executed normally.
"},{"location":"features/#do_except","title":"do_except","text":"Often, it is useful to do some particular things when an exception is caught. Most frequently this includes logging the exception. The do_except
optional argument provides the ability to do this. The do_except
option should be a callable function that accepts a paramter of type DoExceptParams
that can be imported from buzz
. This dataclass
has three attributes:
- err: The caught exception itself
- final_message: A message describing the error (This will be the formatted error message)
- trace: A stack trace
This option might be invoked something like this:
def log_error(dep: DoExceptParams):\n logger.error(dep.final_message)\n logger.error('\\n'.join(dep.trace))\n\nwith handle_errors(\"Something went wrong\", do_except=log_error):\n some_dangerous_function()\n
"},{"location":"features/#do_else","title":"do_else","text":"This option describes some action that should happen if no exceptions are encountered. This option is less useful than do_except
but it may useful in some circumstances. This option should be a callable that takes no arguments:
def log_yay():\n logger.info(\"we did it!\")\n\n with handle_errors(\"Something went wrong\", do_else=log_yay):\n some_not_dangerous_function()\n
"},{"location":"features/#do_finally","title":"do_finally","text":"This option describes some action that should happen at the end of the context regardless to whether an exception occurred or not. This is a useful feature if you need to do some cleanup in either case. It should take a callable that receives no arguments:
def close_resource():\n resource.close()\n\nwith handle_errors(\"Something went wrong\", do_finally=close_resource):\n some_dangerous_function_that_uses_resource(resource)\n
"},{"location":"features/#additional-features","title":"Additional Features","text":""},{"location":"features/#check_expressions","title":"check_expressions","text":"The check_expressions
context manager is used to check multiple expressions inside of a context manager. Each expression is checked and each failing expression is reported at the end in a raised exception. If no expressions fail in the block, no exception is raised.
with check_expressions(main_message='there will be errors') as check:\n check(True)\n check(False)\n check(1 == 2, \"one is not two\")\n check('cooooooool', 'not a problem')\n check(0, \"zero is still zero\")\n
If the above code was executed, an exception would be raised that looks like this:
Exception: Checked expressions failed: there will be errors\n2: 2nd expression failed\n3: one is not two\n5: zero is still zero\n
The check_expressions()
also accepts some keyword arguments:
"},{"location":"features/#raise_exc_class_2","title":"raise_exc_class","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_args_2","title":"raise_args","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_kwargs_2","title":"raise_kwargs","text":"Functions the same as require_condition
.
"},{"location":"features/#exc_builder_2","title":"exc_builder","text":"Functions the same as require_condition
.
"},{"location":"features/#reformat_exception","title":"reformat_exception","text":"This method is used internally by the handle_errors
context manager. However, it is sometimes useful in other circumstances. It simply allows you to wrap an exception message in a more informative block of text:
try:\n raise ValueError(\"I didn't like that\")\nexcept Exception as err:\n print(reformat_exception(\"welp...that didn't work\", err))\n
The above block would result in output like:
> welp...that didn't work -- ValueError: I didn't like that\n
"},{"location":"features/#get_traceback","title":"get_traceback","text":"This function is just a tool to fetch the traceback for the current function. It does this by fetching it out of sys.exc_info
. It is used internally with Buzz but could be useful in other contexts.
"},{"location":"features/#the-buzz-base-class","title":"The Buzz base class","text":"All of the methods described above are attached to the special exception class, Buzz
. You could, for example, use this as the base exception type for your project and then access all the functions of py-buzz
from that exception type:
from buzz import Buzz\n\nclass MyProjectError(Buzz):\n pass\n\nMyProjectError.require_condition(check_vals(), \"Value check failed!\")\n
The code above would raise a MyProjectError
with the supplied message if the condition expression was falsey.
The Buzz
base class provides the same sort of access for handle_errors
, enforce_defined
, and check_expressions
.
Check out the examples for more.
"},{"location":"reference/","title":"Reference","text":""},{"location":"reference/#buzz.tools","title":"buzz.tools","text":"This module supplies the core functions of the py-buzz package.
"},{"location":"reference/#buzz.tools.DoExceptParams","title":"DoExceptParams dataclass
","text":"Dataclass for the do_except
user supplied handling method.
Attributes:
Name Type Description err
Exception
The exception instance itself.
final_message
str
The final, combined message
trace
types.TracebackType | None
A traceback of the exception
"},{"location":"reference/#buzz.tools.check_expressions","title":"check_expressions","text":"check_expressions(\n main_message: str,\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n)\n
Check a series of expressions inside of a context manager. If any fail an exception is raised that contains a main message and a description of each failing expression.
Parameters:
Name Type Description Default main
message
The main failure message to include in the constructed message that is passed to the raised Exception
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey.
Defaults to Exception.\n\n May not be None.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
Example The following is an example usage::
with check_expressions(\"Something wasn't right\") as check:\n check(a is not None)\n check(a > b, \"a must be greater than b\")\n check(a != 1, \"a must not equal 1\")\n check(b >= 0, \"b must not be negative\")\n
This would render output like::
Checked expressions failed: Something wasn't right:\n 1: first expressoin failed\n 3: a must not equal 1\n
"},{"location":"reference/#buzz.tools.default_exc_builder","title":"default_exc_builder","text":"default_exc_builder(\n exc: type[TExc], message: str, *args: str, **kwargs: str\n) -> TExc\n
Build an exception instance using default behavior where message is passed as first positional argument.
Some exception types such as FastAPI's HTTPException do not take a message as the first positional argument, so they will need a different exception builder.
"},{"location":"reference/#buzz.tools.enforce_defined","title":"enforce_defined","text":"enforce_defined(\n value: TNonNull | None,\n message: str = \"Value was not defined (None)\",\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n) -> TNonNull\n
Assert that a value is not None. If the assertion fails, raise an exception with the supplied message.
Parameters:
Name Type Description Default value
TNonNull | None
The value that is checked to be non-null
required message
str
The failure message to attach to the raised Exception
'Value was not defined (None)'
expr
The value that is checked for truthiness (usually an evaluated expression)
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey.
Defaults to Exception.\n May not be None.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
"},{"location":"reference/#buzz.tools.get_traceback","title":"get_traceback","text":"get_traceback() -> types.TracebackType | None\n
Retrieves the traceback after an exception has been raised.
"},{"location":"reference/#buzz.tools.handle_errors","title":"handle_errors","text":"handle_errors(\n message: str,\n raise_exc_class: type[Exception] | None = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n handle_exc_class: type[Exception]\n | Tuple[type[Exception], ...] = Exception,\n do_finally: Callable[[], None] = noop,\n do_except: Callable[[DoExceptParams], None] = noop,\n do_else: Callable[[], None] = noop,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n) -> Iterator[None]\n
Provide a context manager that will intercept exceptions and repackage them with a message attached:
Parameters:
Name Type Description Default message
str
The message to attach to the raised exception.
required raise_exc_class
type[Exception] | None
The exception type to raise with the constructed message if an exception is caught in the managed context.
Defaults to Exception.\n\nIf ``None`` is passed, no new exception will be raised and only the ``do_except``,\n``do_else``, and ``do_finally`` functions will be called.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
handle_exc_class
type[Exception] | Tuple[type[Exception], ...]
Limits the class of exceptions that will be intercepted Any other exception types will not be caught and re-packaged. Defaults to Exception (will handle all exceptions). May also be provided as a tuple of multiple exception types to handle.
Exception
do_finally
Callable[[], None]
A function that should always be called at the end of the block. Should take no parameters.
noop
do_except
Callable[[DoExceptParams], None]
A function that should be called only if there was an exception. Must accept one parameter that is an instance of the DoExceptParams
dataclass. Note that the do_except
method is passed the original exception.
noop
do_else
Callable[[], None]
A function that should be called only if there were no exceptions encountered.
noop
Example The following is an example usage::
with handle_errors(\"It didn't work\"):\n some_code_that_might_raise_an_exception()\n
"},{"location":"reference/#buzz.tools.reformat_exception","title":"reformat_exception","text":"reformat_exception(message: str, err: Exception) -> str\n
Reformat an exception by adding a message to it and reporting the original exception name and message.
"},{"location":"reference/#buzz.tools.require_condition","title":"require_condition","text":"require_condition(\n expr: Any,\n message: str,\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n)\n
Assert that an expression is truthy. If the assertion fails, raise an exception with the supplied message.
Parameters:
Name Type Description Default message
str
The failure message to attach to the raised Exception
required expr
Any
The value that is checked for truthiness (usually an evaluated expression)
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey. Defaults to Exception. May not be None.
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
"},{"location":"reference/#buzz.base","title":"buzz.base","text":"This module defines the Buzz base class.
"},{"location":"reference/#buzz.base.Buzz","title":"Buzz","text":" Bases: Exception
This provides a specialized exception class that wraps up all the buzz utility functions.
"},{"location":"reference/#buzz.base.Buzz.__init__","title":"__init__","text":"__init__(message)\n
Initialize the exception with a message. Dedent the supplied message.
"},{"location":"reference/#buzz.base.Buzz.check_expressions","title":"check_expressions classmethod
","text":"check_expressions(*args, **kwargs)\n
Call the check_expressions()
context manager with this class as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.enforce_defined","title":"enforce_defined classmethod
","text":"enforce_defined(\n value: TNonNull | None,\n *args: TNonNull | None,\n **kwargs: TNonNull | None\n) -> TNonNull\n
Call the enforce_defined()
function with this class as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.get_traceback","title":"get_traceback classmethod
","text":"get_traceback(*args, **kwargs)\n
Call the get_traceback()
function.
"},{"location":"reference/#buzz.base.Buzz.handle_errors","title":"handle_errors classmethod
","text":"handle_errors(*args, re_raise = True, **kwargs)\n
Call the handle_errors()
context manager with this class as the raise_exc_class
kwarg.
Note If re_raise
is not True, None
will be passed as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.require_condition","title":"require_condition classmethod
","text":"require_condition(*args, **kwargs)\n
Call the require_condition()
function with this class as the raise_exc_class
kwarg.
"}]}
\ No newline at end of file
+{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":"That's not flying, it's falling with style
Take Exceptions to infinity...and beyond with py-buzz
!
"},{"location":"#overview","title":"Overview","text":"Have you ever found yourself writing the same code over and over to provide error handling in your python projects? I certainly did. In fact, I found that I often needed to re-implement the same patterns in almost every project. These patterns included:
- checking conditions and raising errors on failure
- checking that values are defined and raising errors if they are not
- catching exceptions and wrapping them in clearer exception types with better error messages
- checking many conditions and reporting which ones failed
This led me to create an exception toolkit called py-buzz
that provides powerful helper tools for each of the cases listed above. The py-buzz
package intends to make your error handling easy, expressive, and robust.
Because py-buzz
requires only Python itself, it's a very light-weight package that you can use in any project with very little overhead.
py-buzz
provides functionality or two different main use-cases. Either use-case allows you to focus on clear and concise error handling in your project without a lot of repetitive code:
"},{"location":"#helper-functions","title":"Helper Functions","text":"This set of functions can be used with any exception types. So, if you already have a set of custom exceptions or simply wish to use existing exceptions, you can import the py-buzz functions like require_condition
, handle_errors
, enforce_defined
, and use them in your code immediately.
"},{"location":"#buzz-base-class","title":"Buzz
base class","text":"This class is meant to be used as a base class for custom exceptions that you can use in your project. Buzz
includes all of the helper functions as class methods that will use your custom exception type.
"},{"location":"#quickstart","title":"Quickstart","text":""},{"location":"#requirements","title":"Requirements","text":" - Python 3.8 or greater
"},{"location":"#installation","title":"Installation","text":"This will install the latest release of py-buzz from pypi via pip:
pip install py-buzz\n
"},{"location":"#usage","title":"Usage","text":"Just import!
from buzz import require_condition\n\nrequire_condition(check_something(), \"The check failed!\")\n
For more examples of usage, see the Features page.
"},{"location":"features/","title":"Features","text":""},{"location":"features/#main-features","title":"Main Features","text":"There are a few main features of py-buzz
that are noteworthy:
"},{"location":"features/#raise-exception-on-condition-failure","title":"Raise exception on condition failure","text":"The py-buzz
package provides a function that checks a condition and raises an exception if it fails. This is nice, because you often find your self writing a lot of if <whatever>: raise Exception(<message>)
throughout your code base. It's just a little easier with py-buzz
:
# Vanilla python\nif not some_condition():\n raise Exception(\"some_condition failed\")\n\n# With py-buzz\nrequire_condition(some_condition(), \"some_condition failed\")\n
This is mostly just a bit of syntactic sugar, but it can make your code a bit more palatable. This is especially true in functions that need to check a lot of conditions before prior to executing their core logic.
You may also specify the exception type that should be raised by passing it to the raise_exc_class
parameter:
require_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n)\n
In this case, a MyProjectError
would be raised if the condition fails.
There are a few special keyword argument parameters for the require_condition()
function:
"},{"location":"features/#raise_exc_class","title":"raise_exc_class","text":"This just specifies the type of exception to raise if the condition fails. It defaults to Exception
.
"},{"location":"features/#raise_args","title":"raise_args","text":"With this parameter, you can specify any positional arguments that should be passed to the raised exception after the message. Here is an example:
class MyProjectError(Exception):\n def __init__(self, message, init_arg1, init_arg2):\n self.init_arg1 = init_arg1\n self.init_arg2 = init_arg2\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n raise_args=[\"foo\", \"bar\"],\n)\n
If the condition fails, require_condition
will rais a MyProjectError
initialized with positional args init_arg1 == \"foo\"
and init_arg2 == \"bar\"
.
"},{"location":"features/#raise_kwargs","title":"raise_kwargs","text":"Like the raise_args
parameter, this one passes along a dictionary of keyword arguments to the newly raised exception:
class MyProjectError(Exception):\n def __init__(self, message, init_kwarg1=None, init_kwarg2=None):\n self.init_kwarg1 = init_kwarg1\n self.init_kwarg2 = init_kwarg2\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=MyProjectError,\n raise_kwargs=dict(\"foo\", \"bar\"),\n)\n
If the condition fails, require_condition
will rais a MyProjectError
initialized with keyword arguments init_kwarg1 == \"foo\"
and init_kwarg2 == \"bar\"
.
"},{"location":"features/#exc_builder","title":"exc_builder","text":"If the exception to be raised with the raise_exc_class
option is a non-standard exception type that does not take a string message as its first argument, you will need to use an alternative exception builder that knows how to map the exception parts to the correct place.
For example, FastAPI's HTTPException
takes a status_code
as its first positional argument and expects that any message details are passed as a keyword argument named details
.
In this case, you need to define a builder function to construct the exception and pass it to the exc_builder
option:
class WeirdArgsError(Exception):\n def __init__(self, weird_arg, detail=\"\"):\n self.weird_arg = weird_arg\n self.detail = detail\n\ndef weird_builder(exc_class, message, *args, **kwargs):\n return exc_class(*args, detail=message, **kwargs)\n\nrequire_condition(\n some_condition(),\n \"some_condition failed\",\n raise_exc_class=WeirdArgsError,\n raise_kwargs=dict(\"foo\", \"bar\"),\n exc_builder=weird_builder,\n)\n
"},{"location":"features/#raise-exception-if-value-is-not-defined","title":"Raise exception if value is not defined","text":"The py-buzz
package provides a function that checks a value and raises an exception if it is not defined. This is especially useful for both checking if a variable passed to a function is defined and also to satisfy static type checkers when you want to call a method on the object.
# Vanilla python\n def vanilla(val: Optional[str]):\n if val is None:\n raise Exception(\"Received an undefined value!\")\n return val.upper()\n\n # With py-buzz\n def buzzy(val: Optional[str]):\n val = enforce_defined(val)\n return val.upper()\n
This is also mostly just syntactic sugar, but it save you a few lines of code and is still very expressive. It might also be useful if you need to supply some more context in your error:
def neopolitan(val: Optional[str]):\n val = enforce_defined(\n val,\n \"Received an undefined value!\"\n raise_exc_class=MyProjectError,\n raise_args=[\"foo\", \"bar\"],\n raise_kwargs=dict(baz=\"QUX\"),\n )\n return val\n
In this case, a MyProjectError
with be raised with positional arguments of \"foo\"
and \"bar\"
and a keyword argument of baz=\"QUX\"
if the value passed in is not defined.
By default, enforce_defined()
raises an exception with a basic message saying that the value was not defined. However, you may pass in a custom message with the message
keyword argument. Like require_condition()
, the enforce_defined()
function also accepts the raise_exc_class
, raise_args
, raise_kwargs
, and exc_builder
keyword arguments.
"},{"location":"features/#exception-handling-context-manager","title":"Exception handling context manager","text":"The py-buzz
package also provides a context manager that catches any exceptions that might be raised while executing a bit of code. The caught exceptions are re-packaged and raised as another exception type. The message attahed to the new expression captures the initial exception's message:
# Vanilla python\ntry:\n this_could_fail()\n this_could_also_fail()\n this_will_definitely_fail()\nexcept Exception as err:\n raise RuntimeError(f\"Something didn't work -- Error: {err}\")\n\n# With py-buzz\nwith handle_errors(\"Something didn't work\", raise_exc_class=RuntimeError):\n this_could_fail()\n this_could_also_fail()\n this_will_definitely_fail()\n
This actually can save a bit of code and makes things a bit cleaner. It is also a implements pattern that tends to get repeated over and over again. If you need to do very complicated things while handling an exception, you should use a standard try- catch block. However, there are some extra bells and whistles on handle_errors
that can be used by passing additional keyword arguments to the function.
"},{"location":"features/#raise_exc_class_1","title":"raise_exc_class","text":"This parameter is the same as for require_condition()
. However, if you pass None
it will not raise a new exception. Instead, handle_errors
will process the do_except
, do_else
, and do_finally
methods and then continue. This effectively absorbs any exceptions that occur in the context. However the context is immediately exited after the first raised exception.
"},{"location":"features/#raise_args_1","title":"raise_args","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_kwargs_1","title":"raise_kwargs","text":"Functions the same as require_condition
.
"},{"location":"features/#exc_builder_1","title":"exc_builder","text":"Functions the same as require_condition
.
"},{"location":"features/#handle_exc_class","title":"handle_exc_class","text":"This option describes the type of exception that will be handled by this context manager. Any instance of the option's exception (or any of its derived exception classes) will be caught. This is very useful if you only want to handle a certain category of exceptions and let the others rise up un-altered:
with handle_errors(\"Something went wrong\", handle_exc_class=MyProjectError):\n some_function_that_could_raise_mine_or_other_errors()\n
Exception instances that do not fall within the inheritance tree of the handle_exc_class
option will not be handled at all. It is worth noting that the do_except
task will not be executed if another exception type occurs. However, the do_else
and do_finally
tasks will be executed normally.
"},{"location":"features/#ignore_exc_class","title":"ignore_exc_class","text":"This option describes a type of exception that should not be handled by this context manager. Any instance of the option's exception (or any of its derived exception classes) will be raised immediately by handle_errors
and will be not be handled or processed.
This is useful if you want a specific variant of your handle_exc_class
to not be handled by handle_errors
. For example, if you want to use handle_exc_class=Exception
but you do not want handle_errors
to handle RuntimeError
, then, you would set ignore_exc_class=RuntimeError
.
"},{"location":"features/#do_except","title":"do_except","text":"Often, it is useful to do some particular things when an exception is caught. Most frequently this includes logging the exception. The do_except
optional argument provides the ability to do this. The do_except
option should be a callable function that accepts a paramter of type DoExceptParams
that can be imported from buzz
. This dataclass
has three attributes:
- err: The caught exception itself
- final_message: A message describing the error (This will be the formatted error message)
- trace: A stack trace
This option might be invoked something like this:
def log_error(dep: DoExceptParams):\n logger.error(dep.final_message)\n logger.error('\\n'.join(dep.trace))\n\nwith handle_errors(\"Something went wrong\", do_except=log_error):\n some_dangerous_function()\n
"},{"location":"features/#do_else","title":"do_else","text":"This option describes some action that should happen if no exceptions are encountered. This option is less useful than do_except
but it may useful in some circumstances. This option should be a callable that takes no arguments:
def log_yay():\n logger.info(\"we did it!\")\n\n with handle_errors(\"Something went wrong\", do_else=log_yay):\n some_not_dangerous_function()\n
"},{"location":"features/#do_finally","title":"do_finally","text":"This option describes some action that should happen at the end of the context regardless to whether an exception occurred or not. This is a useful feature if you need to do some cleanup in either case. It should take a callable that receives no arguments:
def close_resource():\n resource.close()\n\nwith handle_errors(\"Something went wrong\", do_finally=close_resource):\n some_dangerous_function_that_uses_resource(resource)\n
"},{"location":"features/#additional-features","title":"Additional Features","text":""},{"location":"features/#check_expressions","title":"check_expressions","text":"The check_expressions
context manager is used to check multiple expressions inside of a context manager. Each expression is checked and each failing expression is reported at the end in a raised exception. If no expressions fail in the block, no exception is raised.
with check_expressions(main_message='there will be errors') as check:\n check(True)\n check(False)\n check(1 == 2, \"one is not two\")\n check('cooooooool', 'not a problem')\n check(0, \"zero is still zero\")\n
If the above code was executed, an exception would be raised that looks like this:
Exception: Checked expressions failed: there will be errors\n2: 2nd expression failed\n3: one is not two\n5: zero is still zero\n
The check_expressions()
also accepts some keyword arguments:
"},{"location":"features/#raise_exc_class_2","title":"raise_exc_class","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_args_2","title":"raise_args","text":"Functions the same as require_condition
.
"},{"location":"features/#raise_kwargs_2","title":"raise_kwargs","text":"Functions the same as require_condition
.
"},{"location":"features/#exc_builder_2","title":"exc_builder","text":"Functions the same as require_condition
.
"},{"location":"features/#reformat_exception","title":"reformat_exception","text":"This method is used internally by the handle_errors
context manager. However, it is sometimes useful in other circumstances. It simply allows you to wrap an exception message in a more informative block of text:
try:\n raise ValueError(\"I didn't like that\")\nexcept Exception as err:\n print(reformat_exception(\"welp...that didn't work\", err))\n
The above block would result in output like:
> welp...that didn't work -- ValueError: I didn't like that\n
"},{"location":"features/#get_traceback","title":"get_traceback","text":"This function is just a tool to fetch the traceback for the current function. It does this by fetching it out of sys.exc_info
. It is used internally with Buzz but could be useful in other contexts.
"},{"location":"features/#the-buzz-base-class","title":"The Buzz base class","text":"All of the methods described above are attached to the special exception class, Buzz
. You could, for example, use this as the base exception type for your project and then access all the functions of py-buzz
from that exception type:
from buzz import Buzz\n\nclass MyProjectError(Buzz):\n pass\n\nMyProjectError.require_condition(check_vals(), \"Value check failed!\")\n
The code above would raise a MyProjectError
with the supplied message if the condition expression was falsey.
The Buzz
base class provides the same sort of access for handle_errors
, enforce_defined
, and check_expressions
.
Check out the examples for more.
"},{"location":"reference/","title":"Reference","text":""},{"location":"reference/#buzz.tools","title":"buzz.tools","text":"This module supplies the core functions of the py-buzz package.
"},{"location":"reference/#buzz.tools.DoExceptParams","title":"DoExceptParams dataclass
","text":"Dataclass for the do_except
user supplied handling method.
Attributes:
Name Type Description err
Exception
The exception instance itself.
final_message
str
The final, combined message
trace
types.TracebackType | None
A traceback of the exception
"},{"location":"reference/#buzz.tools.check_expressions","title":"check_expressions","text":"check_expressions(\n main_message: str,\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n)\n
Check a series of expressions inside of a context manager. If any fail an exception is raised that contains a main message and a description of each failing expression.
Parameters:
Name Type Description Default main
message
The main failure message to include in the constructed message that is passed to the raised Exception
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey.
Defaults to Exception.\n\n May not be None.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
Example The following is an example usage::
with check_expressions(\"Something wasn't right\") as check:\n check(a is not None)\n check(a > b, \"a must be greater than b\")\n check(a != 1, \"a must not equal 1\")\n check(b >= 0, \"b must not be negative\")\n
This would render output like::
Checked expressions failed: Something wasn't right:\n 1: first expressoin failed\n 3: a must not equal 1\n
"},{"location":"reference/#buzz.tools.default_exc_builder","title":"default_exc_builder","text":"default_exc_builder(\n exc: type[TExc], message: str, *args: str, **kwargs: str\n) -> TExc\n
Build an exception instance using default behavior where message is passed as first positional argument.
Some exception types such as FastAPI's HTTPException do not take a message as the first positional argument, so they will need a different exception builder.
"},{"location":"reference/#buzz.tools.enforce_defined","title":"enforce_defined","text":"enforce_defined(\n value: TNonNull | None,\n message: str = \"Value was not defined (None)\",\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n) -> TNonNull\n
Assert that a value is not None. If the assertion fails, raise an exception with the supplied message.
Parameters:
Name Type Description Default value
TNonNull | None
The value that is checked to be non-null
required message
str
The failure message to attach to the raised Exception
'Value was not defined (None)'
expr
The value that is checked for truthiness (usually an evaluated expression)
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey.
Defaults to Exception.\n May not be None.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
"},{"location":"reference/#buzz.tools.get_traceback","title":"get_traceback","text":"get_traceback() -> types.TracebackType | None\n
Retrieves the traceback after an exception has been raised.
"},{"location":"reference/#buzz.tools.handle_errors","title":"handle_errors","text":"handle_errors(\n message: str,\n raise_exc_class: type[Exception] | None = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n handle_exc_class: type[Exception]\n | Tuple[type[Exception], ...] = Exception,\n ignore_exc_class: type[Exception]\n | Tuple[type[Exception], ...]\n | None = None,\n do_finally: Callable[[], None] = noop,\n do_except: Callable[[DoExceptParams], None] = noop,\n do_else: Callable[[], None] = noop,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n) -> Iterator[None]\n
Provide a context manager that will intercept exceptions and repackage them with a message attached:
Parameters:
Name Type Description Default message
str
The message to attach to the raised exception.
required raise_exc_class
type[Exception] | None
The exception type to raise with the constructed message if an exception is caught in the managed context.
Defaults to Exception.\n\nIf ``None`` is passed, no new exception will be raised and only the ``do_except``,\n``do_else``, and ``do_finally`` functions will be called.\n
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
handle_exc_class
type[Exception] | Tuple[type[Exception], ...]
Limits the class of exceptions that will be intercepted Any other exception types will not be caught and re-packaged. Defaults to Exception (will handle all exceptions). May also be provided as a tuple of multiple exception types to handle.
Exception
ignore_exc_class
type[Exception] | Tuple[type[Exception], ...] | None
Defines an exception or set of exception types that should not be handled at all. Any matching exception types will be immediately re-raised. They will not be handled by the handle_errors
context manager at all. This is useful if you want a specific variant of your handle_exc_class
to not be handled by handle_errors
. For example, if you want to use handle_exc_class=Exception
but you do not want handle_errors
to handle RuntimeError
. Then, you would set ignore_exc_class=RuntimeError
.
None
do_finally
Callable[[], None]
A function that should always be called at the end of the block. Should take no parameters.
noop
do_except
Callable[[DoExceptParams], None]
A function that should be called only if there was an exception. Must accept one parameter that is an instance of the DoExceptParams
dataclass. Note that the do_except
method is passed the original exception.
noop
do_else
Callable[[], None]
A function that should be called only if there were no exceptions encountered.
noop
Example The following is an example usage::
with handle_errors(\"It didn't work\"):\n some_code_that_might_raise_an_exception()\n
"},{"location":"reference/#buzz.tools.reformat_exception","title":"reformat_exception","text":"reformat_exception(message: str, err: Exception) -> str\n
Reformat an exception by adding a message to it and reporting the original exception name and message.
"},{"location":"reference/#buzz.tools.require_condition","title":"require_condition","text":"require_condition(\n expr: Any,\n message: str,\n raise_exc_class: type[Exception] = Exception,\n raise_args: Iterable[Any] | None = None,\n raise_kwargs: Mapping[str, Any] | None = None,\n exc_builder: Callable[\n ..., Exception\n ] = default_exc_builder,\n)\n
Assert that an expression is truthy. If the assertion fails, raise an exception with the supplied message.
Parameters:
Name Type Description Default message
str
The failure message to attach to the raised Exception
required expr
Any
The value that is checked for truthiness (usually an evaluated expression)
required raise_exc_class
type[Exception]
The exception type to raise with the constructed message if the expression is falsey. Defaults to Exception. May not be None.
Exception
raise_args
Iterable[Any] | None
Additional positional args (after the constructed message) that will passed when raising an instance of the raise_exc_class
.
None
raise_kwargs
Mapping[str, Any] | None
Keyword args that will be passed when raising an instance of the raise_exc_class
.
None
"},{"location":"reference/#buzz.base","title":"buzz.base","text":"This module defines the Buzz base class.
"},{"location":"reference/#buzz.base.Buzz","title":"Buzz","text":" Bases: Exception
This provides a specialized exception class that wraps up all the buzz utility functions.
"},{"location":"reference/#buzz.base.Buzz.__init__","title":"__init__","text":"__init__(message)\n
Initialize the exception with a message. Dedent the supplied message.
"},{"location":"reference/#buzz.base.Buzz.check_expressions","title":"check_expressions classmethod
","text":"check_expressions(*args, **kwargs)\n
Call the check_expressions()
context manager with this class as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.enforce_defined","title":"enforce_defined classmethod
","text":"enforce_defined(\n value: TNonNull | None,\n *args: TNonNull | None,\n **kwargs: TNonNull | None\n) -> TNonNull\n
Call the enforce_defined()
function with this class as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.get_traceback","title":"get_traceback classmethod
","text":"get_traceback(*args, **kwargs)\n
Call the get_traceback()
function.
"},{"location":"reference/#buzz.base.Buzz.handle_errors","title":"handle_errors classmethod
","text":"handle_errors(*args, re_raise = True, **kwargs)\n
Call the handle_errors()
context manager with this class as the raise_exc_class
kwarg.
Note If re_raise
is not True, None
will be passed as the raise_exc_class
kwarg.
"},{"location":"reference/#buzz.base.Buzz.require_condition","title":"require_condition classmethod
","text":"require_condition(*args, **kwargs)\n
Call the require_condition()
function with this class as the raise_exc_class
kwarg.
"}]}
\ No newline at end of file
diff --git a/sitemap.xml b/sitemap.xml
index a7058e1..41794d1 100644
--- a/sitemap.xml
+++ b/sitemap.xml
@@ -2,17 +2,17 @@
http://py-buzz.readthedocs.io/en/latest/
- 2023-08-29
+ 2023-09-01
daily
http://py-buzz.readthedocs.io/en/latest/features/
- 2023-08-29
+ 2023-09-01
daily
http://py-buzz.readthedocs.io/en/latest/reference/
- 2023-08-29
+ 2023-09-01
daily
\ No newline at end of file
diff --git a/sitemap.xml.gz b/sitemap.xml.gz
index 2a04a1d..05f1cd9 100644
Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ