I have a piece of code that I don't control and while running it raises an error. I'd like to capture the value of exc object inside the exc_func method.
As you can see exc_func raises two exceptions, one of which is handled. What I care about is the value of the exc object, but so far have little luck retrieving it. The value does not exist in exc_traceback object and the exception message is not very helpful.
import traceback
import sys
def exc_func():
try:
a = 1
a.length()
except Exception as exc:
exc.with_not_existing()
def main():
try:
exc_func()
except Exception as exc:
exc_type, exc_value, exc_traceback = sys.exc_info()
tb_walk = traceback.walk_tb(exc_traceback)
# Need this in order to pickle traceback
tb_summary = traceback.StackSummary.extract(tb_walk, capture_locals=True)
if __name__ == '__main__':
main()
EDIT:
For instance, the exc object in main is AttributeError("'AttributeError' object has no attribute 'with_not_existing'"). What I really want to see is the exc object inside exc_func. Just to be clear, I need the exc object itself, something like traceback.format_exc() is not helpful in my case, due to the nature of the exception (it's a C lib that raises this exception)
When an exception is raised during handling another exception, the initial exception is stored as the __context__. It can be extracted when handling the new exception.
try:
exc_func()
except Exception as exc:
parent = exc.__context__ # the previously handled exception
print(type(parent), parent)
Note that an exception handler may also explicitly chain exceptions via __cause__.
Built-in Exceptions
[...]
When raising (or re-raising) an exception in an except or finally clause __context__ is automatically set to the last exception caught; if the new exception is not handled the traceback that is eventually displayed will include the originating exception(s) and the final exception.
The raise statement
[...]
The from clause is used for exception chaining: if given, the second expression must be another exception class or instance, which will then be attached to the raised exception as the __cause__ attribute (which is writable).
[...]
A similar mechanism works implicitly if an exception is raised inside an exception handler or a finally clause: the previous exception is then attached as the new exception’s __context__ attribute:
I made the following simple class in Visual studio community edition:
class Check(object):
def __init__(self):
self.t = 5
but when run
from Check import Check
try:
print(Check.t)
except Exception as Err:
print(str(Err))
or
import Check
try:
print(Check.t)
except Exception as Err:
print(str(Err))
I get the error of:
The object 'Check' has no attribute 't'
It is also weird because the keyword 'self' is not shown as Python keyword or Buildin func.
You've gotta instantiate the Object of Check before being able to access it.
This can be done by the following
from Check import Check
try:
print(Check().t)
except Exception as Err:
print(str(Err))
When you try to call Check.t it's trying to access the class element 't' which is non existent.
The information regarding visual studio community is redundant here.
The problem lies with how you're defining and calling your class.
You've created a class(the blueprint) but haven't created any instance (actual object) while calling the attribute (variable t).
Let's say you have a module named check.py where you've defined your simple class.
# check.py
class Check:
def __init__(self):
self.t = 5
Now import the class Check from module check.py
from check import Check
# create an instance of the class
ins = Check()
# access and print the attribute
try:
att = ins.t
print(att)
except Exception as err:
print(str(err))
I have to define an attribute in a class and I would like to manage error in the most pythonic way.
Here is the code I have tried so far. I can't figure out why I can not "reach" the exception in the following code.
# global variable to be used in the example
my_dict = {"key1": {"property": 10}, "key2": {}}
class Test(object):
#property
def my_attribute(self):
try:
return self._my_attribute
except AttributeError:
self._my_attribute = {}
for key, value in my_dict.items():
print(key)
self._my_attribute[key] = value['property']
except Exception:
print('error')
# I would like to manage my error here with a log or something
print("I am not reaching here")
finally:
return self._my_attribute
if __name__ == '__main__':
Test().my_attribute
I expected to reach the Exception case in the second iteration of the for loop since it is a KeyError ("key2" has no "property"). But it just passes by it. In this example, if the script is run, it does not print "I am not reaching here". Could anyone explain why I am seeing this wrong? Thanks!
The potential KeyError in self._my_attribute[key] = value['property'] is not covered by the except Exception block. Once it is raised the finally block is executed (as a matter of fact the finally block is always executed, regardless of an exception being raised or even handled). This can be easily verified by using a step-by-step debugger or with a simple print('finally') inside the finally block.
This is (among other reasons) why try blocks should be as minimal as possible. If you know that line might raise a KeyError then explicitly try-except it:
for key, value in my_dict.items():
print(key)
try:
self._my_attribute[key] = value['property']
except KeyError as e:
print('Key ', e, 'does not exist')
I'm trying to wrap all methods of an existing Class (not of my creation) into a try/except suite. It could be any Class, but I'll use the pandas.DataFrame class here as a practical example.
So if the invoked method succeeds, we simply move on. But if it should generate an exception, it is appended to a list for later inspection/discovery (although the below example just issues a print statement for simplicity).
(Note that the kinds of data-related exceptions that can occur when a method on the instance is invoked, isn't yet known; and that's the reason for this exercise: discovery).
This post was quite helpful (particularly #martineau Python-3 answer), but I'm having trouble adapting it. Below, I expected the second call to the (wrapped) info() method to emit print output but, sadly, it doesn't.
#!/usr/bin/env python3
import functools, types, pandas
def method_wrapper(method):
#functools.wraps(method)
def wrapper(*args, **kwargs): #Note: args[0] points to 'self'.
try:
print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
method.__name__))
return method(*args, **kwargs)
except Exception:
print('Exception: %r' % sys.exc_info()) # Something trivial.
#<Actual code would append that exception info to a list>.
return wrapper
class MetaClass(type):
def __new__(mcs, class_name, base_classes, classDict):
newClassDict = {}
for attributeName, attribute in classDict.items():
if type(attribute) == types.FunctionType: # Replace it with a
attribute = method_wrapper(attribute) # decorated version.
newClassDict[attributeName] = attribute
return type.__new__(mcs, class_name, base_classes, newClassDict)
class WrappedDataFrame2(MetaClass('WrappedDataFrame',
(pandas.DataFrame, object,), {}),
metaclass=type):
pass
print('Unwrapped pandas.DataFrame().info():')
pandas.DataFrame().info()
print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()
print()
This outputs:
Unwrapped pandas.DataFrame().info():
<class 'pandas.core.frame.DataFrame'>
Index: 0 entries
Empty DataFrame
Wrapped pandas.DataFrame().info(): <-- Missing print statement after this line.
<class '__main__.WrappedDataFrame2'>
Index: 0 entries
Empty WrappedDataFrame2
In summary,...
>>> unwrapped_object.someMethod(...)
# Should be mirrored by ...
>>> wrapping_object.someMethod(...)
# Including signature, docstring, etc. (i.e. all attributes); except that it
# executes inside a try/except suite (so I can catch exceptions generically).
long time no see. ;-) In fact it's been such a long time you may no longer care, but in case you (or others) do...
Here's something I think will do what you want. I've never answered your question before now because I don't have pandas installed on my system. However, today I decided to see if there was a workaround for not having it and created a trivial dummy module to mock it (only as far as I needed). Here's the only thing in it:
mockpandas.py:
""" Fake pandas module. """
class DataFrame:
def info(self):
print('pandas.DataFrame.info() called')
raise RuntimeError('Exception raised')
Below is code that seems to do what you need by implementing #Blckknght's suggestion of iterating through the MRO—but ignores the limitations noted in his answer that could arise from doing it that way). It ain't pretty, but as I said, it seems to work with at least the mocked pandas library I created.
import functools
import mockpandas as pandas # mock the library
import sys
import traceback
import types
def method_wrapper(method):
#functools.wraps(method)
def wrapper(*args, **kwargs): # Note: args[0] points to 'self'.
try:
print('Calling: {}.{}()... '.format(args[0].__class__.__name__,
method.__name__))
return method(*args, **kwargs)
except Exception:
print('An exception occurred in the wrapped method {}.{}()'.format(
args[0].__class__.__name__, method.__name__))
traceback.print_exc(file=sys.stdout)
# (Actual code would append that exception info to a list)
return wrapper
class MetaClass(type):
def __new__(meta, class_name, base_classes, classDict):
""" See if any of the base classes were created by with_metaclass() function. """
marker = None
for base in base_classes:
if hasattr(base, '_marker'):
marker = getattr(base, '_marker') # remember class name of temp base class
break # quit looking
if class_name == marker: # temporary base class being created by with_metaclass()?
return type.__new__(meta, class_name, base_classes, classDict)
# Temporarily create an unmodified version of class so it's MRO can be used below.
TempClass = type.__new__(meta, 'TempClass', base_classes, classDict)
newClassDict = {}
for cls in TempClass.mro():
for attributeName, attribute in cls.__dict__.items():
if isinstance(attribute, types.FunctionType):
# Convert it to a decorated version.
attribute = method_wrapper(attribute)
newClassDict[attributeName] = attribute
return type.__new__(meta, class_name, base_classes, newClassDict)
def with_metaclass(meta, classname, bases):
""" Create a class with the supplied bases and metaclass, that has been tagged with a
special '_marker' attribute.
"""
return type.__new__(meta, classname, bases, {'_marker': classname})
class WrappedDataFrame2(
with_metaclass(MetaClass, 'WrappedDataFrame', (pandas.DataFrame, object))):
pass
print('Unwrapped pandas.DataFrame().info():')
try:
pandas.DataFrame().info()
except RuntimeError:
print(' RuntimeError exception was raised as expected')
print('\n\nWrapped pandas.DataFrame().info():')
WrappedDataFrame2().info()
Output:
Unwrapped pandas.DataFrame().info():
pandas.DataFrame.info() called
RuntimeError exception was raised as expected
Wrapped pandas.DataFrame().info():
Calling: WrappedDataFrame2.info()...
pandas.DataFrame.info() called
An exception occurred in the wrapped method WrappedDataFrame2.info()
Traceback (most recent call last):
File "test.py", line 16, in wrapper
return method(*args, **kwargs)
File "mockpandas.py", line 9, in info
raise RuntimeError('Exception raised')
RuntimeError: Exception raised
As the above illustrates, the method_wrapper() decoratored version is being used by methods of the wrapped class.
Your metaclass only applies your decorator to the methods defined in classes that are instances of it. It doesn't decorate inherited methods, since they're not in the classDict.
I'm not sure there's a good way to make it work. You could try iterating through the MRO and wrapping all the inherited methods as well as your own, but I suspect you'd get into trouble if there were multiple levels of inheritance after you start using MetaClass (as each level will decorate the already decorated methods of the previous class).
I need to get the information contained in the exception. This is the code I use.
try:
result = yield user_collection.insert_many(content, ordered=False)
except BulkWriteError as e:
print (e)
And in my test when I get into the except with this line,
self.insert_mock.side_effect = [BulkWriteError('')]
it returns me
batch op errors occurred
instead of a MagicMock or a Mock.
How can I mock the BulkWriteError and give it a default return_value and see it when I use print(e)?
Something like this should allow you to test your print was called correctly.
import builtins # mockout print
class BulkWriteErrorStub(BulkWriteError):
''' Stub out the exception so you can bypass the constructor. '''
def __str__:
return 'fake_error'
#mock.patch.object('builtins', 'print')
def testRaisesBulkWrite(self, mock_print):
...
self.insert_mock.side_effect = [BuilkWriteErrorStub]
with self.assertRaises(...):
mock_print.assert_called_once_with('fake_error')
I haven't tested this so feel free to edit it if I made a mistake.