# -*- coding: utf-8 -*-
# pytableaux, a multi-logic proof generator.
# Copyright (C) 2014-2023 Doug Owings.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
"""
pytableaux.errors
^^^^^^^^^^^^^^^^^
"""
from __future__ import annotations
from enum import Enum
from typing import TYPE_CHECKING, Callable, TypeVar
if TYPE_CHECKING:
from .lang import BiCoords
from typing import overload
_ExT = TypeVar('_ExT', bound = Exception)
_T = TypeVar('_T')
# No local imports!
# __all__ defined at the bottom.
# warnings
class RepeatValueWarning(UserWarning):
pass
# Base Errors
[docs]
class IllegalStateError(Exception):
pass
[docs]
class ProofTimeoutError(Exception):
pass
class TreePruningException(Exception):
pass
# ParseErrors
[docs]
class ParseError(Exception):
pass
class UndefinedPredicateError(ParseError):
def __init__(self, coords: BiCoords, *args):
self.coords = coords
super().__init__(*args)
[docs]
class UnboundVariableError(ParseError):
pass
[docs]
class BoundVariableError(ParseError):
pass
# AttributeErrors
[docs]
class MissingAttributeError(AttributeError):
pass
[docs]
class AttributeConflictError(AttributeError):
pass
# KeyErrors
[docs]
class DuplicateKeyError(KeyError):
pass
[docs]
class MissingKeyError(KeyError):
pass
# ValueErrors
[docs]
class DuplicateValueError(ValueError):
pass
[docs]
class MissingValueError(ValueError):
pass
[docs]
class ConfigError(ValueError):
pass
[docs]
class ModelValueError(ValueError):
pass
[docs]
class DenotationError(ModelValueError):
pass
# Tree pruning
class SkipDeparture(TreePruningException):
pass
class SkipNode(TreePruningException):
pass
def _thru(o): return o
def _len(o): return o if isinstance(o, int) else len(o)
class Emsg(Enum):
InstCheck = (TypeError,
"Expected instance of '{1}' but got type '{0}'", (type, _thru))
SubclsCheck = (TypeError,
"Expected subclass of '{1}' but got type '{0}'", 2)
NotSubclsCheck = (TypeError,
"Unexpected type '{0}', subclass of '{1}'", 2)
CantJsonify = (TypeError,
"Object of type {0} is not JSON serializable", (type,))
Type = TypeError,
Attribute = AttributeError,
ReadOnly = (AttributeError,
"'{0.__name__}' object attribute '{1}' is read-only", (type, str))
IndexOutOfRange = IndexError, 'Index out of range'
WrongValue = (ValueError,
"Value '{0}' does not match expected: '{1}'", 2)
WrongLength = (ValueError,
"Expected value of length {1} but got length {0}", (_len, _len))
ArityMismatch = (ValueError,
"{0} has arity {1} but received input of size {2}", (_thru, _len, _len))
ValueConflict = ValueError, "Value conflict: '{0}' conflicts with '{1}'", 2
ValueConflictFor = (ValueError,
"Value conflict for '{0}': '{1}' conflicts with existing '{2}'", 3)
BadAttrName = AttributeError, "Invalid attribute identifier: '{}'", (str,)
NotLogicsPackage = ValueError, "{0} not a registered logics package", 1
BadLogicModule = ValueError, "{0} not a value logic module", 1
MissingAttribute = MissingAttributeError, '{}', 1
AttributeConflict = (AttributeConflictError,
"Attribute conflict for '{0}': '{1}' conflicts with existing '{2}'", 3)
MissingKey = MissingKeyError,
DuplicateKey = DuplicateKeyError,
DuplicateValue = DuplicateValueError,
MissingValue = MissingValueError,
IllegalState = IllegalStateError,
ThreadRuning = IllegalStateError, "Background thread already running"
ThreadStopped = IllegalStateError, "Background thread not running"
Timeout = ProofTimeoutError, "Timeout of {}ms exceeded", (int,)
UnknownForSentence = (ModelValueError,
'Non-existent value {0} for sentence {1}', (str, str))
ConflictForSentence = (ModelValueError,
'Inconsistent value {0} for sentence {1}', (str, str))
ConflictForExtension = (ModelValueError,
'Cannot set value {0} for tuple {1} already in extension', (str, str))
ConflictForAntiExtension = (ModelValueError,
'Cannot set value {0} for tuple {1} already in anti-extension', (str, str))
ParseError = ParseError,
if TYPE_CHECKING:
@overload
def razr(*args): ...
cls: type[Exception]
class check:
@staticmethod
def inst(obj, classinfo: type[_T], err=None) -> _T:
if not isinstance(obj, classinfo):
raise Emsg.InstCheck(obj, classinfo) from err
return obj
@staticmethod
def subcls(cls: type, typeinfo: _T) -> _T:
try:
issub = issubclass(cls, typeinfo)
except TypeError:
issub = False
if not issub:
raise Emsg.SubclsCheck(cls, typeinfo)
return cls
@staticmethod
def callable(obj: _T) -> _T:
if not callable(obj):
raise Emsg.InstCheck(obj, Callable)
return obj
from warnings import warn as warn
# Some external assembly required.
class EmsgBase:
__slots__ = ()
def __init__(self, cls: type[_ExT], msg: str = None, fns = None):
if isinstance(cls, tuple):
cls = type(cls[0], cls[1:], {})
self.cls = cls
self.msg = msg
if fns is None:
fns = ()
elif isinstance(fns, int):
fns = (_thru,) * fns
self.fns = fns
def razr(*args):
raise self._makeas(self.cls, args)
self.razr = razr
def __call__(self, *args):
return self._makeas(self.cls, args)
def _makeas(self, cls: type[_ExT], args: tuple) -> _ExT:
return cls(*self._getargs(args))
def _getargs(self, args: tuple):
alen = len(self.fns)
if alen == 0 or len(args) < alen:
return args
return self.msg.format(
*(f(a) for f,a in zip(self.fns, args))
), *args[alen:]
__all__ = 'check', 'Emsg', *(
name for name, value in locals().items()
if isinstance(value, type) and issubclass(value, (Exception, Warning)))