dalton 1.0
An httplib injection library for recording and playing back HTTP interactions.
License: MIT
Keywords: mechanize httplib crawling mock test

======
Dalton
======

"I want you to be nice, until it's time to not be nice."
    -- Dalton, Road-House

An httplib injection library for recording and playing back HTTP interactions.

Dalton monkey-patches two methods of httplib's HTTPConnection class to
intercept request/response interactions, and can play them back based on a
Dalton recording. To ease testing, the recording is generated Python code to
ease in customization of the response and to allow for branches in the
playback path.

Monkey-patched methods of HTTPConnection:
    - request
    - getresponse

Using the more verbose method to send/recieve requests with HTTPConnection is
not supported at this time.

**Note:** This is a first and early release, mainly so that I could use it
with mechanize to record/playback interactions. As mechanize only uses the
request/getresponse API on HTTPConnection, I have no interest in adding
intercept to the rest. Please feel free to fork this to add additional
features as I don't plan on adding them myself (though I will happily pull bug
fixes and feature additions with unit tests).

**Warning:** Dalton uses ``inspect.currentframe`` magic to derive the caller
which may only work on CPython (PyPy and Jython is untested).


Example
=======

Since dalton monkey-patches httplib, no modification is necessary of libraries
that utilize the supported methods.

::

    import dalton
    dalton.inject() # monkey-patch httplib
    
    from httplib import HTTPConnection
    h = HTTPConnection('www.google.com')
    
    # when recording, httplib capture is restricted by caller
    recorder = dalton.Recorder(caller=h)
    
    # record httplib calls in this block
    with recorder.recording():
        h.request('GET', '/')
        resp = h.getresponse()
        body = resp.read()
    
    # save the interaction
    recorder.save('google')

A folder called ``google`` will be created in the current directory for use
with dalton's playback facility.

Playing it back::
    
    import dalton
    dalton.inject() # monkey-patch httplib
    
    from httplib import HTTPConnection
    h = HTTPConnection('www.google.com')
    
    # load the player
    player = dalton.Player(caller=h, playback_dir='google')
    
    # run httplib calls against the player
    with player.playing():
        h.request('GET', '/')
        resp = h.getresponse()
        body = resp.read()
    
    # body is now the same as it was recorded, no calls to www.google.com
    # were made

This generates a directory ``google`` with the following layout:
    - ``__init__.py``
    - ``step_0_response.txt``

The contents of ``__init__.py`` contain the following generated playback
information::
    
    import os
    import dalton
    from dalton import FileWrapper

    here = os.path.abspath(os.path.dirname(__file__))

    class StepNumber0(object):
        recorded_request = {
            'headers':  {},
            'url': '/',
            'method': 'GET',
            'body': None,
        }
        recorded_response = {
            'headers':  [('x-xss-protection', '1; mode=block'),
                         ('transfer-encoding', 'chunked'),
                         (                    'set-cookie',
                                              'PREF=ID=ff; expires=Thu, 11-Apr-2013 20:19:35 GMT; path=/; domain=.google.com, NID=45=fU; expires=Wed, 12-Oct-2011 20:19:35 GMT; path=/; domain=.google.com; HttpOnly'),
                         ('expires', '-1'),
                         ('server', 'gws'),
                         ('cache-control', 'private, max-age=0'),
                         ('date', 'Tue, 12 Apr 2011 20:19:35 GMT'),
                         ('content-type', 'text/html; charset=ISO-8859-1')],
            'body': FileWrapper('step_0_response.txt', here),
            'status': 200,
            'reason': 'OK',
            'version': 11,
        }
        next_step = 'None'

        def handle_request(self, request):
            assert dalton.request_match(request, self.recorded_request)
            return (self.next_step, dalton.create_response(self.recorded_response))

This file can be modified after recordings to customize the playback, add
additional branches, etc.

Support
=======

Dalton is considered feature-complete as the project owner (Ben Bangert) has
no additional functionality or development beyond bug fixes planned. Bugs can
be filed on github, should be accompanied by a test case to retain current
code coverage, and should be in a Pull request when ready to be accepted into
the Dalton code-base.