Source code for pcapkit.utilities.exceptions

# -*- coding: utf-8 -*-
"""User Defined Exceptions
=============================

:mod:`pcapkit.exceptions` refined built-in exceptions.
Make it possible to show only user error stack infomation [*]_,
when exception raised on user's operation.

.. [*] See |tbtrim|_ project for Pythonic implementation.

.. |tbtrim| replace:: ``tbtrim``
.. _tbtrim: https://github.com/gousaiyang/tbtrim

"""
import os
import struct
import sys
import traceback
from typing import TYPE_CHECKING

from pcapkit.utilities.compat import ModuleNotFoundError  # pylint: disable=redefined-builtin
from pcapkit.utilities.logging import DEVMODE, logger

if TYPE_CHECKING:
    from typing import Any

__all__ = [
    'stacklevel',

    'BaseError',                                                    # Exception
    'DigitError', 'IntError', 'RealError', 'ComplexError',          # TypeError
    'BoolError', 'BytesError', 'StringError', 'BytearrayError',     # TypeError
    'DictError', 'ListError', 'TupleError', 'IterableError',        # TypeError
    'IOObjError', 'ProtocolUnbound', 'CallableError',               # TypeError
    'InfoError', 'IPError', 'EnumError', 'ComparisonError',         # TypeError
    'RegistryError',                                                # TypeError
    'FormatError', 'UnsupportedCall',                               # AttributeError
    'FileError',                                                    # IOError
    'FileExists',                                                   # FileExistsError
    'FileNotFound',                                                 # FileNotFoundError
    'ProtocolNotFound',                                             # IndexError
    'VersionError', 'IndexNotFound', 'ProtocolError',               # ValueError
    'EndianError', 'KeyExists',                                     # ValueError
    'ProtocolNotImplemented', 'VendorNotImplemented',               # NotImplementedError
    'StructError',                                                  # struct.error
    'MissingKeyError', 'FragmentError', 'PacketError',              # KeyError
    'ModuleNotFound',                                               # ModuleNotFoundError
]


