@wraps for all decorators
[mygpo.git] / mygpo / decorators.py
blob49658cd466850b4a36896c9defbbb870503a494f
1 # -*- coding: utf-8 -*-
3 # gPodder - A media aggregator and podcast client
4 # Copyright (c) 2005-2009 Thomas Perl and the gPodder Team
6 # gPodder is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 3 of the License, or
9 # (at your option) any later version.
11 # gPodder is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program. If not, see <http://www.gnu.org/licenses/>.
20 from functools import wraps
22 from django.http import Http404
23 from django.shortcuts import render_to_response, get_object_or_404
24 from django.template import RequestContext
25 from django.http import HttpResponseForbidden, HttpResponseNotAllowed
26 import gc
28 def requires_token(token_name, denied_template=None):
29 """
30 returns a decorator that checks if the security token in the 'token' GET
31 parameter matches the requires token for the resource. The protected
32 resource is indicated by
33 * the username parameter passed to the decorated function
34 * token_name passed to this method
36 The decorated method is returned, if
37 * no token is required for the resource
38 * the token in the 'token' GET parameter matches the required token
40 If the passed token does not match
41 * the denied_template is rendered and returned if given
42 * HttpResponseForbidden is returned, if denied_template is not given
43 """
44 def decorator(fn):
45 @wraps(fn)
46 def tmp(request, username, *args, **kwargs):
48 from mygpo.users.models import User
49 user = User.get_user(username)
50 if not user:
51 raise Http404
53 token = getattr(user, token_name, '')
54 u_token = request.GET.get('token', '')
56 if token == '' or token == u_token:
57 return fn(request, username, *args, **kwargs)
59 else:
60 if denied_template:
61 return render_to_response(denied_template, {
62 'other_user': user
63 }, context_instance=RequestContext(request))
65 else:
66 return HttpResponseForbidden()
68 return tmp
69 return decorator
72 def allowed_methods(methods):
73 def decorator(fn):
74 @wraps(fn)
75 def tmp(request, *args, **kwargs):
76 if request.method in methods:
77 return fn(request, *args, **kwargs)
78 else:
79 return HttpResponseNotAllowed(methods)
81 return tmp
83 return decorator
86 def repeat_on_conflict(obj_names=[], reload_f=None):
87 """
88 In case of a CouchDB ResourceConflict, reloads the parameter with the
89 given name and repeats the function call until it succeeds.
90 When calling the function, the parameter that should be reloaded must be
91 given as a keyword-argument
92 """
93 from couchdbkit import ResourceConflict
95 def default_reload(obj):
96 return obj.__class__.get(obj._id)
98 reload_f = reload_f or default_reload
100 def decorator(f):
101 @wraps(f)
102 def tmp(*args, **kwargs):
103 while True:
104 try:
105 return f(*args, **kwargs)
106 break
107 except ResourceConflict:
108 for obj_name in obj_names:
109 obj = kwargs[obj_name]
110 kwargs[obj_name] = reload_f(obj)
112 return tmp
114 return decorator
117 def query_if_required():
118 """ If required, queries some resource before calling the function
120 The decorated method is expected to be bound and its class is
121 expected to have define the methods _needs_query() and _query().
124 def decorator(f):
125 @wraps(f)
126 def wrapper(self, *args, **kwargs):
128 if self._needs_query():
129 self._query()
131 return f(self, *args, **kwargs)
133 return wrapper
134 return decorator