[Repoze-checkins] r1140 - in repoze.debug/trunk: . repoze/debug repoze/debug/tests

Chris McDonough chrism at agendaless.com
Thu Jun 26 18:11:42 EDT 2008


Author: Chris McDonough <chrism at agendaless.com>
Date: Thu Jun 26 18:11:41 2008
New Revision: 1140

Log:
Add pdbpm middleware; recover from brownbag 0.3 (not all files added to dist).



Added:
   repoze.debug/trunk/repoze/debug/canary.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/pdbpm.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/settings.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/tests/
   repoze.debug/trunk/repoze/debug/tests/__init__.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/tests/test_canary.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/tests/test_pdbpm.py   (contents, props changed)
   repoze.debug/trunk/repoze/debug/tests/test_responselogger.py   (contents, props changed)
Removed:
   repoze.debug/trunk/repoze/debug/tests.py
Modified:
   repoze.debug/trunk/CHANGES.txt
   repoze.debug/trunk/README.txt
   repoze.debug/trunk/setup.py

Modified: repoze.debug/trunk/CHANGES.txt
==============================================================================
--- repoze.debug/trunk/CHANGES.txt	(original)
+++ repoze.debug/trunk/CHANGES.txt	Thu Jun 26 18:11:41 2008
@@ -1,4 +1,9 @@
-Next release
+0.4 (2008/06/25)
+
+  - Add pdbpm middleware for dropping into the post-mortem debugger
+    upon an exception (copied from repoze.errorlog).
+
+0.3 (2008/06/25)
 
   - Add 'canary' middleware for detecting environment dictionary
     leaks.  Add to your Paste config via 'egg:repoze.debug#canary'; it

Modified: repoze.debug/trunk/README.txt
==============================================================================
--- repoze.debug/trunk/README.txt	(original)
+++ repoze.debug/trunk/README.txt	Thu Jun 26 18:11:41 2008
@@ -147,6 +147,31 @@
 know you are leaking WSGI environment dictionaries.  Use e.g. Dozer to
 find reference leaks.
 
+pdbpm middleware
+=================
+
+The 'pdbpm' middleware, when in the WSGI pipeline, will drop your
+(foregrounded) server process into the pdb post-mortem debugger upon
+any exception.
+
+Configuration via Python
+------------------------
+
+Wire up the middleware in your application::
+
+ from repoze.debug.pdbpm import PostMortemDebug
+ middleware = PostMortemDebug(app)
+
+Configuration via Paste
+------------------------
+
+Use the 'egg:repoze.debug#pdbpm' entry point in your Paste
+configuration, eg.::
+
+      [pipeline:main]
+      pipeline = egg:Paste#cgitb
+                 egg:repoze.debug#pdbpm
+                 myapp
 
 Reporting Bugs / Development Versions
 -------------------------------------

Added: repoze.debug/trunk/repoze/debug/canary.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/canary.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1,14 @@
+class CanaryMiddleware:
+    def __init__(self, app):
+        self.app = app
+
+    def __call__(self, environ, start_response):
+        environ['repoze.debug.canary'] = Canary()
+        return self.app(environ, start_response)
+
+class Canary(object):
+    pass
+
+def make_middleware(app, global_conf):
+    """ Paste filter-app converter """
+    return CanaryMiddleware(app)

Added: repoze.debug/trunk/repoze/debug/pdbpm.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/pdbpm.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1,19 @@
+import pdb
+import sys
+
+# stolen partly from z3c.evalexception
+def PostMortemDebug(application):
+    """Middleware that catches exceptions and invokes pdb's
+    post-mortem debugging facility."""
+    def middleware(environ, start_response):
+        try:
+            return application(environ, start_response)
+        except:
+            pdb.post_mortem(sys.exc_info()[2])
+            raise
+
+    return middleware
+
+def make_middleware(app, global_conf):
+    return PostMortemDebug(app)
+

Added: repoze.debug/trunk/repoze/debug/settings.py
==============================================================================

Added: repoze.debug/trunk/repoze/debug/tests/__init__.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/tests/__init__.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1 @@
+# tests package