[docs]def stacklevel() -> 'int': """Fetch current stack level. The function will walk through the straceback stack (:func:`traceback.extract_stack`), and fetch the stack level where the path contains ``/pcapkit/``. So that it won't display any disturbing internal traceback information when raising errors. Returns: Stack level until internal stacks, i.e. contains ``/pcapkit/``. """ pcapkit = f'{os.path.sep}pcapkit{os.path.sep}' tb = traceback.extract_stack() for index, tbitem in enumerate(tb): if pcapkit in tbitem[0]: break else: index = len(tb) return index-1
############################################################################## # BaseError (abc of exceptions) session. ##############################################################################
[docs]class BaseError(Exception): """Base error class of all kinds. Important: * Turn off system-default traceback function by set :data:`sys.tracebacklimit` to ``0``. * But bugs appear in Python 3.6, so we have to set :data:`sys.tracebacklimit` to ``None``. .. note:: This note is deprecated since Python fixed the problem above. * In Python 2.7, :func:`trace.print_stack(limit)` dose not support negative limit. See Also: :func:`pcapkit.utilities.exceptions.stacklevel` """ def __init__(self, *args: 'Any', quiet: 'bool' = False, **kwargs: 'Any') -> 'None': # log error if not quiet: if DEVMODE: logger.error('%s: %s', type(self).__name__, str(self), exc_info=self, stack_info=True, stacklevel=-stacklevel()) # logger.error('%s: %s', type(self).__name__, str(self)) else: logger.error('%s: %s', type(self).__name__, str(self)) if not DEVMODE: sys.tracebacklimit = 0 super().__init__(*args, **kwargs)
############################################################################## # TypeError session. ##############################################################################
[docs]class DigitError(BaseError, TypeError): """The argument(s) must be (a) number(s)."""
[docs]class IntError(BaseError, TypeError): """The argument(s) must be integral."""
[docs]class RealError(BaseError, TypeError): """The function is not defined for real number."""
[docs]class ComplexError(BaseError, TypeError): """The function is not defined for complex instance."""
[docs]class BytesError(BaseError, TypeError): """The argument(s) must be :obj:`bytes` type."""
[docs]class BytearrayError(BaseError, TypeError): """The argument(s) must be :obj:`bytearray` type."""
[docs]class BoolError(BaseError, TypeError): """The argument(s) must be :obj:`bool` type."""
[docs]class StringError(BaseError, TypeError): """The argument(s) must be :obj:`str` type."""
[docs]class DictError(BaseError, TypeError): """The argument(s) must be :obj:`dict` type."""
[docs]class ListError(BaseError, TypeError): """The argument(s) must be :obj:`list` type."""
[docs]class TupleError(BaseError, TypeError): """The argument(s) must be :obj:`tuple` type."""
[docs]class IterableError(BaseError, TypeError): """The argument(s) must be *iterable*."""
[docs]class CallableError(BaseError, TypeError): """The argument(s) must be *callable*."""
[docs]class ProtocolUnbound(BaseError, TypeError): """Protocol slice unbound."""
[docs]class IOObjError(BaseError, TypeError): """The argument(s) must be *file-like object*."""
[docs]class InfoError(BaseError, TypeError): """The argument(s) must be :class:`~pcapkit.corekit.infoclass.Info` instance."""
[docs]class IPError(BaseError, TypeError): """The argument(s) must be *IP address*."""
[docs]class EnumError(BaseError, TypeError): """The argument(s) must be *enumeration protocol* type."""
[docs]class ComparisonError(BaseError, TypeError): """Rich comparison not supported between instances."""
[docs]class RegistryError(BaseError, TypeError): """The argument(s) must be *registry* type."""
############################################################################## # AttributeError session. ##############################################################################
[docs]class FormatError(BaseError, AttributeError): """Unknown format(s)."""
[docs]class UnsupportedCall(BaseError, AttributeError): """Unsupported function or property call."""
############################################################################## # IOError session. ##############################################################################
[docs]class FileError(BaseError, IOError): """[Errno 5] Wrong file format."""
# args: errno, strerror, filename, winerror, filename2 ############################################################################## # FileExistsError session. ##############################################################################
[docs]class FileExists(BaseError, FileExistsError): """[Errno 17] File already exists."""
# args: errno, strerror, filename, winerror, filename2 ############################################################################## # FileNotFoundError session. ##############################################################################
[docs]class FileNotFound(BaseError, FileNotFoundError): """[Errno 2] File not found."""
# args: errno, strerror, filename, winerror, filename2 ############################################################################## # IndexError session. ##############################################################################
[docs]class ProtocolNotFound(BaseError, IndexError): """Protocol not found in ProtoChain."""
############################################################################## # ValueError session. ##############################################################################
[docs]class VersionError(BaseError, ValueError): """Unknown IP version."""
[docs]class IndexNotFound(BaseError, ValueError): """Protocol not in ProtoChain."""
[docs]class ProtocolError(BaseError, ValueError): """Invalid protocol format."""
[docs]class EndianError(BaseError, ValueError): """Invalid endian (byte order)."""
[docs]class KeyExists(BaseError, ValueError): """Key already exists."""
############################################################################## # NotImplementedError session. ##############################################################################
[docs]class ProtocolNotImplemented(BaseError, NotImplementedError): """Protocol not implemented."""
[docs]class VendorNotImplemented(BaseError, NotImplementedError): """Vendor not implemented."""
############################################################################## # struct.error session. ##############################################################################
[docs]class StructError(BaseError, struct.error): """Unpack failed.""" def __init__(self, *args: 'Any', eof: 'bool' = False, **kwargs: 'Any') -> 'None': self.eof = eof super().__init__(*args, **kwargs)
############################################################################## # KeyError session. ##############################################################################
[docs]class MissingKeyError(BaseError, KeyError): """Key not found."""
[docs]class FragmentError(BaseError, KeyError): """Invalid fragment dict."""
[docs]class PacketError(BaseError, KeyError): """Invalid packet dict."""
############################################################################## # ModuleNotFoundError session. ##############################################################################
[docs]class ModuleNotFound(BaseError, ModuleNotFoundError): """Module not found."""
# kwargs: name, path