Are python Exceptions as class attributes a bad thing? - python

I find myself often wanting to structure my exception classes like this:
# legends.py
class Error(Exception): pass
class Rick(object):
class Error(Error): pass
class GaveYouUp(Error): pass
class LetYouDown(Error): pass
class Michael(object):
class Error(Error): pass
class BlamedItOnTheSunshine(Error): pass
class BlamedItOnTheMoonlight(Error): pass
I have only seen this pattern used in Django (DoesNotExist) and it makes so much sense. Is there anything I'm missing, why most people seem to favor top-level Exceptions?
edit
I would use these classes for versatile granularity, e.g:
import legends
try:
do_stuff()
except legends.Michael.Error:
blame_it_on_the_boogie()
except legends.Rick.GaveYouUp:
let_you_down()
except legends.Error:
pass
except Exception as e:
raise Hell()

This is the exact pattern used by Django for certain ORM-related exceptions.
The advantage is that you can have an except clause which checks against a type accessed through an instance:
rick = Rick()
try:
rick.roll()
except rick.GaveYouUp:
never()
except rick.LetYouDown:
never_ever()
This doesn't look that useful here, but if rick were a function parameter, then it would potentially be rather useful.
This is also extremely useful in writing generic code which raises the exceptions:
GoddamStar(object):
def sing(self,tune):
raise self.Error()
class Rick(GoddamStar):
class Error(Error): pass
class GaveYouUp(Error): pass
class LetYouDown(Error): pass
class Michael(GoddamStar):
class Error(Error): pass
class BlamedItOnTheSunshine(Error): pass
class BlamedItOnTheMoonlight(Error): pass
rick = Rick()
try:
rick.sing()
except Rick.GaveYouUp:
never()
except Michael.Error:
never_ever()
Django's exceptions generally all derive from global base classes, so that you can also have a catch-all clause which still switches on a type of exception, in case your rick is of an unknown (or otherwise unprovided for) class.
The reason why this isn't much more common is that (a) it doesn't work in early-bound languages, which attract most of the book writers (b) it's moderately rare that this is useful to the user, and so application writers likely figure they aren't going to need it.

If you want to raise e.g. BlamedItOnTheSunshine outside of Micheal you would have to call it by raise Micheal.BlamedItOnTheSunshine('error text').
e.g.:
class A:
class E(Exception): pass
def __init__(self): raise A('error in A')
class B:
def __init__(self): raise A.E('error in B')
in this Example A and B are not related, but if you have a relation like:
class Interpret(object):
class LetsYouDown(Exception): pass
def __init__(self): raise self.LetsYouDown("I'm not Rick!")
class Michael(Interpret):
class BlameItOnTheSunshine(Exception): pass
def __init__(self): raise self.BlameItOnTheSunshine("It's not the Moon!")
class Rick(Interpret):
class NeverEver(Exception): pass
def __init__(self): print "Never Ever!"
and want now something like:
try:
for superstar in [Interpret, Michael, Rick]:
star_in_show = superstar()
except superstar.LetsYouDown:
print "Where's Rick?"
except superstar.BlameItOnTheSunshine:
print "Must be Michael!"
you will get an Error i would call a Liskov's Principle violation.
So one of the main reason's (polymorphism) for using OOP is somewhat compromised. But it
doesn't necesarrily mean you can't or shouldn't use it. Just be aware of the limitations.
i hope that cleared my initial cryptical reservations up.

Related

Unittest Abstract Class exception notImplementederror

I have an abstract class that has multiple #abstractmethod that raises NotImplementedError("Need to implement this").
How do I set up a test-case using python's builtin unittest?
I tried using #patch.multibyte but it is not working.
I dunno what you are trying to achieve by using #patch.multibyte, but if your goal is to test that you have to implement abstract methods in your concrete classes it's just a matter of using assertRaises.
Let's pretend to have an abstract class MyAbstractClass in module my_api.py:
import abc
class MyAbstractClass(abc.ABC):
#abc.abstractmethod
def method_1(self):
pass
Then you write the tests in my_api_tests.py:
from unittest import TestCase
from my_api import MyAbstractClass
class MyConcreteClassWithoutImplementations(MyAbstractClass):
pass
class MyConcreteClassWithImplementations(MyAbstractClass):
def method_1(self):
return 1
class MyAbstractClassTest(TestCase):
def test_cannot_instantiate_concrete_classes_if_abstract_method_are_not_implemented(self):
self.assertRaises(TypeError, lambda: MyConcreteClassWithoutImplementations())
def test_can_instantiate_concrete_classes_if_abstract_method_are_implemented(self):
error = None
try:
my_object = MyConcreteClassWithImplementations()
self.assertEqual(my_object.method_1(), 1)
except TypeError as e:
error = e
self.assertIsNone(error)
...but you are actually testing Python's API, not your own code, so such tests are not useful... you have to test your own business logic ;)

Documenting exceptions that can happen in other functions in python docstrings

