Overview
The pyTooling.Exceptions package provides the base-classes every other exception in pyTooling derives
from, and a small set of predefined exceptions for situations that occur in almost every application: a missing
environment variable, an unsupported platform, an unconfigured setting, an invalid configuration, and an optional
dependency that was not installed.
Packages and frameworks building on pyTooling are meant to derive from these rather than from
Exception directly, so an application can catch everything raised by its stack with a
single clause.
Note
Exception classes carry the suffix ***Error, following PEP 8: “you should use the suffix `Error` on your
exception names (if the exception actually is an error)”. The suffix ***Exception is reserved for a
package’s own base-exception — ToolingException here.
Exception Base Classes
ExceptionBase
The ExceptionBase is the base-class for all exceptions in pyTooling as well as derived packages and
frameworks.
It keeps the message in self.message and renders it through __str__(), and it adds two
read-only properties for the notes attached with add_note():
HasNotes— whether any note is attached.__notes__only exists once the first note was added, so testing this property is safer than testing the attribute.Notes— the attached notes as a tuple, empty when there are none.
from pyTooling.Exceptions import ExceptionBase
class MyPackageError(ExceptionBase):
"""Base-exception of all exceptions raised by 'myPackage'."""
Attention
ExceptionBase does not forward its message to Exception.__init__, so
ex.args is empty while str(ex) returns the message. Code reading ex.args[0] has to
read str(ex) instead.
ToolingException
The ToolingException is the base-exception for errors raised by pyTooling’s own features — the
package-level bases such as GraphError and TreeError derive from it.
It is the one class that keeps the ***Exception suffix, because it names a package rather than an error.
Applications deriving their own hierarchy use ExceptionBase; ToolingException marks “this came out
of pyTooling itself”.
Predefined Exceptions
Predefined exceptions of pyTooling.Exceptions.
Inheritance diagram:
EnvironmentVariableError
The EnvironmentVariableError is raised when an environment variable the program depends on is not set.
Use it where the variable is required: a program that cannot proceed without JAVA_HOME should say so once, by
name, instead of failing later with a path error that does not mention the variable at all.
from os import environ
from pyTooling.Exceptions import EnvironmentVariableError
if (javaHome := environ.get("JAVA_HOME")) is None:
raise EnvironmentVariableError("Environment variable 'JAVA_HOME' is not set.")
Hint
Until v9.0.0 this class was called EnvironmentException. It was not renamed to EnvironmentError,
because that name is a builtin — a deprecated alias of OSError — and would have shadowed
it on import.
ConfigurationError
The ConfigurationError is raised when a configuration is invalid: an unknown key, a value of the wrong type,
a setting that contradicts another one.
It is the base-exception for configuration problems in pyTooling and in packages building on it, so an
application reading configuration from several sources can catch them all with one clause instead of one per
package. pyTooling.Configuration derives KeyNotFoundError,
UnsupportedValueTypeError, InterpolationError and
PathExpressionError from it.
Attach the details as notes rather than folding them into the message — the value that was rejected, the file it came from, and the values that would have been accepted are three separate facts:
from pyTooling.Exceptions import ConfigurationError
ex = ConfigurationError(f"Unknown log level '{value}'.")
ex.add_note(f"Configuration file: {path}")
ex.add_note(f"Allowed values: {', '.join(levels)}")
raise ex
PlatformNotSupportedError
The PlatformNotSupportedError is raised when the program is running on a platform it has no implementation
for — a Windows-only registry lookup on Linux, a /proc reader on macOS.
Raise it where the branch would otherwise fall through silently, so the message names the platform instead of
leaving a NameError further down:
from pyTooling.Common import CurrentPlatform
from pyTooling.Exceptions import PlatformNotSupportedError
if CurrentPlatform.IsNativeWindows:
...
elif CurrentPlatform.IsNativeLinux:
...
else:
raise PlatformNotSupportedError(f"Platform '{CurrentPlatform}' is not supported.")
See also
pyTooling.Platform→ Detecting the current platform, and its own
UnknownPlatformErrorfor a platform that could not be identified at all.
NotConfiguredError
The NotConfiguredError is raised when a setting that has no default was never configured — the program knows
the setting exists, and knows it has no value.
It differs from ConfigurationError in what went wrong: a configuration error means a value was given and
rejected, while this one means no value was given at all.
from pyTooling.Exceptions import NotConfiguredError
if self._installationDirectory is None:
raise NotConfiguredError("Installation directory of tool 'GHDL' is not configured.")
MissingDependencyError
The MissingDependencyError is raised when an optional dependency is not installed. Unlike the other
predefined exceptions it derives from ImportError, so an except ImportError around
an optional import still catches it.
Some modules need a package pyTooling does not install by default. Importing such a module without its
dependency raises this exception, and it carries what to install and which extra provides it:
from pyTooling.Exceptions import MissingDependencyError
try:
from ruamel.yaml import YAML
except ImportError as ex: # pragma: no cover
raise MissingDependencyError(dependency="ruamel.yaml", extra="yaml") from ex
TerminalApplication prints it with
PrintMissingDependencyError(), which names the package and
the command that installs it — and, unlike the other exception printers, does not invite a bug report, because
nothing is wrong with the program.
OverloadResolutionError
The OverloadResolutionError is raised when no overload of a dispatched method matches the given arguments.
It derives from Exception directly, because it reports a call that cannot be resolved
rather than a failure inside a feature.
See also
- Base exception class
ExceptionBase Base class for all exceptions.
- Base exception class
ToolingException Base class for exceptions raised by pyTooling itself.