1import re
2from itertools import ifilter
3from functools import wraps
4from twisted.web.resource import Resource, NoResource
5
6class _FakeResource(Resource):
7    _result = ''
8    isLeaf = True
9    def __init__(self, result):
10        Resource.__init__(self)
11        self._result = result
12    def render(self, request):
13        return self._result
14
15
16def maybeResource(f):
17    @wraps(f)
18    def inner(*args, **kwargs):
19        result = f(*args, **kwargs)
20        if not isinstance(result, Resource):
21            result = _FakeResource(result)
22        return result
23    return inner
24
25
26class APIResource(Resource):
27
28    _registry = None
29
30    def __init__(self, *args, **kwargs):
31        Resource.__init__(self, *args, **kwargs)
32        self._registry = []
33
34    def _get_callback(self, request):
35        filterf = lambda t:t[0] in (request.method, 'ALL')
36        path_to_check = getattr(request, '_remaining_path', request.path)
37        for m, r, cb in ifilter(filterf, self._registry):
38            result = r.search(path_to_check)
39            if result:
40                request._remaining_path = path_to_check[result.span()[1]:]
41                return cb, result.groupdict()
42        return None, None
43
44    def register(self, method, regex, callback):
45        self._registry.append((method, re.compile(regex), callback))
46
47    def unregister(self, method=None, regex=None, callback=None):
48        if regex is not None: regex = re.compile(regex)
49        for m, r, cb in self._registry[:]:
50            if not method or (method and m==method):
51                if not regex or (regex and r==regex):
52                    if not callback or (callback and cb==callback):
53                        self._registry.remove((m, r, cb))
54
55    def getChild(self, name, request):
56        r = self.children.get(name, None)
57        if r is None:
58            # Go into the thing
59            callback, args = self._get_callback(request)
60            if callback is None:
61                return NoResource()
62            else:
63                return maybeResource(callback)(request, **args)
64        else:
65            return r
66