sources for linker.py [rev. unknown]
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import py
import os
html = py.xml.html
# this here to serve two functions: first it makes the proto part of the temp
# urls (see TempLinker) customizable easily (for tests and such) and second
# it makes sure the temp links aren't replaced in generated source code etc.
# for this file (and its tests) itself.
TEMPLINK_PROTO = 'apigen.temp'
def getrelfspath(dotted_name):
    # XXX need to make sure its imported on non-py lib 
    return eval(dotted_name, {"py": py})
class LazyHref(object):
    def __init__(self, linker, linkid):
        self._linker = linker
        self._linkid = linkid
    def __unicode__(self):
        return unicode(self._linker.get_target(self._linkid))
class Linker(object):
    fromlocation = None
    def __init__(self):
        self._linkid2target = {}
    def get_lazyhref(self, linkid):
        return LazyHref(self, linkid)
    def set_link(self, linkid, target):
        assert (linkid not in self._linkid2target,
                'linkid %r already used' % (linkid,))
        self._linkid2target[linkid] = target
    def get_target(self, linkid):
        linktarget = self._linkid2target[linkid]
        if self.fromlocation is not None:
            linktarget = relpath(self.fromlocation, linktarget)
        return linktarget
    def call_withbase(self, base, func, *args, **kwargs):
        assert self.fromlocation is None
        self.fromlocation = base 
        try:
            return func(*args, **kwargs)
        finally:
            del self.fromlocation 
    
class TempLinker(object):
    """ performs a similar role to the Linker, but with a different approach
        instead of returning 'lazy' hrefs, this returns a simple URL-style
        string
        the 'temporary urls' are replaced on the filesystem after building the
        files, so that means even though a second pass is still required,
        things don't have to be built in-memory (as with the Linker)
    """
    fromlocation = None
    def __init__(self):
        self._linkid2target = {}
    def get_lazyhref(self, linkid):
        return '%s://%s' % (TEMPLINK_PROTO, linkid)
    def set_link(self, linkid, target):
        assert linkid not in self._linkid2target
        self._linkid2target[linkid] = target
    def get_target(self, tempurl, fromlocation=None):
        assert tempurl.startswith('%s://' % (TEMPLINK_PROTO,))
        linkid = '://'.join(tempurl.split('://')[1:])
        linktarget = self._linkid2target[linkid]
        if fromlocation is not None:
            linktarget = relpath(fromlocation, linktarget)
        return linktarget
    _reg_tempurl = py.std.re.compile('["\'](%s:\/\/[^"\s]*)["\']' % (
                                      TEMPLINK_PROTO,))
    def replace_dirpath(self, dirpath, stoponerrors=True):
        """ replace temporary links in all html files in dirpath and below """
        for fpath in dirpath.visit('*.html'):
            html = fpath.read()
            while 1:
                match = self._reg_tempurl.search(html)
                if not match:
                    break
                tempurl = match.group(1)
                try:
                    html = html.replace('"' + tempurl + '"',
                                        '"' + self.get_target(tempurl,
                                                fpath.relto(dirpath)) + '"')
                except KeyError:
                    if stoponerrors:
                        raise
                    html = html.replace('"' + tempurl + '"',
                                        '"apigen.notfound://%s"' % (tempurl,))
            fpath.write(html)
            
def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True):
    """ create a relative path from p1 to p2
        sep is the seperator used for input and (depending
        on the setting of 'normalize', see below) output
        back is the string used to indicate the parent directory
        when 'normalize' is True, any backslashes (\) in the path
        will be replaced with forward slashes, resulting in a consistent
        output on Windows and the rest of the world
        paths to directories must end on a / (URL style)
    """
    if normalize:
        p1 = p1.replace(sep, '/')
        p2 = p2.replace(sep, '/')
        sep = '/'
        # XXX would be cool to be able to do long filename expansion and drive
        # letter fixes here, and such... iow: windows sucks :(
    if (p1.startswith(sep) ^ p2.startswith(sep)): 
        raise ValueError("mixed absolute relative path: %r -> %r" %(p1, p2))
    fromlist = p1.split(sep)
    tolist = p2.split(sep)
    # AA
    # AA BB     -> AA/BB
    #
    # AA BB
    # AA CC     -> CC
    #
    # AA BB 
    # AA      -> ../AA
    diffindex = 0
    for x1, x2 in zip(fromlist, tolist):
        if x1 != x2:
            break
        diffindex += 1
    commonindex = diffindex - 1
    fromlist_diff = fromlist[diffindex:]
    tolist_diff = tolist[diffindex:]
    if not fromlist_diff:
        return sep.join(tolist[commonindex:])
    backcount = len(fromlist_diff)
    if tolist_diff:
        return sep.join([back,]*(backcount-1) + tolist_diff)
    return sep.join([back,]*(backcount) + tolist[commonindex:])