Catch exceptions inside a class - python

Is it possible to write an exception handler to catch the run-time errors generated by ALL the methods in class? I can do it by surrounding each one with try/except:
class MyError(Exception):
def __init__(self, obj, method):
print 'Debug info:', repr(obj.data), method.__name__
raise
class MyClass:
def __init__(self, data):
self.data = data
def f1(self):
try:
all method code here, maybe failing at run time
except:
raise MyError(self, self.f1)
I wonder if is there a more general way to achieve the same - for any error raising anywhere in the class. I would like to be able to access the class data to print some debug info.
Also, how do I get the failing method name (f1 in the example)?
Update: thanks to all for the insighful answers, the decorator idea looks like the way to go.
About the risk of trapping ALL the exceptions: a raise statement in the except branch should re-raise the exception without losing any information, doesn't it? That's why I put it in MyError also...

Warning: if you want something like this, it's likely you don't... but if you really want to...
Something like:
import functools
def catch_exception(f):
#functools.wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
print 'Caught an exception in', f.__name__
return func
class Test(object):
def __init__(self, val):
self.val = val
#catch_exception
def calc():
return self.val / 0
t = Test(3)
t.calc()
shows how to decorate individual functions. You can then create a class decorator to apply this decorator to each method (be careful of classmethod's/staticmethod's/properties etc...)

Assuming you've got a decorator catch_exception as in #Jon Clement's answer...
class ErrorCatcher(type):
def __new__(cls, name, bases, dct):
for m in dct:
if hasattr(dct[m], '__call__'):
dct[m] = catch_exception(dct[m])
return type.__new__(cls, name, bases, dct)
class Test(object):
__metaclass__ = ErrorCatcher
def __init__(self, val):
self.val = val
def calc(self):
return self.val / 0
The metaclass applies catch_exception to everything that appears to be a method while it is defining Test.
In response to a comment regarding custom messages for each method, one could attach such a message (or even a callback function to generate a message) as an attribute:
class Test(object):
__metaclass__ = ErrorCatcher
def __init__(self, val):
self.val = val
def calc(self):
return self.val / 0
calc.msg = "Dividing by 0 is ill-advised"
The catch_exception decorator would look for a msg attribute on its argument and use it, if found, in handling the exception.
This approach could be extended; instead of a string, msg could be a mapping of exception types to strings. In either case, the string could be replaced (with support from catch_exception, of course) with an arbitrary callback function that takes, say, the raised exception as an argument.
def calc_handler(exc):
# ...
calc.callback = calc_handler

A decorator would be a good solution here.
Here's an example of how you could do it:
import inspect
def catch_exception_decorator(function):
def decorated_function:
try:
function()
except:
raise MyError(self.__class__, inspect.stack()[1][3])
return decorated_function
class MyClass(object):
def __init__(self):
...
#catch_exception_decorator
def f1(self):
...
#catch_exception_decorator on top of the function is a shortcut for f1 = catch_exception_decorator(f1).
Instead of doing self.class, you could also access class data from the instance, as long as you're not shadowing variables. inspect.stack()[1][3] is the function name of the current function. You can use these to create the exception attributes.

Related

Calling property setter using event handler

I use following class to define event:
class Event(object):
def __init__(self):
self.handlers = set()
def handle(self, handler):
self.handlers.add(handler)
return self
def unhandle(self, handler):
try:
self.handlers.remove(handler)
except:
raise ValueError("Handler is not handling this event, so cannot unhandle it.")
return self
def fire(self, *args, **kwargs):
for handler in self.handlers:
print(handler)
handler(*args, **kwargs)
def getHandlerCount(self):
return len(self.handlers)
__iadd__ = handle
__isub__ = unhandle
__call__ = fire
__len__ = getHandlerCount
I have some model class defined like this:
class SomeModel(object):
def __init__(self):
self._foo = 0
self.fooChanged = Event()
#property
def foo(self):
return self._foo
#foo.setter
def foo(self, value):
self._foo = value
self.fooChanged(value)
Now, suppose that I want to change foo like this:
model = SomeModel()
other_model = SomeModel()
model.fooChanged += other_model.foo
model.foo = 1
After model.foo = 1, I get following error:
TypeError: 'int' object is not callable
Now, suppose that I use this code for defining model:
class SomeModel(object):
def __init__(self):
self._foo = 0
self.fooChanged = Event()
def get_foo(self):
return self._foo
def set_foo(self, value):
self._foo = value
self.fooChanged(value)
foo = property(get_foo, set_foo)
and this code to change the value of foo:
model = SomeModel()
other_model = SomeModel()
model.fooChanged += other_model.set_foo
model.foo = 1
Second version works fine, however, it seems little un-Pythonic to me. I have to define get_foo method, which I'd like to avoid (since properties are available). Is there some other workaround here, so first version of code could run?
Note: error will depend on self._foo type. If it's None, it will return error stating that NoneType is not callable, if it's string, error will state that str object is not callable.
After a lot of digging, I found this answer to be very informative and it pushed me in the right direction.
Using this knowledge, I was able to solve this problem by using:
model.fooChanged += lambda value: type(other_model).foo.__set__(other_model, value)
or
model.fooChanged += lambda value: type(other_model).foo.fset(other_model, value)
The later line looks more Pythonic to me, since no calls for double-underscore functions are made.
while you write model.fooChanged += other_model.foo, I guess what you actually want is its setter method, but as other_model.foo is a property object, you have to get from its class other_model.__class__.foo.fset, write as:
model.fooChanged += lambda value: other_model.__class__.foo.fset(other_model, value)
OTOH, I think your second version is pythonic to me, as:
Explicit is better than implicit.

