import os
import time

from functools import wraps

from twill.commands import browser, save_html, show, showforms
from twill.errors import TwillAssertionError, TwillException
from twill.utils import print_form


__all__ = ('show_on_error', )


def show_on_error(func, clsname=None):
    """
    On ``TwillAssertionError``, show last page got by twill.

    On ``TwillException: no field matches``, call twill ``showforms`` command.

    By default, decorator show output in stdout. To store output somewhere in
    disk - set up ``TWILL_ERROR_DIR`` environment var, e.g. if some error
    cathced in tests ran by::

        TWILL_ERROR_DIR=/tmp/tddspry/ django-nosetests.py

    all output would be stored in ``/tmp/tddspry/`` dir.
    """
    def save_forms(filename):
        forms = browser.get_all_forms()
        stream = open(filename, 'w')

        for i, form in enumerate(forms):
            print_form(i, form, stream)

        stream.close()

    @wraps(func)
    def wrapper(*args, **kwargs):
        try:
            return func(*args, **kwargs)
        except (TwillAssertionError, TwillException), e:
            field_error = False

            # Ignore all ``TwillException`` errors, but "no field matches"
            if e.__class__ == TwillException:
                message = e.args[0]

                if not message.startswith('no field matches "'):
                    raise

                field_error = True

            cmd = field_error and showforms or show
            saved, showed = False, False

            if 'TWILL_ERROR_DIR' in os.environ:
                dirname = os.environ['TWILL_ERROR_DIR']

                if not os.path.isdir(dirname):
                    try:
                        os.mkdir(dirname)
                    except:
                        print('Cannot create directory at %r.') % dirname
                        cmd()

                        showed = True

                if not showed:
                    timestamp = int(time.time())

                    if clsname:
                        filename = '%s.%s.%s' % (func.__module__,
                                                    clsname,
                                                    func.__name__)
                    else:
                        filename = '%s.%s' % (func.__module__,
                                                func.__name__)

                    format = field_error and 'txt' or 'html'
                    filename = '%s-%d.%s' % (filename, timestamp, format)
                    filename = os.path.join(dirname, filename)

                    try:
                        if field_error:
                            save_forms(filename)
                        else:
                            save_html(filename)
                    except:
                        print('Cannot write to file at %r.') % filename
                        cmd()

                        showed = True
                    else:
                        print('Output saved to %r.') % filename
                        saved = True

            if not saved and not showed:
                cmd()

            raise

    return wrapper

@@ -222,25 +222,1923 @@
 = ('
-show_on_error', )
+django_request', 'show_on_error')%0A%0A%0Adef django_request(name, code=None):%0A    %22%22%22%0A    Makes a GET, POST, HEAD, OPTIONS, PUT or DELETE request (reads from%0A    %60%60name%60%60) with Django test client on the provided %60%60url%60%60 and returns a%0A    Response object, which is wrapped to work with Twill.%0A%0A    %60%60url%60%60 passed to this method auto-builds with %60%60build_url%60%60 method,%0A    so you can to provide %60%60args%60%60 and %60%60kwargs%60%60 args to reverse%0A    urlpattern name in %60%60url%60%60.%0A%0A    Decorator returns Django response.%0A    %22%22%22%0A    def decorator(obj, url, args=None, kwargs=None, data=None, follow=False,%0A                  **extra):%0A        # Make able to use Django syntax for requests with data%0A        if isinstance(args, dict) and not data:%0A            data = args%0A            args = None%0A%0A        check_links = extra.pop('check_links', False)%0A%0A        # Build URL%0A        url = obj.build_url(url, args, kwargs)%0A%0A        # Make request%0A        data = data or %7B%7D%0A        request = getattr(obj.client, name)%0A        response = request(url, data, follow=follow, **extra)%0A%0A        # Wrap Django response to work with Twill.%0A        obj.response_to_twill(response)%0A%0A        # Show some debug info%0A        print('%25s ==%3E %25s') %25 (name.upper(), browser.get_url())%0A%0A        # Check for code if needed%0A        if code:%0A            obj.code(code)%0A%0A        # Check for links if needed%0A        if check_links:%0A            obj.check_links()%0A%0A        return response%0A%0A    # Add fancy docstring to resulted method%0A    allnames = 'GET, POST, HEAD, OPTIONS, PUT or DELETE'%0A    deletes = (' (reads from', '%60%60name%60%60) ')%0A%0A    decorator.__doc__ = django_request.__doc__.replace(allnames, name.upper())%0A    decorator.__doc__ = decorator.__doc__.replace('Decorator', 'Method')%0A%0A    for delete in deletes:%0A        decorator.__doc__ = decorator.__doc__.replace(delete, '')%0A%0A    decorator.__name__ = code and '%25s%25d' %25 (name, code) or name%0A%0A    return decorator
 %0A%0A%0Ad

Add decorator to make function for doing requests with Django test client.