summaryrefslogtreecommitdiff
path: root/repoze/bfg/traversal.py
blob: ee66ae887ecc6bce3ff2fa599f188af7b3aebff5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import urllib

from zope.interface import classProvides
from zope.interface import implements
from zope.location.location import located
from zope.location.location import LocationIterator
from zope.location.interfaces import ILocation

from repoze.bfg.interfaces import ITraverser
from repoze.bfg.interfaces import ITraverserFactory

def split_path(path):
    if path.startswith('/'):
        path = path[1:]
    if path.endswith('/'):
        path = path[:-1]
    clean=[]
    for segment in path.split('/'):
        segment = urllib.unquote(segment) # deal with spaces in path segment
        if not segment or segment=='.':
            continue
        elif segment == '..':
            del clean[-1]
        else:
            clean.append(segment)
    return clean

def step(ob, name, default):
    if name.startswith('@@'):
        return name[2:], default
    if not hasattr(ob, '__getitem__'):
        return name, default
    try:
        return name, ob[name]
    except KeyError:
        return name, default

_marker = ()

class NaiveTraverser(object):
    classProvides(ITraverserFactory)
    implements(ITraverser)
    def __init__(self, root, request):
        self.root = root
        self.locatable = ILocation.providedBy(root)
        self.request = request

    def __call__(self, path):
        root = self.root
        path = split_path(path)

        ob = self.root
        name = ''

        while path:
            segment = path.pop(0)
            segment, next = step(ob, segment, _marker)
            if next is _marker:
                name = segment
                break
            if self.locatable:
                next = located(next, ob, segment)
            ob = next

        return ob, name, path

def find_interface(context, interface):
    """ Return an object providing 'interface' anywhere in the parent
    chain of 'context' or None if no object providing that interface
    can be found in the parent chain """
    for location in LocationIterator(context):
        if interface.providedBy(location):
            return location