Added: repoze.debug/trunk/repoze/debug/tests/test_canary.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/tests/test_canary.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1,38 @@
+import unittest
+
+class TestCanaryMiddleware(unittest.TestCase):
+    def _getTargetClass(self):
+        from repoze.debug.canary import CanaryMiddleware
+        return CanaryMiddleware
+
+    def _makeOne(self, app):
+        klass = self._getTargetClass()
+        return klass(app)
+
+    def test_call(self):
+        def app(environ, start_response):
+            environ['app_saw'] = True
+            return True
+        mw = self._makeOne(app)
+        environ = {}
+        result = mw(environ, None)
+        self.assertEqual(result, True)
+        from repoze.debug.canary import Canary
+        self.failUnless(isinstance(environ['repoze.debug.canary'], Canary))
+        self.assertEqual(environ['app_saw'], True)
+        
+class TestMakeCanaryMiddleware(unittest.TestCase):
+    def _getFUT(self):
+        from repoze.debug.canary import make_middleware
+        return make_middleware
+
+    def test_maker(self):
+        f = self._getFUT()
+        mw = f(None, None)
+        self.assertEqual(mw.app, None)
+        
+        
+
+
+        
+        

Added: repoze.debug/trunk/repoze/debug/tests/test_pdbpm.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/tests/test_pdbpm.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1,74 @@
+import unittest
+
+class TestPDBPM(unittest.TestCase):
+    def _getFUT(self):
+        from repoze.debug.pdbpm import PostMortemDebug
+        return PostMortemDebug
+
+    def _makeOne(self, app):
+        f = self._getFUT()
+        return f(app)
+
+    def test_post_mortem_withexc(self):
+        app = DummyApplication(KeyError)
+        mw = self._makeOne(app)
+        fake_pdb = FakePDB()
+        try:
+            import repoze.debug.pdbpm
+            old_pdb = repoze.debug.pdbpm.pdb
+            repoze.debug.pdbpm.pdb = fake_pdb
+            environ = {}
+            self.assertRaises(KeyError, mw, environ, None)
+        finally:
+            repoze.debug.pdb = old_pdb
+        self.assertEqual(fake_pdb.called, True)
+
+    def test_post_mortem_noexc(self):
+        app = DummyApplication()
+        mw = self._makeOne(app)
+        fake_pdb = FakePDB()
+        try:
+            import repoze.debug.pdbpm
+            old_pdb = repoze.debug.pdbpm.pdb
+            repoze.debug.pdbpm.pdb = fake_pdb
+            environ = {}
+            result = mw(environ, None)
+        finally:
+            repoze.debug.pdbpm.pdb = old_pdb
+        self.assertEqual(fake_pdb.called, False)
+        self.assertEqual(result, ['hello world'])
+
+    def test_paste_constructor(self):
+        app = DummyApplication() 
+        from repoze.debug.pdbpm import make_middleware
+        mw = make_middleware(app, None)
+        fake_pdb = FakePDB()
+        try:
+            import repoze.debug.pdbpm
+            old_pdb = repoze.debug.pdbpm.pdb
+            repoze.debug.pdbpm.pdb = fake_pdb
+            environ = {}
+            result = mw(environ, None)
+        finally:
+            repoze.debug.pdbpm.pdb = old_pdb
+        self.assertEqual(fake_pdb.called, False)
+        self.assertEqual(result, ['hello world'])
+
+class FakePDB:
+    called = False
+    def post_mortem(self, *args):
+        self.called = True
+
+class DummyApplication:
+    def __init__(self, exc=None):
+        self.exc = exc
+
+    def __call__(self, environ, start_response):
+        if self.exc:
+            raise self.exc
+        self.environ = environ
+        self.start_response = start_response
+        return ['hello world']
+
+class DummyException(Exception):
+    pass

