[snowy] Revert "Update piston to 92644a459862"



commit 5c3309fa7e257d8adebc06676b42c5d617930553
Author: Brad Taylor <brad getcoded net>
Date:   Fri Jul 24 10:43:25 2009 -0400

    Revert "Update piston to 92644a459862"
    
    This reverts commit 17845e37a399fae5619bb581b6c690451917efca.
    
    This update to Piston broke sync with Tomboy, so I'm reverting it so that we
    don't block our forward progress while we investigate the issue.

 lib/piston/authentication.py                     |   35 ++---
 lib/piston/doc.py                                |   82 +---------
 lib/piston/emitters.py                           |  126 +++-------------
 lib/piston/forms.py                              |    5 +-
 lib/piston/handler.py                            |   19 +--
 lib/piston/middleware.py                         |   20 ---
 lib/piston/oauth.py                              |    3 +-
 lib/piston/resource.py                           |  101 ++++--------
 lib/piston/templates/documentation.html          |   55 -------
 lib/piston/templates/piston/authorize_token.html |   15 --
 lib/piston/utils.py                              |  186 +++-------------------
 11 files changed, 101 insertions(+), 546 deletions(-)
---
diff --git a/lib/piston/authentication.py b/lib/piston/authentication.py
index c0bd1cc..2f325fa 100644
--- a/lib/piston/authentication.py
+++ b/lib/piston/authentication.py
@@ -1,6 +1,3 @@
-import binascii
-
-import oauth
 from django.http import HttpResponse, HttpResponseRedirect
 from django.contrib.auth.models import User, AnonymousUser
 from django.contrib.auth.decorators import login_required
@@ -11,7 +8,9 @@ from django.core.urlresolvers import get_callable
 from django.core.exceptions import ImproperlyConfigured
 from django.shortcuts import render_to_response
 from django.template import RequestContext
+from django.utils.importlib import import_module
 
+import oauth
 from piston import forms
 
 class NoAuthentication(object):
@@ -47,16 +46,13 @@ class HttpBasicAuthentication(object):
         if not auth_string:
             return False
             
-        try:
-            (authmeth, auth) = auth_string.split(" ", 1)
-
-            if not authmeth.lower() == 'basic':
-                return False
-
-            auth = auth.strip().decode('base64')
-            (username, password) = auth.split(':', 1)
-        except (ValueError, binascii.Error):
+        (authmeth, auth) = auth_string.split(" ", 1)
+        
+        if not authmeth.lower() == 'basic':
             return False
+            
+        auth = auth.strip().decode('base64')
+        (username, password) = auth.split(':', 1)
         
         request.user = self.auth_func(username=username, password=password) \
             or AnonymousUser()
@@ -69,6 +65,8 @@ class HttpBasicAuthentication(object):
         resp.status_code = 401
         return resp
 
+DataStore = None
+
 def load_data_store():
     '''Load data store for OAuth Consumers, Tokens, Nonces and Resources
     '''
@@ -77,9 +75,8 @@ def load_data_store():
     # stolen from django.contrib.auth.load_backend
     i = path.rfind('.')
     module, attr = path[:i], path[i+1:]
-
     try:
-        mod = __import__(module, {}, {}, attr)
+        mod = import_module(module)
     except ImportError, e:
         raise ImproperlyConfigured, 'Error importing OAuth data store %s: "%s"' % (module, e)
 
@@ -90,9 +87,6 @@ def load_data_store():
 
     return cls
 
-# Set the datastore here.
-oauth_datastore = load_data_store()
-
 def initialize_server_request(request):
     """
     Shortcut for initialization.
@@ -103,7 +97,11 @@ def initialize_server_request(request):
         query_string=request.environ.get('QUERY_STRING', ''))
         
     if oauth_request:
-        oauth_server = oauth.OAuthServer(oauth_datastore(oauth_request))
+        global DataStore
+        if DataStore is None:
+            DataStore = load_data_store()
+
+        oauth_server = oauth.OAuthServer(DataStore(oauth_request))
         oauth_server.add_signature_method(oauth.OAuthSignatureMethod_PLAINTEXT())
         oauth_server.add_signature_method(oauth.OAuthSignatureMethod_HMAC_SHA1())
     else:
@@ -145,7 +143,6 @@ def oauth_auth_view(request, token, callback, params):
         'oauth_token': token.key,
         'oauth_callback': callback,
         })
-
     return render_to_response('piston/authorize_token.html',
             { 'form': form }, RequestContext(request))
 
diff --git a/lib/piston/doc.py b/lib/piston/doc.py
index 441702f..02c54af 100644
--- a/lib/piston/doc.py
+++ b/lib/piston/doc.py
@@ -1,11 +1,5 @@
 import inspect, handler
 
-from piston.handler import typemapper
-
-from django.core.urlresolvers import get_resolver, get_callable, get_script_prefix
-from django.shortcuts import render_to_response
-from django.template import RequestContext
-
 def generate_doc(handler_cls):
     """
     Returns a `HandlerDocumentation` object
