X-Git-Url: https://git.mdrn.pl/wolnelektury.git/blobdiff_plain/0e87ae0739ed3e72301b7b718098f97a7f06a5d8..9c4c7a3325eedfcff4bb3550218b1f47275402b0:/apps/piston/resource.py diff --git a/apps/piston/resource.py b/apps/piston/resource.py deleted file mode 100644 index 40f065d05..000000000 --- a/apps/piston/resource.py +++ /dev/null @@ -1,260 +0,0 @@ -import sys, inspect - -from django.http import (HttpResponse, Http404, HttpResponseNotAllowed, - HttpResponseForbidden, HttpResponseServerError) -from django.views.debug import ExceptionReporter -from django.views.decorators.vary import vary_on_headers -from django.conf import settings -from django.core.mail import send_mail, EmailMessage -from django.db.models.query import QuerySet -from django.http import Http404 - -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 - -CHALLENGE = object() - -class Resource(object): - """ - Resource. Create one for your URL mappings, just - like you would with Django. Takes one argument, - the handler. The second argument is optional, and - is an authentication handler. If not specified, - `NoAuthentication` will be used by default. - """ - callmap = { 'GET': 'read', 'POST': 'create', - 'PUT': 'update', 'DELETE': 'delete' } - - def __init__(self, handler, authentication=None): - if not callable(handler): - raise AttributeError, "Handler not callable." - - self.handler = handler() - - if not authentication: - self.authentication = (NoAuthentication(),) - elif isinstance(authentication, (list, tuple)): - self.authentication = authentication - else: - self.authentication = (authentication,) - - # 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 - - @property - def anonymous(self): - """ - Gets the anonymous handler. Also tries to grab a class - if the `anonymous` value is a string, so that we can define - anonymous handlers that aren't defined yet (like, when - you're subclassing your basehandler into an anonymous one.) - """ - if hasattr(self.handler, 'anonymous'): - anon = self.handler.anonymous - - if callable(anon): - return anon - - for klass in typemapper.keys(): - if anon == klass.__name__: - return klass - - return None - - def authenticate(self, request, rm): - actor, anonymous = False, True - - for authenticator in self.authentication: - if not authenticator.is_authenticated(request): - if self.anonymous and \ - rm in self.anonymous.allowed_methods: - - actor, anonymous = self.anonymous(), True - else: - actor, anonymous = authenticator.challenge, CHALLENGE - else: - return self.handler, self.handler.is_anonymous - - return actor, anonymous - - @vary_on_headers('Authorization') - def __call__(self, request, *args, **kwargs): - """ - 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) - - actor, anonymous = self.authenticate(request, rm) - - if anonymous is CHALLENGE: - return actor() - else: - handler = actor - - # Translate nested datastructs into `request.data` here. - if rm in ('POST', 'PUT'): - try: - translate_mime(request) - except MimerDataException: - return rc.BAD_REQUEST - - if not rm in handler.allowed_methods: - return HttpResponseNotAllowed(handler.allowed_methods) - - meth = getattr(handler, self.callmap.get(rm), None) - - if not meth: - raise Http404 - - # Support emitter both through (?P) and ?format=emitter. - em_format = self.determine_emitter(request, *args, **kwargs) - - kwargs.pop('emitter_format', None) - - # Clean up the request object a bit, since we might - # very well have `oauth_`-headers in there, and we - # don't want to pass these along to the handler. - request = self.cleanup_request(request) - - try: - result = meth(request, *args, **kwargs) - except FormValidationError, e: - resp = rc.BAD_REQUEST - resp.write(' '+str(e.form.errors)) - - return resp - except TypeError, e: - result = rc.BAD_REQUEST - hm = HandlerMethod(meth) - sig = hm.signature - - msg = 'Method signature does not match.\n\n' - - if sig: - msg += 'Signature should be: %s' % sig - else: - msg += 'Resource does not expect any parameters.' - - if self.display_errors: - msg += '\n\nException was: %s' % str(e) - - result.content = format_error(msg) - except Http404: - return rc.NOT_FOUND - except HttpStatusCode, e: - return e.response - except Exception, e: - """ - On errors (like code errors), we'd like to be able to - give crash reports to both admins and also the calling - user. There's two setting parameters for this: - - Parameters:: - - `PISTON_EMAIL_ERRORS`: Will send a Django formatted - error email to people in `settings.ADMINS`. - - `PISTON_DISPLAY_ERRORS`: Will return a simple traceback - to the caller, so he can tell you what error they got. - - 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: - self.email_exception(rep) - if self.display_errors: - return HttpResponseServerError( - format_error('\n'.join(rep.format_exception()))) - else: - raise - - emitter, ct = Emitter.get(em_format) - fields = handler.fields - if hasattr(handler, 'list_fields') and ( - isinstance(result, list) or isinstance(result, QuerySet)): - fields = handler.list_fields - - srl = emitter(result, typemapper, 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) - - if not isinstance(stream, HttpResponse): - resp = HttpResponse(stream, mimetype=ct) - else: - resp = stream - - resp.streaming = self.stream - - return resp - except HttpStatusCode, e: - return e.response - - @staticmethod - def cleanup_request(request): - """ - Removes `oauth_` keys from various dicts on the - request object, and returns the sanitized version. - """ - for method_type in ('GET', 'PUT', 'POST', 'DELETE'): - block = getattr(request, method_type, { }) - - if True in [ k.startswith("oauth_") for k in block.keys() ]: - sanitized = block.copy() - - for k in sanitized.keys(): - if k.startswith("oauth_"): - sanitized.pop(k) - - setattr(request, method_type, sanitized) - - return request - - # -- - - def email_exception(self, reporter): - subject = "Piston crash report" - html = reporter.get_traceback_html() - - message = EmailMessage(settings.EMAIL_SUBJECT_PREFIX+subject, - html, settings.SERVER_EMAIL, - [ admin[1] for admin in settings.ADMINS ]) - - message.content_subtype = 'html' - message.send(fail_silently=True)