# -*- 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 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