In Python, should we document in the docstrings exceptions that can be raised in other functions/classes besides the ones that are raised in the body of current function/method?
Obs.: I'm considering the Google Python docstring style https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
It has been a long time since I don't play with Java, but there you would explicitly say what kind of exceptions that your method could raise with the "throws" keyword.
Eg.:
class MyException(Exception):
pass
class A(object):
def foo(self):
"""This class does foo
Returns:
Int: The number of foo.
Raises:
MyException - In case something happen
"""
if True:
raise MyException
return 0
class B(object):
def __init__(self):
self._a = A()
def bar(self):
"""This class does bar
Returns:
Int: number of bar
Raises:
MyException ????? Should this be here?
"""
return self._a.foo()
Yes, you should document that bar() (and foo()) can raise a MyException. This way it's immediately obvious for anyone who's about to use bar() that which exceptions can occur when calling it.

minimal class to help you raise exception?

What does the following class does:
class HException(Exception):
def __init__(self, value=''):
self.value = value
def __str__(self):
return self.value
I saw it being called by raise
This is custom exception for more precise designations problems.
Not just an abstract exception. But an exception has arisen in a particular subject area.
class MoneyTransactionException(Exception):
...
says that something wrong with money manipulate in program.
User defined exceptions

Raise exception while defining a class improperly

How can I write a mixin, which raises an Exception if the class which is using this specific mixin is not created properly.
If I do these checks and balances in the __init__ or __new__ methods of the mixin, Exception is raised when this erroneous class tries to create an instance. Which is late, ideally the exception needs to be thrown when the compiler detects a wrong class. (Assuming, how to detect if a class is acceptable or not is a trivial matter)
To Illustrate the question
class ASampleMixin:
"""
A sample docstring
"""
def a_method(self):
raise NotImplementedError
def class_rule(self):
if something is wrong:
return False
return True
# more methods
class AClass(ASampleMixin, BaseClass):
"""
This class should satisfy a condition specified in class_rule method of the mixin
"""
# some methods
I am right now performing the check in the init method of mixin. Which raises an exception if rule returns False. Now this needs to be done at the time AClass is read by interpreter and not when I try to create an instance of AClass.
Is it possible even in dynamically typed languages like Python 3.5?
This sounds as if you want to create a custom metaclass that performs the check upon creation of the class object. See the documentation for metaclasses.
A metaclass example as reference:
class CustomType(type):
def __call__(cls, *args, **kwargs):
if not CustomType.some_rule(kwargs.pop('some_attr', None)):
raise Exception('Abort! Abort!')
return super(CustomType, cls).__call__(*args, **kwargs)
#staticmethod
def some_rule(var):
if type(var) is not str:
return False
return True
class A(object):
__metaclass__ = CustomType
class B(A):
pass
b = B(some_attr='f') # all is well
b = B() # raises

Python, IoC, Exceptions and loose coupling

Say we have two classes, Class A with a custom error which is thrown frequently and it is part of its functionality.
#a.py
class AError(Exception):
"""This exception flags a functional error"""
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError() #This exception is raised eventually, business code removed for clarity
Class B, which uses class A to perform some operations.
#b.py
import a
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except a.AError:
pass #This was expected, here will go some business logic
This works great, but it becomes an issue when I have different types of A. Ideally, I'd like to fully decouple A from B so I can pass any class like A that satisfies the same interface, but I cannot due to the exception(as it is not part of the interface itself)
In C++ I would have a header file with the definition of my Interface plus the exceptions which the concrete classes will implement. How is this usually solved in Python? Or said another way, what is the most pythonic approach?
I thought the following options:
1. create module with exceptions and maybe a base class/metaclass (the C++/Java way)
#common.py
class AErrorBase(Exception):
pass
class AIface(object):
def work(self):
raise NotImplemented()
.
#a.py
import common
class AError(common.AErrorBase):
pass
class A(common.AIface):
def work(self):
"""Throws AError when it is tired"""
raise AError()
.
#b.py
import common
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except common.AErrorBase:
pass #This was expected
2. pass exception as an argument
#a.py
class AError(Exception):
pass
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
.
#b.py
class B(object):
def make_him_work(self, afected, ex_type):
try:
afected.work()
except ex_type:
pass #This was expected
3. Exception as an attribute of the Class so it becomes part of the interface.
#a.py
class A(object):
def work(self):
"""Throws AError when it is tired"""
raise AError()
class AError(Exception):
pass
.
#b.py
class B(object):
def make_him_work(self, afected):
try:
afected.work()
except afected.AError:
pass #This was expected
4. Dont use exception, just a return code. !C days are back!
Any other option? What do you find more "pythonic"?
Edit: Added comments to clarify the purpose of the exception. It needs to be handled in B
Note: This might perfectly be that I am approaching the problem with my old C++ background, I just want to know how do you apply IoC in python when we have exceptions. Feel free to say all my approaches are garbage and I should do it in another way
My class would look like this:
class A(object):
def can_work(self):
"returns True if an A can work otherwise False (e.g. is an A is tired)"
return ...
def work(self):
assert not self._is_tired, "Test if self.can_work() first!"
...
This way you allow the users of A to test whether they should use work.
The assertion is useful for debugging and making sure you or others did not forget about the Interface.
Class B will use A as follows:
class B(object):
def make_him_work(self, afected):
if afected.can_work():
afected.work()

Categories

Resources