Added: repoze.debug/trunk/repoze/debug/tests/test_responselogger.py
==============================================================================
--- (empty file)
+++ repoze.debug/trunk/repoze/debug/tests/test_responselogger.py	Thu Jun 26 18:11:41 2008
@@ -0,0 +1,123 @@
+import unittest
+
+class TestResponseLoggingMiddleware(unittest.TestCase):
+    def _getTargetClass(self):
+        from repoze.debug.responselogger import ResponseLoggingMiddleware
+        return ResponseLoggingMiddleware
+
+    def _makeOne(self, *arg, **kw):
+        klass = self._getTargetClass()
+        return klass(*arg, **kw)
+
+    def _makeEnviron(self):
+        environ = {
+            'SERVER_NAME':'localhost',
+            'SERVER_PORT':'80',
+            'wsgi.version':(1,0),
+            'wsgi.multiprocess':False,
+            'wsgi.multithread':True,
+            'wsgi.run_once':False,
+            'wsgi.url_scheme':'http',
+            }
+        return environ
+
+    def test_call(self):
+        body = ['thebody']
+        app = DummyApp(body, '200 OK', [('HeaderKey', 'headervalue')])
+        logger = FakeLogger()
+        mw = self._makeOne(app, 0, logger)
+        environ = self._makeEnviron()
+        start_response = FakeStartResponse()
+        app_iter = mw(environ, start_response)
+        self.assertEqual(''.join(app_iter), 'thebody')
+        self.assertEqual(len(logger.logged), 2)
+        self.assertEqual(start_response.status, '200 OK')
+        self.assertEqual(len(start_response.headers), 1)
+        self.assertEqual(start_response.headers[0], ('HeaderKey','headervalue'))
+        self.assertEqual(start_response.exc_info, None)
+        self.assertEqual(app.called, True)
+
+    def test_call_overmaxbodylen(self):
+        body = ['thebody']
+        app = DummyApp(body, '200 OK', [('HeaderKey', 'headervalue')])
+        logger = FakeLogger()
+        mw = self._makeOne(app, 1, logger)
+        environ = self._makeEnviron()
+        start_response = FakeStartResponse()
+        app_iter = mw(environ, start_response)
+        self.assertEqual(''.join(app_iter), 'thebody')
+        self.assertEqual(len(logger.logged), 2)
+        self.failUnless('(truncated)' in logger.logged[1])
+
+    def test_call_contentlengthwrong(self):
+        body = ['thebody']
+        app = DummyApp(body, '200 OK', [('Content-Length', '1')])
+        logger = FakeLogger()
+        mw = self._makeOne(app, 1, logger)
+        environ = self._makeEnviron()
+        start_response = FakeStartResponse()
+        app_iter = mw(environ, start_response)
+        self.assertEqual(''.join(app_iter), 'thebody')
+        self.assertEqual(len(logger.logged), 2)
+        self.failUnless('WARNING-1' in logger.logged[1])
+
+    def test_call_sourceurl_in_response(self):
+        body = ['thebody']
+        app = DummyApp(body, '200 OK', [('Content-Length', '1')])
+        logger = FakeLogger()
+        mw = self._makeOne(app, 1, logger)
+        environ = self._makeEnviron()
+        start_response = FakeStartResponse()
+        app_iter = mw(environ, start_response)
+        self.assertEqual(''.join(app_iter), 'thebody')
+        self.assertEqual(len(logger.logged), 2)
+        self.failUnless('URL: http://localhost' in logger.logged[1])
+
+class TestMakeResponseLoggingMiddleware(unittest.TestCase):
+    def _getFUT(self):
+        from repoze.debug.responselogger import make_middleware
+        return make_middleware
+
+    def test_make_middleware_defaults(self):
+        f = self._getFUT()
+        app = DummyApp(None, None, None)
+        global_conf = {}
+        import tempfile
+        fn = tempfile.mktemp()
+        mw = f(app, global_conf, fn)
+        self.assertEqual(len(mw.logger.handlers), 1)
+        self.assertEqual(mw.max_bodylen, 0)
+
+    def test_make_middleware_nondefaults(self):
+        f = self._getFUT()
+        app = DummyApp(None, None, None)
+        global_conf = {}
+        import tempfile
+        fn = tempfile.mktemp()
+        mw = f(app, global_conf, fn, '0', '0')
+        self.assertEqual(len(mw.logger.handlers), 1)
+
+class FakeStartResponse:
+    def __call__(self, status, headers, exc_info=None):
+        self.status = status
+        self.headers = headers
+        self.exc_info = exc_info
+
+class FakeLogger:
+    def __init__(self):
+        self.logged = []
+        
+    def info(self, msg):
+        self.logged.append(msg)
+
+class DummyApp:
+    def __init__(self, body, status, headers):
+        self.body = body
+        self.status = status
+        self.headers = headers
+        
+    def __call__(self, environ, start_response):
+        start_response(self.status, self.headers)
+        self.called = True
+        return self.body
+    

Modified: repoze.debug/trunk/setup.py
==============================================================================
--- repoze.debug/trunk/setup.py	(original)
+++ repoze.debug/trunk/setup.py	Thu Jun 26 18:11:41 2008
@@ -12,7 +12,7 @@
 #
 ##############################################################################
 
-__version__ = '0.3'
+__version__ = '0.4'
 
 import os
 
@@ -58,6 +58,7 @@
         [paste.filter_app_factory]
         responselogger = repoze.debug.responselogger:make_middleware
         canary = repoze.debug.canary:make_middleware
+        pdbpm = repoze.debug.pdbpm:make_middleware
       """
       )
 


More information about the Repoze-checkins mailing list