@@ -78,89 +72,19 @@ class HandlerDocumentation(object):
             met = getattr(self.handler, method)
             stale = inspect.getmodule(met) is handler
 
-            if not self.handler.is_anonymous:
-                if met and (not stale or include_default):
-                    yield HandlerMethod(met, stale)
-            else:
-                if not stale or met.__name__ == "read" \
-                    and 'GET' in self.allowed_methods:
-                    
-                    yield HandlerMethod(met, stale)
-        
-    def get_all_methods(self):
-        return self.get_methods(include_default=True)
+            if met and (not stale or include_default):
+                yield HandlerMethod(met, stale)
         
     @property
     def is_anonymous(self):
-        return handler.is_anonymous
+        return False
 
     def get_model(self):
         return getattr(self, 'model', None)
-            
-    def get_doc(self):
-        return self.handler.__doc__
     
-    doc = property(get_doc)
-
     @property
     def name(self):
         return self.handler.__name__
     
-    @property
-    def allowed_methods(self):
-        return self.handler.allowed_methods
-    
-    def get_resource_uri_template(self):
-        """
-        URI template processor.
-        
-        See http://bitworking.org/projects/URI-Templates/
-        """
-        def _convert(template, params=[]):
-            """URI template converter"""
-            paths = template % dict([p, "{%s}" % p] for p in params)
-            return u'%s%s' % (get_script_prefix(), paths)
-        
-        try:
-            resource_uri = self.handler.resource_uri()
-            
-            components = [None, [], {}]
-
-            for i, value in enumerate(resource_uri):
-                components[i] = value
-        
-            lookup_view, args, kwargs = components
-            lookup_view = get_callable(lookup_view, True)
-
-            possibilities = get_resolver(None).reverse_dict.getlist(lookup_view)
-            
-            for possibility, pattern in possibilities:
-                for result, params in possibility:
-                    if args:
-                        if len(args) != len(params):
-                            continue
-                        return _convert(result, params)
-                    else:
-                        if set(kwargs.keys()) != set(params):
-                            continue
-                        return _convert(result, params)
-        except:
-            return None
-        
-    resource_uri_template = property(get_resource_uri_template)
-    
     def __repr__(self):
         return u'<Documentation for "%s">' % self.name
-
-def documentation_view(request):
-    """
-    Generic documentation view. Generates documentation
-    from the handlers you've defined.
-    """
-    docs = [ ]
-
-    for handler, (model, anonymous) in typemapper.iteritems():
-        docs.append(generate_doc(handler))
-        
-    return render_to_response('documentation.html', 
-        { 'docs': docs }, RequestContext(request))
diff --git a/lib/piston/emitters.py b/lib/piston/emitters.py
index e82670d..7190ca1 100644
--- a/lib/piston/emitters.py
+++ b/lib/piston/emitters.py
@@ -1,6 +1,4 @@
-from __future__ import generators
-
-import decimal, re, inspect
+import types, decimal, types, re, inspect
 
 try:
     # yaml isn't standard with python.  It shouldn't be required if it
@@ -9,16 +7,6 @@ try:
 except ImportError:
     yaml = None
 
-# Fallback since `any` isn't in Python <2.5
-try:
-    any
-except NameError:
-    def any(iterable):
-        for element in iterable:
-            if element:
-                return True
-        return False
-
 from django.db.models.query import QuerySet
 from django.db.models import Model, permalink
 from django.utils import simplejson
@@ -26,9 +14,8 @@ from django.utils.xmlutils import SimplerXMLGenerator
 from django.utils.encoding import smart_unicode
 from django.core.serializers.json import DateTimeAwareJSONEncoder
 from django.http import HttpResponse
-from django.core import serializers
 
-from utils import HttpStatusCode, Mimer
+from utils import HttpStatusCode
 
 try:
     import cStringIO as StringIO
