Cool monkey patch to make tracebacks more useful :-)
Hi All, Ever noticed how tracebacks from ZPT are actually quite useful when viewed through the error_log object, but totally useless when they fail, say, in a unit test? This bugged me, so I had a dig, and just discovered this cool monkey patch, to be inserted in any module that gets imported before an exception is raised: import traceback from zExceptions.ExceptionFormatter import format_exception traceback.format_exception = format_exception So now I'm mailing you lot for two reasons: 1. This makes life a lot easier when debugging failing unit tests and looking through entries in event.log that have tracebacks, so I thought I'd share it :-) 2. I wonder if there's anything bad that can happen as a result of this? If the answer to 2 is "no", then I'm gonna wrap this up into a minute product... cheers, Chris -- Simplistix - Content Management, Zope & Python Consulting - http://www.simplistix.co.uk
On 7/13/05, Chris Withers <chris@simplistix.co.uk> wrote:
Ever noticed how tracebacks from ZPT are actually quite useful when viewed through the error_log object, but totally useless when they fail, say, in a unit test?
Yeah.
This bugged me, so I had a dig, and just discovered this cool monkey patch, to be inserted in any module that gets imported before an exception is raised:
So, could this be in the unit test module itself, for example? And a question for you: Ever noticed how it's near impossible to figure out what actually causes an error when you run functional tests? Can you fix that too? :-) I used to do this in all my functional test cases: def getTraceback(self, response): return getattr(response._response, '_text_traceback', None) def assertResponse(self, response, status, message=''): if not response.getStatus() == status: tb = self.getTraceback(response) if tb is not None: raise self.failureException, tb else: raise ValueError, "Response had status %s, expected %s. %s" % ( response.getStatus(), status, message) And then end all my tests with: self.assertResponse(response, 200) And that _used_ to print out any exception that had occurred, but in newer version of Zope this never happens. There sinmply never is and _text_exception on the response anymore. Any ideas on how to get a useable exception report on unit tests? -- Lennart Regebro, Nuxeo http://www.nuxeo.com/ CPS Content Management http://www.cps-project.org/
Lennart Regebro wrote at 2005-7-13 12:47 +0200:
... Ever noticed how it's near impossible to figure out what actually causes an error when you run functional tests? Can you fix that too? :-)
I solve this with a browser emulation that does not use HTTP for requests but instead emulates "ZPublisher.Publish.publish" and therefore can handle exceptions itself. It somehow looks like this: def _internalRequest(self, path, query, method, headers, request_params, catch_exc, response, ): '''perform a request against the internal *url*.''' from Zope import zpublisher_exception_hook as err_hook from Zope import zpublisher_validated_hook as validated_hook try: # Note: we should use the checkpoint feature, new in ZODB 3.3 # Unfortunately, we still use ZODB 3.2... # Without this feature, we cannot reliably undo the effects # of failed requests (we try with subtransactions) request = self._makeRequest(path, query, method, headers, request_params) zope_response = request.response object = request.traverse(request['PATH_INFO'].strip(), validated_hook=validated_hook, ) result=mapply(object, request.args, request, call_object,1, missing_name, dont_publish_class, request, bind=1, ) if result is not zope_response: zope_response.setBody(result) get_transaction().commit(1) except: try: # Probably, we need special treatment for 'raise redirect' if not catch_exc or err_hook is None: raise response._setException() # we learned from Malcolm that 'err_hook' always raises an exception try: zope_response = err_hook(request['PARENTS'][0], *exc_info()) except: # Note: we do not handle "Retry" (as it is very unlikely) zope_response.exception(*exc_info()) finally: get_transaction().abort(1) response._setFromZope(zope_response) -- Dieter
Lennart Regebro wrote:
This bugged me, so I had a dig, and just discovered this cool monkey patch, to be inserted in any module that gets imported before an exception is raised:
So, could this be in the unit test module itself, for example?
Well, like I said, I'm gonna roll up a load of these type patches into a little product and release it :-) (the benefits of this product would only be missed if an error occurred before it was imported, and the benefits aren't likely to be very beneficial at that stage ;-)
Ever noticed how it's near impossible to figure out what actually causes an error when you run functional tests? Can you fix that too? :-)
Depends what you mean by "functional tests"!
I used to do this in all my functional test cases:
def getTraceback(self, response): return getattr(response._response, '_text_traceback', None)
def assertResponse(self, response, status, message=''): if not response.getStatus() == status: tb = self.getTraceback(response) if tb is not None: raise self.failureException, tb else: raise ValueError, "Response had status %s, expected %s. %s" % ( response.getStatus(), status, message)
Hmm, if something failed, I'd just consult the event log, which I always copy all errors to.
And then end all my tests with: self.assertResponse(response, 200)
Sounds good, but we use Zelenium ;-)
And that _used_ to print out any exception that had occurred, but in newer version of Zope this never happens. There sinmply never is and _text_exception on the response anymore. Any ideas on how to get a useable exception report on unit tests?
Ugh? on unit tests? my patch works exactly as I want on unit tests. What, specifically, are you referring to? cheers, Chris -- Simplistix - Content Management, Zope & Python Consulting - http://www.simplistix.co.uk
Chris Withers wrote at 2005-7-13 09:40 +0100:
Ever noticed how tracebacks from ZPT are actually quite useful when viewed through the error_log object, but totally useless when they fail, say, in a unit test?
This bugged me, so I had a dig, and just discovered this cool monkey patch, to be inserted in any module that gets imported before an exception is raised:
I had a similar experience but solved it by modifying "zLOG.EventLogger.log" in the following way: # DM 2005-06-27: Zope style tracebacks from zExceptions.ExceptionFormatter import format_exception .... # DM 2005-06-27: we now use Zope's traceback format because # it is more informative #self.logger.log(level, msg, exc_info=(error is not None)) exc_class = sys.exc_info()[0] if error and exc_class is not None: # Note: "error" is a boolean! msg += '\n' + ''.join(format_exception(*sys.exc_info())) self.logger.log(level, msg) Maybe, code like this should move into the Zope core? -- Dieter
Dieter Maurer wrote:
I had a similar experience but solved it by modifying "zLOG.EventLogger.log" in the following way:
# DM 2005-06-27: Zope style tracebacks from zExceptions.ExceptionFormatter import format_exception .... # DM 2005-06-27: we now use Zope's traceback format because # it is more informative #self.logger.log(level, msg, exc_info=(error is not None)) exc_class = sys.exc_info()[0] if error and exc_class is not None: # Note: "error" is a boolean! msg += '\n' + ''.join(format_exception(*sys.exc_info())) self.logger.log(level, msg)
I like this one too :-) That just leaves the traceback printed by pdb, anyone reckon they can get that to use the sensible formatter? ;-)
Maybe, code like this should move into the Zope core?
*shrugs* I'm quite happy with it as a product that I can add and remove as required... cheers, Chris -- Simplistix - Content Management, Zope & Python Consulting - http://www.simplistix.co.uk
Chris Withers wrote at 2005-7-15 20:33 +0100:
... That just leaves the traceback printed by pdb, anyone reckon they can get that to use the sensible formatter? ;-)
Do you get a traceback by "pdb"? I only get information of the form "*** TypeError: <exceptions.TypeError instance at 0x405e0cac>" This is even more stupid than Microsoft error messages. Today, I have been so annoyed by this that I decided to improve "pdb" in this respect and donate the patch as a feature for the Python core... -- Dieter
Dieter Maurer wrote:
Do you get a traceback by "pdb"?
I meant when I type "w" in a pdb session.
I only get information of the form "*** TypeError: <exceptions.TypeError instance at 0x405e0cac>"
This is even more stupid than Microsoft error messages.
Today, I have been so annoyed by this that I decided to improve "pdb" in this respect and donate the patch as a feature for the Python core...
Indeed, if you could post any patches you make here, I'd be happy to include them in my little patch product. cheers, Chris -- Simplistix - Content Management, Zope & Python Consulting - http://www.simplistix.co.uk
participants (3)
-
Chris Withers -
Dieter Maurer -
Lennart Regebro