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. Common bases classify the errors every data structure raises - a duplicate, an element already in a hierarchy, an element of another data structure - across packages.

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 hands its message to Exception, so ex.args is (message, ) and str(ex) returns the message, as for any Python exception. It adds three read-only properties:

  • Message — the message, the first element of args.

  • HasNotes — whether any note attached with add_note() exists. __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'."""

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”.

Common Bases

Some errors occur in every data structure: an element is added twice, an element is added to a hierarchy while it’s already part of one, an element is used together with an element of another data structure, an element isn’t a direct child of the element it’s removed from, or an element is removed while other elements still use it. A package raises its own exception for each - derived from its base-exception - and adds a common base as a second base-class. So a caller catches all duplicates of all packages by one clause, all errors of one package by its base-exception, or one error by its own class.

Common base

Raised when

In pyTooling

DuplicateElementError

an element of the same kind and key exists already.

DuplicateVertexError, DuplicateEdgeError, DuplicateBlockError, DuplicateEdgeError, DuplicateFunctionError

AlreadyInHierarchyError

an element is already part of a hierarchy.

AlreadyInTreeError

NotInSameHierarchyError

an element is used together with an element of another data structure: it’s valid, but its ownership is wrong.

NotInSameGraph, NotInSameTreeError, NotInSameListError

NotAChildError

an element is not a direct child of the element it’s removed from: a grandchild is in the same hierarchy, but not a child.

NotAChildError

InUseError

an element is removed from a data structure, but other elements still use it.

none yet (pyVHDLModel: LibraryInUseError)

from pyTooling.Exceptions import DuplicateElementError, ExceptionBase

class ModelError(ExceptionBase):
  """Base-exception of a data model."""

class DuplicateItemError(ModelError, DuplicateElementError):
  """The exception is raised when the model already contains an item of the same ID."""

try:
  model.AddItem(item)
except DuplicateElementError as ex:
  print(f"Skipped: {ex}")

Note

The common bases derive from Exception and have no initializer of their own. The package’s base-exception - ExceptionBase or ToolingException - stores the message, as without the common base.

Predefined Exceptions

Predefined exceptions of pyTooling.Exceptions.

Inheritance diagram:

Inheritance diagram of pyTooling.Exceptions

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 UnknownPlatformError for 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. It derives from ToolingException and ImportError: an except ImportError around an optional import still catches it, and so does an except ToolingException handling pyTooling’s own errors. Its name is the missing package.

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.