@@ -60,19 +47,6 @@ class Emitter(object):
         if isinstance(self.data, Exception):
             raise
     
-    def method_fields(self, data, fields):
-        if not data:
-            return { }
-
-        has = dir(data)
-        ret = dict()
-            
-        for field in fields:
-            if field in has:
-                ret[field] = getattr(data, field)
-        
-        return ret
-    
     def construct(self):
         """
         Recursively serialize a lot of types, and
@@ -87,9 +61,7 @@ class Emitter(object):
             """
             ret = None
             
-            if isinstance(thing, QuerySet):
-                ret = _qs(thing, fields=fields)
-            elif isinstance(thing, (tuple, list)):
+            if isinstance(thing, (tuple, list, QuerySet)):
                 ret = _list(thing)
             elif isinstance(thing, dict):
                 ret = _dict(thing)
@@ -98,14 +70,10 @@ class Emitter(object):
             elif isinstance(thing, Model):
                 ret = _model(thing, fields=fields)
             elif isinstance(thing, HttpResponse):
-                raise HttpStatusCode(thing)
-            elif inspect.isfunction(thing):
+                raise HttpStatusCode(thing.content, code=thing.status_code)
+            elif isinstance(thing, types.FunctionType):
                 if not inspect.getargspec(thing)[0]:
                     ret = _any(thing())
-            elif hasattr(thing, '__emittable__'):
-                f = thing.__emittable__
-                if inspect.ismethod(f) and len(inspect.getargspec(f)[0]) == 1:
-                    ret = _any(f())
             else:
                 ret = smart_unicode(thing, strings_only=True)
 
@@ -135,10 +103,9 @@ class Emitter(object):
             `exclude` on the handler (see `typemapper`.)
             """
             ret = { }
-            handler = self.in_typemapper(type(data), self.anonymous)
-            get_absolute_uri = False
             
-            if handler or fields:
+            if self.in_typemapper(type(data), self.anonymous) or fields:
+
                 v = lambda f: getattr(data, f.attname)
 
                 if not fields:
@@ -148,10 +115,7 @@ class Emitter(object):
                     """
                     mapped = self.in_typemapper(type(data), self.anonymous)
                     get_fields = set(mapped.fields)