How to proxy a class's methods dynamically?

I would like to proxy "all" methods of a class using following code:
import paramiko
class SFTPProxy():
def __init__(self, sftp):
self.sftp = TRANSPORT.open_sftp_client()
for x, y in type(self.sftp).__dict__.items():
if re.search(r'^__', x):
continue
def fn(self, *args, **kwargs):
return y(self.sftp, *args, **kwargs)
setattr(SFTPProxy, x, fn)
When I call the method like this:
fooproxy.sftp.listdir()
It works.
When I call the method like this:
fooproxy.listdir() # this is the method belongs to the proxied class
The program just hangs, is there any shallow problems in the code?
One issue I can see with your approach is that not all values in type(self.sftp).__dict__ are functions. Hence, y(...) will fail. Isn't it simpler and cleaner to override __getattr__:
class SFTPProxy(object):
def __init__(self, sftp):
self.sftp = TRANSPORT.open_sftp_client()
def __getattr__(self, item):
if hasattr(self.sftp, item):
return getattr(self.sftp, item)
raise AttributeError(item)
This will handle all kinds of attributes rather swiftly: instance/class fields, instance/class/static methods.

Using decorators inside a class

I am trying to implement a simple logging feature within my app.
class messages(object):
# Implement decorator here
def on(self):
def wrapper():
# Do something here
return wrapper
def off(self):
def wrapper():
# Do something here
return wrapper
class website(object):
#messages.on #This line can be switched on/off
def login(self):
# Do a whole bunch of stuff here
self.response = "[+] Login Succeeded!"
website = website()
website.login() # prints self.response based on #messages.on/off
But i am not sure what i need to apply in my decorator. I have tried creating instances and adding params but mostly receive TypeError. I am fairly new to decorators. Any help would be appreciated and i'd love to remember this for next time.
If you just want Dog to bark (like in the example), there is no need for enabling a decorator
class Dog(object):
def __init__(self):
self.sound = "Woof!"
def bark(self):
return self.sound
If you want to enable logging for some functions in class, here is a code that does that with explanation in comments
from functools import wraps
class Utilities(object):
#staticmethod # no default first argument in logger function
def logger(f): # accepts a function
#wraps(f) # good practice https://docs.python.org/2/library/functools.html#functools.wraps
def wrapper(self, *args, **kwargs): # explicit self, which means this decorator better be used inside classes only
print("Before:", self.sound)
result = f(self, *args, **kwargs)
print("After:", self.sound)
return result
return wrapper
class Dog(object):
def __init__(self):
self.sound = "Woof!"
#Utilities.logger
def run(self):
"""Perform running movement"""
print("Running")
Example:
>>> dog = Dog()
>>> dog.run()
Before: Woof!
Running
After: Woof!
Though after all there is no need to store decorators functionality in the Utilities class - it is better to have a separate module (file) named utils and put decorator there as a standalone function
Below is the sample decorator that you may use:
class Utilities:
#staticmethod
def add_logger(func):
def wrapped_func(*args, **kwargs):
# Sample logic, feel free to update this
try:
func_response = func(*args, **kwargs)
except:
print 'I am error handled by logger'
func_response = None
return func_response
return wrapped_func
Let's define your class now:
class Dog(object):
#Utilities.add_logger
def bark(self):
print 'In bark'
#Utilities.add_logger
def my_error_function(self):
print 'In my_error_function'
raise Exception # <--- Raises Exception
Now, let's check how it works:
>>> dog = Dog()
>>> dog.bark()
In bark
>>> dog.my_error_function()
In my_error_function
I am error handled by logger # Content from logger in case of exception
Note: You shouldn't really be creating a class here to store utility function. Create a separate utility file and write such functions over there.
Without class, your decorator should be like (let's say in utility.py):
def add_logger(func):
def wrapped_func(*args, **kwargs):
# Sample logic, feel free to update this
try:
func_response = func(*args, **kwargs)
except:
print 'I am error handled by logger'
func_response = None
return func_response
return wrapped_func
For using it, simply do:
import utility
class Dog(object):
#utility.add_logger
def bark(self):
print 'In bark'

How can I built in a trace ability to python calls?

Suppose I have some python code, e.g. some class defined somewhere, which cannot be modified
class MyClass(object):
def __init__(self, arg1, arg2):
do_something...
def foo(self):
do_something
Now I want to add a trace capability, e.g. some mechanism from outside that traces each and every method call for the above class. I want to be able to print out when e.g, __init__ has been called, or foo or even the __del__ method of MyClass.
Is this possible to do, and how is this done best?
Create a proxy class that wraps the original class and then delegates the work after printing a trace:
class MyClassProxy(object):
def __init__(*args, **kwds):
print 'Initializing'
self.o = MyClass(*args, **kwds)
def foo(self):
print 'Fooing'
return self.o.foo()
You can create a trace decorator and attach it to all the methods of the class instance or class definition as shown in decorate_methods function.
import functools
import inspect
import types
class TestClass(object):
def func1(self):
pass
def func2(self, a, b):
pass
def trace(func):
#functools.wraps(func)
def decorator(*args, **kwargs):
print "TRACE:", func.__name__, args, kwargs
return func(*args, **kwargs)
return decorator
def decorate_methods(obj, decorator):
for name, func in inspect.getmembers(obj):
if isinstance(func, types.MethodType):
setattr(obj, name, decorator(func))
# Apply the decorator to a class instance
test1 = TestClass()
decorate_methods(test1, trace)
test1.func1()
test1.func2('bar1', b='bar2')
# Apply the decorator to the class definition
decorate_methods(TestClass, trace)
test2 = TestClass()
test2.func1()
test2.func2('bar1', b='bar2')
The output of the script will be:
TRACE: func1 () {}
TRACE: func2 ('bar1',) {'b': 'bar2'}
TRACE: func1 (<__main__.TestClass object at 0x7f5a8d888150>,) {}
TRACE: func2 (<__main__.TestClass object at 0x7f5a8d888150>, 'bar1') {'b': 'bar2'}
Use decorator as shown below:
def call_trace(orig_func):
def decorated_func(*args, **kwargs):
print "========>In function: " + orig_func.__name__ + "<========"
orig_func(*args, **kwargs)
return decorated_func
Apply this decorator to trace the function. It prints function name before entering the function.
Ex:
#call_trace
def foo(self):
do_something
Hope it helps.
[Update]: You can use metaclass, only thing you got to change is to add "metaclass" parameter to your class as shown below. As you can see, below code applies "call_trace" decorator to every function in the class "ExBase".
I tried this out yesterday, it worked fine. I am also new to python.:)
def call_trace(orig_func):
def inner_func(*args, **kwargs):
print ("function name:" + str(orig_func.__name__))
orig_func(*args, **kwargs)
return inner_func
class ExMeta(type):
def __new__(cls, name, bases, attrs):
for attr in attrs:
if hasattr(attrs[attr], '__call__'):
attrs[attr] = call_trace(attrs[attr])
return type.__new__(cls, name, bases, attrs)
class ExBase(metaclass=ExMeta):
x = "x"
y = "y"
def __init__(self):
self.__name = "name"
def getname(self):
return self.__name
b = ExBase()
b.getname()
Get the code for OnlinePythonTutor from github.com/pgbovine/OnlinePythonTutor/tree/master/v3.
You don't need to bother with all the JS stuff. Extract the files into some directory. You can run your scripts using python /path/to/my/OnlinePythonTutor-master/v3/generate_json_trace my_script.py
This will give you basically everything your program is doing in a step by step manner. It will probably be overkill so if you want look into the source code and the underlying source in bdb http://docs.python.org/2/library/bdb.html. The docs for bdb are horrible so I'm having trouble figuring out what exactly is going on but I think this is a pretty cool problem, good luck.

