X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/d0ab1d8908cadac9f51a17e2fe9e1193f14e28cc..0e87ae0739ed3e72301b7b718098f97a7f06a5d8:/apps/piston/emitters.py diff --git a/apps/piston/emitters.py b/apps/piston/emitters.py new file mode 100644 index 000000000..1ff0a52da --- /dev/null +++ b/apps/piston/emitters.py @@ -0,0 +1,441 @@ +from __future__ import generators + +import decimal, re, inspect +import copy + +try: + # yaml isn't standard with python. It shouldn't be required if it + # isn't used. + import yaml +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 +from django.utils.xmlutils import SimplerXMLGenerator +from django.utils.encoding import smart_unicode +from django.core.urlresolvers import reverse, NoReverseMatch +from django.core.serializers.json import DateTimeAwareJSONEncoder +from django.http import HttpResponse +from django.core import serializers + +from utils import HttpStatusCode, Mimer + +try: + import cStringIO as StringIO +except ImportError: + import StringIO + +try: + import cPickle as pickle +except ImportError: + import pickle + +# Allow people to change the reverser (default `permalink`). +reverser = permalink + +class Emitter(object): + """ + Super emitter. All other emitters should subclass + this one. It has the `construct` method which + conveniently returns a serialized `dict`. This is + usually the only method you want to use in your + emitter. See below for examples. + + `RESERVED_FIELDS` was introduced when better resource + method detection came, and we accidentially caught these + as the methods on the handler. Issue58 says that's no good. + """ + EMITTERS = { } + RESERVED_FIELDS = set([ 'read', 'update', 'create', + 'delete', 'model', 'anonymous', + 'allowed_methods', 'fields', 'exclude' ]) + + def __init__(self, payload, typemapper, handler, fields=(), anonymous=True): + self.typemapper = typemapper + self.data = payload + self.handler = handler + self.fields = fields + self.anonymous = anonymous + + if isinstance(self.data, Exception): + raise + + def method_fields(self, handler, fields): + if not handler: + return { } + + ret = dict() + + for field in fields - Emitter.RESERVED_FIELDS: + t = getattr(handler, str(field), None) + + if t and callable(t): + ret[field] = t + + return ret + + def construct(self): + """ + Recursively serialize a lot of types, and + in cases where it doesn't recognize the type, + it will fall back to Django's `smart_unicode`. + + Returns `dict`. + """ + def _any(thing, fields=()): + """ + Dispatch, all types are routed through here. + """ + ret = None + + if isinstance(thing, QuerySet): + ret = _qs(thing, fields=fields) + elif isinstance(thing, (tuple, list)): + ret = _list(thing) + elif isinstance(thing, dict): + ret = _dict(thing) + elif isinstance(thing, decimal.Decimal): + ret = str(thing) + elif isinstance(thing, Model): + ret = _model(thing, fields=fields) + elif isinstance(thing, HttpResponse): + raise HttpStatusCode(thing) + elif inspect.isfunction(thing): + 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()) + elif repr(thing).startswith("