-                    exclude_fields = set(mapped.exclude).difference(get_fields)
-
-                    if 'absolute_uri' in get_fields:
-                        get_absolute_uri = True
+                    exclude_fields = set(mapped.exclude)
                 
                     if not get_fields:
                         get_fields = set([ f.attname.replace("_id", "", 1)
@@ -161,7 +125,6 @@ class Emitter(object):
                     for exclude in exclude_fields:
                         if isinstance(exclude, basestring):
                             get_fields.discard(exclude)
-                            
                         elif isinstance(exclude, re._pattern_type):
                             for field in get_fields.copy():
                                 if exclude.match(field):
@@ -170,10 +133,8 @@ class Emitter(object):
                 else:
                     get_fields = set(fields)
 
-                met_fields = self.method_fields(handler, get_fields)
-
                 for f in data._meta.local_fields:
-                    if f.serialize and not any([ p in met_fields for p in [ f.attname, f.name ]]):
+                    if f.serialize:
                         if not f.rel:
                             if f.attname in get_fields:
                                 ret[f.attname] = _any(v(f))
@@ -184,14 +145,14 @@ class Emitter(object):
                                 get_fields.remove(f.name)
                 
                 for mf in data._meta.many_to_many:
-                    if mf.serialize and mf.attname not in met_fields:
+                    if mf.serialize:
                         if mf.attname in get_fields:
                             ret[mf.name] = _m2m(data, mf)
                             get_fields.remove(mf.name)
                 
                 # try to get the remainder of fields
                 for maybe_field in get_fields:
-                    
+
                     if isinstance(maybe_field, (list, tuple)):
                         model, fields = maybe_field
                         inst = getattr(data, model, None)
@@ -199,31 +160,19 @@ class Emitter(object):
                         if inst:
                             if hasattr(inst, 'all'):
                                 ret[model] = _related(inst, fields)
-                            elif callable(inst):
-                                if len(inspect.getargspec(inst)[0]) == 1:
-                                    ret[model] = _any(inst(), fields)
                             else:
                                 ret[model] = _model(inst, fields)
 
-                    elif maybe_field in met_fields:
-                        # Overriding normal field which has a "resource method"
-                        # so you can alter the contents of certain fields without
-                        # using different names.
-                        ret[maybe_field] = _any(met_fields[maybe_field](data))
-
                     else:                    
                         maybe = getattr(data, maybe_field, None)
                         if maybe:
-                            if callable(maybe):
-                                if len(inspect.getargspec(maybe)[0]) == 1:
-                                    ret[maybe_field] = _any(maybe())
-                            else:
+                            if isinstance(maybe, (int, basestring)):
                                 ret[maybe_field] = _any(maybe)
                         else:
-                            handler_f = getattr(handler or self.handler, maybe_field, None)
+                            handler_f = getattr(self.handler, maybe_field, None)
 
                             if handler_f:
-                                ret[maybe_field] = _any(handler_f(data))
+                                ret[maybe_field] = handler_f(data)
 
             else:
                 for f in data._meta.fields:
@@ -236,8 +185,8 @@ class Emitter(object):
                     ret[k] = _any(getattr(data, k))
             
             # resouce uri
-            if self.in_typemapper(type(data), self.anonymous):
-                handler = self.in_typemapper(type(data), self.anonymous)
+            if type(data) in self.typemapper.keys():
+                handler = self.typemapper.get(type(data))
                 if hasattr(handler, 'resource_uri'):
                     url_id, fields = handler.resource_uri()
                     ret['resource_uri'] = permalink( lambda: (url_id, 
@@ -248,18 +197,12 @@ class Emitter(object):
                 except: pass
             
             # absolute uri
-            if hasattr(data, 'get_absolute_url') and get_absolute_uri:
+            if hasattr(data, 'get_absolute_url'):
                 try: ret['absolute_uri'] = data.get_absolute_url()
                 except: pass
             
             return ret
         
-        def _qs(data, fields=()):
-            """
-            Querysets.
-            """
-            return [ _any(v, fields) for v in data ]
-                
         def _list(data):
             """
             Lists.
@@ -287,15 +230,6 @@ class Emitter(object):
         """
         raise NotImplementedError("Please implement render.")
         
-    def stream_render(self, request, stream=True):
-        """
-        Tells our patched middleware not to look
-        at the contents, and returns a generator
-        rather than the buffered string. Should be
-        more memory friendly for large datasets.
-        """
-        yield self.render(request)
-        
     @classmethod
     def get(cls, format):
         """
@@ -330,9 +264,7 @@ class XMLEmitter(Emitter):
     def _to_xml(self, xml, data):
         if isinstance(data, (list, tuple)):
             for item in data:
-                xml.startElement("resource", {})
                 self._to_xml(xml, item)
-                xml.endElement("resource")
         elif isinstance(data, dict):
             for key, value in data.iteritems():
                 xml.startElement(key, {})
@@ -356,7 +288,6 @@ class XMLEmitter(Emitter):
         return stream.getvalue()
 
 Emitter.register('xml', XMLEmitter, 'text/xml; charset=utf-8')
-Mimer.register(lambda *a: None, ('text/xml',))
 
 class JSONEmitter(Emitter):
     """
@@ -364,7 +295,7 @@ class JSONEmitter(Emitter):
     """
     def render(self, request):
         cb = request.GET.get('callback')
-        seria = simplejson.dumps(self.construct(), cls=DateTimeAwareJSONEncoder, ensure_ascii=False, indent=4)
+        seria = simplejson.dumps(self.construct(), cls=DateTimeAwareJSONEncoder)
 
         # Callback
         if cb:
@@ -373,7 +304,6 @@ class JSONEmitter(Emitter):
         return seria
     
 Emitter.register('json', JSONEmitter, 'application/json; charset=utf-8')
-Mimer.register(simplejson.loads, ('application/json',))
     
 class YAMLEmitter(Emitter):
     """
@@ -385,7 +315,6 @@ class YAMLEmitter(Emitter):
 
 if yaml:  # Only register yaml if it was import successfully.
     Emitter.register('yaml', YAMLEmitter, 'application/x-yaml; charset=utf-8')
-    Mimer.register(yaml.load, ('application/x-yaml',))
 
 class PickleEmitter(Emitter):
     """
@@ -394,21 +323,4 @@ class PickleEmitter(Emitter):
     def render(self, request):
         return pickle.dumps(self.construct())
         
-Emitter.register('pickle', PickleEmitter, 'application/python-pickle')
-Mimer.register(pickle.loads, ('application/python-pickle',))
-
-class DjangoEmitter(Emitter):
-    """
-    Emitter for the Django serialized format.
-    """
-    def render(self, request, format='xml'):
-        if isinstance(self.data, HttpResponse):
-            return self.data
-        elif isinstance(self.data, (int, str)):
-            response = self.data
-        else:
-            response = serializers.serialize(format, self.data, indent=True)
-
-        return response
-        
-Emitter.register('django', DjangoEmitter, 'text/xml; charset=utf-8')
+Emitter.register('pickle', PickleEmitter, 'application/octet-stream')
diff --git a/lib/piston/forms.py b/lib/piston/forms.py
index 0cf9d4b..8f1f1d7 100644
--- a/lib/piston/forms.py
+++ b/lib/piston/forms.py
@@ -1,4 +1,5 @@
-import hmac, base64
+import hmac
+import base64
 
 from django import forms
 from django.conf import settings
@@ -23,7 +24,7 @@ class ModelForm(forms.ModelForm):
 
 class OAuthAuthenticationForm(forms.Form):
     oauth_token = forms.CharField(widget=forms.HiddenInput)
-    oauth_callback = forms.CharField(widget=forms.HiddenInput)
+    oauth_callback = forms.URLField(widget=forms.HiddenInput)
     authorize_access = forms.BooleanField(required=True)
     csrf_signature = forms.CharField(widget=forms.HiddenInput)
 
diff --git a/lib/piston/handler.py b/lib/piston/handler.py
index 19acbee..f983c63 100644
--- a/lib/piston/handler.py
+++ b/lib/piston/handler.py
@@ -1,5 +1,4 @@
-from utils import rc
-from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
+from piston.utils import rc
 
 typemapper = { }
 
@@ -58,18 +57,8 @@ class BaseHandler(object):
     def read(self, request, *args, **kwargs):
         if not self.has_model():
             return rc.NOT_IMPLEMENTED
-
-        pkfield = self.model._meta.pk.name
-
-        if pkfield in kwargs:
-            try:
-                return self.model.objects.get(pk=kwargs.get(pkfield))
-            except ObjectDoesNotExist:
-                return rc.NOT_FOUND
-            except MultipleObjectsReturned: # should never happen, since we're using a PK
-                return rc.BAD_REQUEST
-        else:
-            return self.model.objects.filter(*args, **kwargs)
+        
+        return self.model.objects.filter(*args, **kwargs)
     
     def create(self, request, *args, **kwargs):
         if not self.has_model():
@@ -84,8 +73,6 @@ class BaseHandler(object):
             inst = self.model(**attrs)
             inst.save()
             return inst
-        except self.model.MultipleObjectsReturned:
-            return rc.DUPLICATE_ENTRY
     
     def update(self, request, *args, **kwargs):
         # TODO: This doesn't work automatically yet.
diff --git a/lib/piston/oauth.py b/lib/piston/oauth.py
index 1e50f1c..6090800 100644
--- a/lib/piston/oauth.py
+++ b/lib/piston/oauth.py
@@ -247,11 +247,10 @@ class OAuthRequest(object):
     @staticmethod
     def _split_header(header):
         params = {}
-        header = header.replace('OAuth ', '', 1)
         parts = header.split(',')
         for param in parts:
             # ignore realm parameter
-            if param.find('realm') > -1:
+            if param.find('OAuth realm') > -1:
                 continue
             # remove whitespace
             param = param.strip()
diff --git a/lib/piston/resource.py b/lib/piston/resource.py
index d78121e..bae36c2 100644
--- a/lib/piston/resource.py
+++ b/lib/piston/resource.py
@@ -1,7 +1,6 @@
 import sys, inspect
 
-from django.http import (HttpResponse, Http404, HttpResponseNotAllowed,
-    HttpResponseForbidden, HttpResponseServerError)
+from django.http import HttpResponse, Http404, HttpResponseNotAllowed, HttpResponseForbidden
 from django.views.debug import ExceptionReporter
 from django.views.decorators.vary import vary_on_headers
 from django.conf import settings
@@ -10,9 +9,16 @@ from django.core.mail import send_mail, EmailMessage
 from emitters import Emitter
 from handler import typemapper
 from doc import HandlerMethod
-from authentication import NoAuthentication
-from utils import coerce_put_post, FormValidationError, HttpStatusCode
-from utils import rc, format_error, translate_mime, MimerDataException
+from utils import coerce_put_post, FormValidationError, HttpStatusCode, rc, format_error
+
+class NoAuthentication(object):
+    """
+    Authentication handler that always returns
+    True, so no authentication is needed, nor
+    initiated (`challenge` is missing.)
+    """
+    def is_authenticated(self, request):
+        return True
 
 class Resource(object):
     """
@@ -39,24 +45,6 @@ class Resource(object):
         # Erroring
         self.email_errors = getattr(settings, 'PISTON_EMAIL_ERRORS', True)
         self.display_errors = getattr(settings, 'PISTON_DISPLAY_ERRORS', True)
-        self.stream = getattr(settings, 'PISTON_STREAM_OUTPUT', False)
-
-    def determine_emitter(self, request, *args, **kwargs):
-        """
-        Function for determening which emitter to use
-        for output. It lives here so you can easily subclass
-        `Resource` in order to change how emission is detected.
-
-        You could also check for the `Accept` HTTP header here,
-        since that pretty much makes sense. Refer to `Mimer` for
-        that as well.
-        """
-        em = kwargs.pop('emitter_format', None)
-        
-        if not em:
-            em = request.GET.get('format', 'json')
-
-        return em
     
     @vary_on_headers('Authorization')
     def __call__(self, request, *args, **kwargs):
@@ -64,45 +52,33 @@ class Resource(object):
         NB: Sends a `Vary` header so we don't cache requests
         that are different (OAuth stuff in `Authorization` header.)
         """
-        rm = request.method.upper()
-
-        # Django's internal mechanism doesn't pick up
-        # PUT request, so we trick it a little here.
-        if rm == "PUT":
-            coerce_put_post(request)
-
         if not self.authentication.is_authenticated(request):
-            if hasattr(self.handler, 'anonymous') and \
-                callable(self.handler.anonymous) and \
-                rm in self.handler.anonymous.allowed_methods:
-
+            if self.handler.anonymous and callable(self.handler.anonymous):
                 handler = self.handler.anonymous()
                 anonymous = True
             else:
                 return self.authentication.challenge()
         else:
             handler = self.handler
-            anonymous = handler.is_anonymous
+            anonymous = False
         
-        # Translate nested datastructs into `request.data` here.
-        if rm in ('POST', 'PUT'):
-            try:
-                translate_mime(request)
-            except MimerDataException:
-                return rc.BAD_REQUEST
+        rm = request.method.upper()
+        
+        # Django's internal mechanism doesn't pick up
+        # PUT request, so we trick it a little here.
+        if rm == "PUT":
+            coerce_put_post(request)
         
         if not rm in handler.allowed_methods:
             return HttpResponseNotAllowed(handler.allowed_methods)
         
-        meth = getattr(handler, self.callmap.get(rm), None)
+        meth = getattr(handler, Resource.callmap.get(rm), None)
         
         if not meth:
             raise Http404
 
         # Support emitter both through (?P<emitter_format>) and ?format=emitter.
-        em_format = self.determine_emitter(request, *args, **kwargs)
-
-        kwargs.pop('emitter_format', None)
+        em_format = kwargs.pop('emitter_format', request.GET.get('format', 'json'))
         
         # Clean up the request object a bit, since we might
         # very well have `oauth_`-headers in there, and we
@@ -111,9 +87,9 @@ class Resource(object):
         
         try:
             result = meth(request, *args, **kwargs)
-        except FormValidationError, e:
+        except FormValidationError, form:
             # TODO: Use rc.BAD_REQUEST here
-            return HttpResponse("Bad Request: %s" % e.form.errors, status=400)
+            return HttpResponse("Bad Request: %s" % form.errors, status=400)
         except TypeError, e:
             result = rc.BAD_REQUEST
             hm = HandlerMethod(meth)
@@ -131,8 +107,7 @@ class Resource(object):
                 
             result.content = format_error(msg)
         except HttpStatusCode, e:
-            #result = e ## why is this being passed on and not just dealt with now?
-            return e.response
+            result = e
         except Exception, e:
             """
             On errors (like code errors), we'd like to be able to
@@ -148,36 +123,24 @@ class Resource(object):
             If `PISTON_DISPLAY_ERRORS` is not enabled, the caller will
             receive a basic "500 Internal Server Error" message.
             """
-            exc_type, exc_value, tb = sys.exc_info()
-            rep = ExceptionReporter(request, exc_type, exc_value, tb.tb_next)
             if self.email_errors:
+                exc_type, exc_value, tb = sys.exc_info()
+                rep = ExceptionReporter(request, exc_type, exc_value, tb.tb_next)
+
                 self.email_exception(rep)
+
             if self.display_errors:
-                return HttpResponseServerError(
-                    format_error('\n'.join(rep.format_exception())))
+                result = format_error('\n'.join(rep.format_exception()))
             else:
                 raise
 
         emitter, ct = Emitter.get(em_format)
         srl = emitter(result, typemapper, handler, handler.fields, anonymous)
-
+        
         try:
-            """
-            Decide whether or not we want a generator here,
-            or we just want to buffer up the entire result
-            before sending it to the client. Won't matter for
-            smaller datasets, but larger will have an impact.
-            """
-            if self.stream: stream = srl.stream_render(request)
-            else: stream = srl.render(request)
-
-            resp = HttpResponse(stream, mimetype=ct)
-
-            resp.streaming = self.stream
-
-            return resp
+            return HttpResponse(srl.render(request), mimetype=ct)
         except HttpStatusCode, e:
-            return e.response
+            return HttpResponse(e.message, status=e.code)
 
     @staticmethod
     def cleanup_request(request):
diff --git a/lib/piston/utils.py b/lib/piston/utils.py
index b197ded..a992ef2 100644
--- a/lib/piston/utils.py
+++ b/lib/piston/utils.py
@@ -1,4 +1,5 @@
-from django.http import HttpResponseNotAllowed, HttpResponseForbidden, HttpResponse, HttpResponseBadRequest
+from functools import wraps
+from django.http import HttpResponseNotAllowed, HttpResponseForbidden, HttpResponse
 from django.core.urlresolvers import reverse
 from django.core.cache import cache
 from django import get_version as django_version
@@ -6,7 +7,7 @@ from decorator import decorator
 
 from datetime import datetime, timedelta
 
-__version__ = '0.2.2'
+__version__ = '0.2'
 
 def get_version():
     return __version__
@@ -15,43 +16,31 @@ def format_error(error):
     return u"Piston/%s (Django %s) crash report:\n\n%s" % \
         (get_version(), django_version(), error)
 
-class rc_factory(object):
+def create_reply(message, status=200):
+    return HttpResponse(message, status=status)
+
+class rc(object):
     """
     Status codes.
     """
-    CODES = dict(ALL_OK = ('OK', 200),
-                 CREATED = ('Created', 201),
-                 DELETED = ('', 204), # 204 says "Don't send a body!"
-                 BAD_REQUEST = ('Bad Request', 400),
-                 FORBIDDEN = ('Forbidden', 401),
-                 NOT_FOUND = ('Not Found', 404),
-                 DUPLICATE_ENTRY = ('Conflict/Duplicate', 409),
-                 NOT_HERE = ('Gone', 410),
-                 NOT_IMPLEMENTED = ('Not Implemented', 501),
-                 THROTTLED = ('Throttled', 503))
-
-    def __getattr__(self, attr):
-        """
-        Returns a fresh `HttpResponse` when getting 
-        an "attribute". This is backwards compatible
-        with 0.2, which is important.
-        """
-        try:
-            (r, c) = self.CODES.get(attr)
-        except TypeError:
-            raise AttributeError(attr)
-
-        return HttpResponse(r, content_type='text/plain', status=c)
-    
-rc = rc_factory()
+    ALL_OK = create_reply('OK', status=200)
+    CREATED = create_reply('Created', status=201)
+    DELETED = create_reply('', status=204) # 204 says "Don't send a body!"
+    BAD_REQUEST = create_reply('Bad Request', status=400)
+    FORBIDDEN = create_reply('Forbidden', status=401)
+    DUPLICATE_ENTRY = create_reply('Conflict/Duplicate', status=409)
+    NOT_HERE = create_reply('Gone', status=410)
+    NOT_IMPLEMENTED = create_reply('Not Implemented', status=501)
+    THROTTLED = create_reply('Throttled', status=503)
     
 class FormValidationError(Exception):
     def __init__(self, form):
         self.form = form
 
 class HttpStatusCode(Exception):
-    def __init__(self, response):
-        self.response = response
+    def __init__(self, message, code=200):
+        self.message = message
+        self.code = code
 
 def validate(v_form, operation='POST'):
     @decorator
@@ -59,6 +48,7 @@ def validate(v_form, operation='POST'):
         form = v_form(getattr(request, operation))
     
         if form.is_valid():
+#            kwa.update({ 'form': form })
             return f(self, request, *a, **kwa)
         else:
             raise FormValidationError(form)
@@ -125,138 +115,10 @@ def throttle(max_requests, timeout=60*60, extra=''):
     return wrap
 
 def coerce_put_post(request):
-    """
-    Django doesn't particularly understand REST.
-    In case we send data over PUT, Django won't
-    actually look at the data and load it. We need
-    to twist its arm here.
-    
-    The try/except abominiation here is due to a bug
-    in mod_python. This should fix it.
-    """
     if request.method == "PUT":
-        try:
-            request.method = "POST"
-            request._load_post_and_files()
-            request.method = "PUT"
-        except AttributeError:
-            request.META['REQUEST_METHOD'] = 'POST'
-            request._load_post_and_files()
-            request.META['REQUEST_METHOD'] = 'PUT'
-            
+        request.method = "POST"
+        request._load_post_and_files()
+        request.method = "PUT"
         request.PUT = request.POST
+        del request._post
 
-
-class MimerDataException(Exception):
-    """
-    Raised if the content_type and data don't match
-    """
-    pass
-
-class Mimer(object):
-    TYPES = dict()
-    
-    def __init__(self, request):
-        self.request = request
-        
-    def is_multipart(self):
-        content_type = self.content_type()
-
-        if content_type is not None:
-            return content_type.lstrip().startswith('multipart')
-
-        return False
-
-    def loader_for_type(self, ctype):
-        """
-        Gets a function ref to deserialize content
-        for a certain mimetype.
-        """
-        for loadee, mimes in Mimer.TYPES.iteritems():
-            for mime in mimes:
-                if ctype.startswith(mime):
-                    return loadee
-
-    def content_type(self):
-        """
-        Returns the content type of the request in all cases where it is
-        different than a submitted form - application/x-www-form-urlencoded
-        """
-        type_formencoded = "application/x-www-form-urlencoded"
-
-        ctype = self.request.META.get('CONTENT_TYPE', type_formencoded)
-        
-        if ctype.startswith(type_formencoded):
-            return None
-        
-        return ctype
-        
-
-    def translate(self):
-        """
-        Will look at the `Content-type` sent by the client, and maybe
-        deserialize the contents into the format they sent. This will
-        work for JSON, YAML, XML and Pickle. Since the data is not just
-        key-value (and maybe just a list), the data will be placed on
-        `request.data` instead, and the handler will have to read from
-        there.
-        
-        It will also set `request.content_type` so the handler has an easy
-        way to tell what's going on. `request.content_type` will always be
-        None for form-encoded and/or multipart form data (what your browser sends.)
-        """    
-        ctype = self.content_type()
-        self.request.content_type = ctype
-        
-        if not self.is_multipart() and ctype:
-            loadee = self.loader_for_type(ctype)
-            
-            try:
-                self.request.data = loadee(self.request.raw_post_data)
-                
-                # Reset both POST and PUT from request, as its
-                # misleading having their presence around.
-                self.request.POST = self.request.PUT = dict()
-            except (TypeError, ValueError):
-                raise MimerDataException
-
-        return self.request
-                
-    @classmethod
-    def register(cls, loadee, types):
-        cls.TYPES[loadee] = types
-        
-    @classmethod
-    def unregister(cls, loadee):
-        return cls.TYPES.pop(loadee)
-
-def translate_mime(request):
-    request = Mimer(request).translate()
-    
-def require_mime(*mimes):
-    """
-    Decorator requiring a certain mimetype. There's a nifty
-    helper called `require_extended` below which requires everything
-    we support except for post-data via form.
-    """
-    @decorator
-    def wrap(f, self, request, *args, **kwargs):
-        m = Mimer(request)
-        realmimes = set()
-
-        rewrite = { 'json':   'application/json',
-                    'yaml':   'application/x-yaml',
-                    'xml':    'text/xml',
-                    'pickle': 'application/python-pickle' }
-
-        for idx, mime in enumerate(mimes):
-            realmimes.add(rewrite.get(mime, mime))
-
-        if not m.content_type() in realmimes:
-            return rc.BAD_REQUEST
-
-        return f(self, request, *args, **kwargs)
-    return wrap
-
-require_extended = require_mime('json', 'yaml', 'xml', 'pickle')
-    



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]