Python introspection: Automatic wrapping of methods

object of type A and Is there a way to programatically wrap a class object?
Given
class A(object):
def __init__(self):
## ..
def f0(self, a):
## ...
def f1(self, a, b):
## ..
I want another class that wraps an A, such as
class B(object):
def __init__(self):
self.a = A()
def f0(self,a):
try:
a.f0(a)
except (Exception),ex:
## ...
def f1(self, a, b):
try:
a.f1(a,b)
except (Exception),ex:
## ...
Is there a way to do create B.f0 & B.f1 by reflection/inspection of class A?
If you want to create class B by calling a function on a predefined class A, you can simply do B = wrap_class(A) with a function wrap_class that looks like this:
import copy
def wrap_class(cls):
'Wraps a class so that exceptions in its methods are caught.'
# The copy is necessary so that mutable class attributes are not
# shared between the old class cls and the new class:
new_cls = copy.deepcopy(cls)
# vars() is used instead of dir() so that the attributes of base classes
# are not modified, but one might want to use dir() instead:
for (attr_name, value) in vars(cls).items():
if isinstance(value, types.FunctionType):
setattr(new_cls, attr_name, func_wrapper(value))
return new_cls
B = wrap_class(A)
As Jürgen pointed out, this creates a copy of the class; this is only needed, however, if you really want to keep your original class A around (like suggested in the original question). If you don't care about A, you can simply decorate it with a wrapper that does not perform any copy, like so:
def wrap_class(cls):
'Wraps a class so that exceptions in its methods are caught.'
# vars() is used instead of dir() so that the attributes of base classes
# are not modified, but one might want to use dir() instead:
for (attr_name, value) in vars(cls).items():
if isinstance(value, types.FunctionType):
setattr(cls, attr_name, func_wrapper(value))
return cls
#wrap_class
class A(object):
… # Original A class, with methods that are not wrapped with exception catching
The decorated class A catches exceptions.
The metaclass version is heavier, but its principle is similar:
import types
def func_wrapper(f):
'Returns a version of function f that prints an error message if an exception is raised.'
def wrapped_f(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception, ex:
print "Function", f, "raised", ex
return wrapped_f
class ExceptionCatcher(type):
'Metaclass that wraps methods with func_wrapper().'
def __new__(meta, cname, bases, cdict):
# cdict contains the attributes of class cname:
for (attr_name, value) in cdict.items():
if isinstance(value, types.FunctionType): # Various attribute types can be wrapped differently
cdict[attr_name] = func_wrapper(value)
return super(meta, ExceptionCatcher).__new__(meta, cname, bases, cdict)
class B(object):
__metaclass__ = ExceptionCatcher # ExceptionCatcher will be used for creating class A
class_attr = 42 # Will not be wrapped
def __init__(self):
pass
def f0(self, a):
return a*10
def f1(self, a, b):
1/0 # Raises a division by zero exception!
# Test:
b = B()
print b.f0(3.14)
print b.class_attr
print b.f1(2, 3)
This prints:
31.4
42
Function <function f1 at 0x107812d70> raised integer division or modulo by zero
None
What you want to do is in fact typically done by a metaclass, which is a class whose instances are a class: this is a way of building the B class dynamically based on its parsed Python code (the code for class A, in the question). More information on this can be found in the nice, short description of metaclasses given in Chris's Wiki (in part 1 and parts 2-4).
Meta classes are an option, but generally hard to understand. As is too much reflection
if not needed in simple cases, because it is easy to catch too many (internal) functions. If the wrapped functions are a stable known set, and B might gain other functions, you can delegate explicitly function by function and still keep your error handling code in one place:
class B(object):
def __init__(self):
a = A()
self.f0 = errorHandler(a.f0)
self.f1 = errorHandler(a.f1)
You might do the assignments in a loop if they are many, using getattr/setattr.
The errorhandler function will need to return a function which wraps its argument with
error handling code.
def errorHandler(f):
def wrapped(*args, **kw):
try:
return f(*args, **kw)
except:
# log or something
return wrapped
You can also use errorhandler as decorator on new functions not delegating to the A instance.
def B(A):
...
#errorHandler
def f_new(self):
...
This solution keeps B simple and it is quite explicit what's going on.
You could try it old-school with __getattr__:
class B(object):
def __init__(self):
self.a = A()
def __getattr__(self, name):
a_method = getattr(a, name, None)
if not callable(a_method):
raise AttributeError("Unknown attribute %r" % name)
def wrapper(*args, **kwargs):
try:
return a_method(*args, **kwargs)
except Exception, ex:
# ...
return wrapper
Or with updating B's dict:
class B(object):
def __init__(self):
a = A()
for attr_name in dir(a):
attr = getattr(a, attr_name)
if callable(attr):
def wrapper(*args, **kwargs):
try:
return attr(*args, **kwargs)
except Exception, ex:
# ...
setattr(self, attr_name, wrapper) # or try self.__dict__[x] = y

Categories

Resources