# -*- 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 pcapkit.utilities.compat import ModuleNotFoundError # pylint: disable=redefined-builtin
__all__ = [
'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
'FormatError', 'UnsupportedCall', # AttributeError
'FileError', # IOError
'FileExists', # FileExistsError
'FileNotFound', # FileNotFoundError
'ProtocolNotFound', # IndexError
'VersionError', 'IndexNotFound', 'ProtocolError', # ValueError
'EndianError', # ValueError
'ProtocolNotImplemented', 'VendorNotImplemented', # NotImplementedError
'StructError', # struct.error
'FragmentError', 'PacketError', # KeyError
'ModuleNotFound', # ModuleNotFoundError
]
# boolean mappings
BOOLEAN_STATES = {'1': True, '0': False,
'yes': True, 'no': False,
'true': True, 'false': False,
'on': True, 'off': False}
#: Development mode (``DEVMODE``) flag.
DEVMODE = BOOLEAN_STATES.get(os.environ.get('PCAPKIT_DEVMODE', 'false').casefold(), False)
[docs]def stacklevel():
"""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:
int: 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`
"""
[docs] def __init__(self, *args, quiet=False, **kwargs):
if DEVMODE:
index = stacklevel()
if not quiet and index:
fmt_exc = traceback.format_exc(limit=-index)
if len(fmt_exc.splitlines(True)) > 1:
print(fmt_exc, file=sys.stderr)
else:
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."""
##############################################################################
# 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)."""
##############################################################################
# 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."""
##############################################################################
# KeyError session.
##############################################################################
[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