diff --git a/CHANGELOG.md b/CHANGELOG.md index 3c99703f7eae51e8cc1dbf2cbbece5a3e43148e8..5e518d9cc2fe0f4c658a43dbe06efaa190106618 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,6 @@ # v2.4.29 -* _TBA_ +* added `CustomException` # v2.4.28 diff --git a/docs/exceptions.rst b/docs/exceptions.rst new file mode 100644 index 0000000000000000000000000000000000000000..e0acbf5cd130e12c10f86f5f0fbcf92ea71d41f2 --- /dev/null +++ b/docs/exceptions.rst @@ -0,0 +1,17 @@ +Exceptions +========== + +CustomException +--------------- + +This is the class you can base your exceptions off. It provides +a reasonable __repr__ and __str__, eg.: + +:: + + class MyException(CustomException): + ... + + assert str(MyException) == 'MyException()' + +__repr__ will additionally prefix the exception class name with entire module path to it. diff --git a/docs/index.rst b/docs/index.rst index 7fa8dfbe450bdb0d13a093fcee0cc3914f32125b..24bc9dae55667c140f2b05ec9dc706d837776b92 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ Visit the project's page at GitHub_! import files time + exceptions Indices and tables diff --git a/satella/__init__.py b/satella/__init__.py index c67ecddd24b2e49a65d9f2aa8e5a45a2a229cc26..56cdd0bfd1ab6862afffa31b912f7d042fd1b858 100644 --- a/satella/__init__.py +++ b/satella/__init__.py @@ -1 +1 @@ -__version__ = '2.4.29a1' +__version__ = '2.4.29' diff --git a/satella/exceptions.py b/satella/exceptions.py index 55a4539c4a965da9a2b8ca2b19a32a556c3f41e2..4dfeb4525ea4a104715ff739e74640503548a3cc 100644 --- a/satella/exceptions.py +++ b/satella/exceptions.py @@ -1,23 +1,31 @@ -__all__ = ['BaseSatellaException', 'ResourceLockingError', 'ResourceNotLocked', 'ResourceLocked', +import warnings + +__all__ = ['BaseSatellaError', 'ResourceLockingError', 'ResourceNotLocked', 'ResourceLocked', 'ConfigurationValidationError', 'ConfigurationError', 'ConfigurationSchemaError', - 'PreconditionError', 'MetricAlreadyExists'] + 'PreconditionError', 'MetricAlreadyExists', 'BaseSatellaException', 'CustomException'] -class BaseSatellaException(Exception): - """"Base class for all Satella exceptions""" - def __init__(self, msg, *args, **kwargs): - super().__init__(*(msg, *args)) +class CustomException(Exception): + """" + Base class for your custom exceptions. It will: + + 1. Accept any number of arguments + 2. Provide faithful __repr__ and a reasonable __str__ + + It passed all arguments that your exception received via super() + """ + def __init__(self, *args, **kwargs): + super().__init__(*args) self.kwargs = kwargs - self.msg = msg - def __str__(self): - a = '%s(%s' % (self.__class__.__qualname__, self.args) + def __str__(self) -> str: + a = '%s(%s' % (self.__class__.__qualname__.split('.')[-1], ', '.join(map(repr, self.args))) if self.kwargs: - a += ', '+(', '.join(map(lambda k, v: '%s=%s' % (k, repr(v)), self.kwargs.items()))) + a += ', ' + ', '.join(map(lambda k, v: '%s=%s' % (k, repr(v)), self.kwargs.items())) a += ')' return a - def __repr__(self): + def __repr__(self) -> str: a = '%s%s(%s' % ((self.__class__.__module__ + '.') if self.__class__.__module__ != 'builtins' else '', self.__class__.__qualname__, @@ -25,11 +33,21 @@ class BaseSatellaException(Exception): if self.kwargs: a += ', ' + (', '.join(map(lambda kv: '%s=%s' % (kv[0], repr(kv[1])), self.kwargs.items()))) - a += ')' + a += ')' return a -class ResourceLockingError(BaseSatellaException): +class BaseSatellaError(CustomException): + """"Base class for all Satella exceptions""" + + +class BaseSatellaException(BaseSatellaError): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + warnings.warn('Use BaseSatellaError instead', DeprecationWarning) + + +class ResourceLockingError(BaseSatellaError): """Base class for resource locking issues""" @@ -41,13 +59,13 @@ class ResourceNotLocked(ResourceLockingError): """Locking given resource is needed in order to access it""" -class PreconditionError(BaseSatellaException, ValueError): +class PreconditionError(BaseSatellaError, ValueError): """ A precondition was not met for the argument """ -class ConfigurationError(BaseSatellaException): +class ConfigurationError(BaseSatellaError): """A generic error during configuration""" @@ -66,7 +84,7 @@ class ConfigurationValidationError(ConfigurationSchemaError): self.value = value -class MetricAlreadyExists(BaseSatellaException): +class MetricAlreadyExists(BaseSatellaError): """Metric with given name already exists, but with a different type""" def __init__(self, msg, name, requested_type, existing_type): diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 6fb9e94cf6d84dab711c5d005d0fa01b07a14169..f7cf4f3e5813a15fc08a3483333c5f15753d74f2 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -1,33 +1,22 @@ import unittest -from satella.exceptions import BaseSatellaException + +from satella.exceptions import BaseSatellaError, CustomException class TestExceptions(unittest.TestCase): def test_exception_kwargs(self): - e = BaseSatellaException('hello world', label='value') + e = BaseSatellaError('hello world', label='value') self.assertIn("label='value'", repr(e)) - def test_exception(self): - try: - raise BaseSatellaException('message', 'arg1', 'arg2') - except BaseSatellaException as e: - self.assertIn('arg1', str(e)) - self.assertIn('arg2', str(e)) - self.assertIn('BaseSatellaException', str(e)) - else: - self.fail() - def test_except_inherited(self): - class InheritedException(BaseSatellaException): + class InheritedError(CustomException): pass try: - raise InheritedException('message', 'arg1', 'arg2') - except BaseSatellaException as e: - self.assertIn('arg1', str(e)) - self.assertIn('arg2', str(e)) - self.assertIn('InheritedException', str(e)) + raise InheritedError('message', 'arg1', 'arg2') + except CustomException as e: + self.assertEqual(str(e), "InheritedError('message', 'arg1', 'arg2')") else: self.fail()