/[theodore]/bunnyblog/modules/urlpath.py


UCC Code Repository

Contents of /bunnyblog/modules/urlpath.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1 - (show annotations) (download) (as text)
Tue Jan 29 14:32:01 2008 UTC (12 years, 6 months ago) by svn-admin
File MIME type: text/x-python
File size: 7847 byte(s)
Re-import of repository after repository database corruption.

1 # urlpath.py
2
3 # 0.1.0
4 # 2005/08/20
5
6 # Functions that handle url paths.
7 # Part of Pythonutils
8 # http://www.voidspace.org.uk/python/pythonutils.html
9
10 # Copyright Michael Foord, 2004 & 2005.
11 # Released subject to the BSD License
12 # Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
13
14 # For information about bugfixes, updates and support, please join the
15 # Pythonutils mailing list.
16 # http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
17 # Comments, suggestions and bug reports welcome.
18 # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
19 # E-mail [email protected]
20
21 import posixpath
22 import os
23 from urllib import url2pathname, pathname2url
24
25 __all__ = [
26 'nativejoin',
27 'pathjoin',
28 'relpathto',
29 'tslash',
30 'relpath'
31 ]
32
33 def pathjoin(base, *paths):
34 """
35 Join paths to a base, observing pardir.
36
37 If base doesn't *end* with '/' we assume it's a file rather than a directory.
38 (so we get rid of it)
39 """
40 # XXXX will posixpath.join do all this anyway?
41 if base and not base.endswith('/'):
42 # get rid of the filename
43 base = '/'.join(base.split('/')[:-1])
44 base = tslash(base)
45 path = (base,) + paths
46 return posixpath.normpath(posixpath.join(*path))
47
48 def nativejoin(base, path):
49 """
50 Joins two paths - returning a native file path.
51
52 Given a base path and a relative location, (in posix format)
53 return a file path in a (relatively) OS native way.
54 """
55 return url2pathname(pathjoin(base, path))
56
57 def relpathto(thisdir, origin, dest):
58 """
59 Given two paths relative to a directory, work out a path from origin
60 to destination.
61
62 Assumes UNIX/URL type relative paths.
63 If origin doesn't *end* with '/' we assume it's a file rather than a
64 directory.
65
66 If the same paths are passed in :
67 if the path ends with ('/') then we return ''
68 else we return the last part of the path (presumably a filename)
69
70 If thisdir doesn't start with '/' then we add one
71 (this makes the top level of thisdir our root directory)
72 """
73 orig_thisdir = thisdir
74 if not thisdir.startswith('/'):
75 thisdir = '/' + thisdir
76 orig_abs = posixpath.normpath(posixpath.join(thisdir, origin))
77 dest_abs = posixpath.normpath(posixpath.join(thisdir, dest))
78 if origin.endswith('/') and not orig_abs.endswith('/'):
79 orig_abs = orig_abs + '/'
80 if dest.endswith('/') and not dest_abs.endswith('/'):
81 dest_abs = dest_abs + '/'
82 # print orig_abs, dest_abs
83 #
84 # if the first item is a filename, we want to get rid of it
85 orig_list = orig_abs.split('/')[:-1]
86 dest_list = dest_abs.split('/')
87 # print orig_list, dest_list
88
89 if orig_list[0] != dest_list[0]:
90 # can't get here from there
91 # XXXX raise exception?
92 return dest
93 #
94 # find the location where the two paths start to differ.
95 i = 0
96 for start_seg, dest_seg in zip(orig_list, dest_list):
97 if start_seg != dest_seg:
98 break
99 i += 1
100 #
101 # now i is the point where the two paths diverge;
102 # need a certain number of "os.pardir"s to work up
103 # from the origin to the point of divergence.
104 segments = ['..'] * (len(orig_list) - i)
105 # need to add the diverging part of dest_list.
106 segments += dest_list[i:]
107 if len(segments) == 0:
108 # if they happen to be identical paths
109 # identical directories
110 if dest.endswith('/'):
111 return ''
112 # just the filename - the last part of dest
113 return dest_list[-1]
114 else:
115 return '/'.join(segments)
116
117 def relpath(origin, dest):
118 """Given two absolute paths, work out a path from origin to destination.
119
120 Assumes UNIX/URL type relative paths.
121 If origin doesn't *end* with '/' we assume it's a file rather than
122 a directory.
123
124 If the same paths are passed in :
125 if the path ends with ('/') then we return ''
126 else we return the last part of the path (presumably a filename)
127
128 If origin or dest don't start with '/' then we add it.
129
130 We are *assuming* relative paths on the same device
131 (i.e. same top level directory)
132 """
133 if not origin.startswith('/'):
134 origin = '/' + origin
135 if not dest.startswith('/'):
136 dest = '/' + dest
137 #
138 # if the first item is a filename, we want to get rid of it
139 orig_list = origin.split('/')[:-1]
140 dest_list = dest.split('/')
141 #
142 # find the location where the two paths start to differ.
143 i = 0
144 for start_seg, dest_seg in zip(orig_list, dest_list):
145 if start_seg != dest_seg:
146 break
147 i += 1
148
149 # now i is the point where the two paths diverge.
150 # need a certain number of "os.pardir"s to work up
151 # from the origin to the point of divergence.
152 segments = ['..'] * (len(orig_list) - i)
153 # need to add the diverging part of dest_list.
154 segments += dest_list[i:]
155 if len(segments) == 0:
156 # if they happen to be identical paths
157 # identical directories
158 if dest.endswith('/'):
159 return ''
160 # just the filename - the last part of dest
161 return dest_list[-1]
162 else:
163 return '/'.join(segments)
164
165 def tslash(apath):
166 """Add a trailing slash to a path if it needs one.
167
168 Doesn't use os.sep because you end up jiggered on windoze - when you
169 want separators for URLs.
170 """
171 if (apath and
172 apath != '.' and
173 not apath.endswith('/') and
174 not apath.endswith('\\')):
175 return apath + '/'
176 else:
177 return apath
178
179 ##############################################
180
181 def testJoin():
182 thelist = [
183 ('/', 'fish.html'),
184 ('/dir/dir/', '../file'),
185 ('dir/dir/', '../file'),
186 ('dir/dir/', '../../file'),
187 ('dir/dir/', '../../../file'),
188 ('/dir/dir/', '../notherdir/file'),
189 ('/dir/dir/', '../../notherdir/file'),
190 ('dir/dir/', '../../notherdir/file'),
191 ('dir/dir/', '../../../notherdir/file'),
192 ('', '../path'),
193 ]
194 for entry in thelist:
195 print entry, ' :: ', pathjoin(*entry)
196 print entry, ' :: ', nativejoin(*entry)
197 print '\n'
198
199 def testRelpathto():
200 thedir = '//toplevel/dirone/dirtwo/dirthree'
201 thelist = [
202 ('file1.html', 'file2.html'),
203 ('file1.html', '../file2.html'),
204 ('../file1.html', '../file2.html'),
205 ('../file1.html', 'file2.html'),
206 ('../fish1/fish2/', '../../sub1/sub2/'),
207 ('../fish1/fish2/', 'sub1/sub2'),
208 ('../../../fish1/fish2/', 'sub1/sub2/'),
209 ('../../../fish1/fish2/', 'sub1/sub2/file1.html'),
210 ]
211 for orig, dest in thelist:
212 print '(%s, %s) : ' % (orig, dest), relpathto(thedir, orig, dest)
213
214 def testRelpathto2():
215 thedir = 'section3/'
216 thelist = [
217 ('../archive/strangeindex1.html', 'article2.html'),
218 ]
219 for orig, dest in thelist:
220 answer = relpathto(thedir, orig, dest)
221 print '(%s, %s) : ' % (orig, dest), answer
222
223 def testRelpath():
224 thelist = [
225 ('/hello/fish/', 'bungles'),
226 ]
227 for orig, dest in thelist:
228 answer = relpath(orig, dest)
229 print '(%s, %s) : ' % (orig, dest), answer
230
231
232 if __name__ == '__main__':
233 testJoin()
234 testRelpathto()
235 testRelpath()
236 # testRelpathto2()
237
238 """
239 TODO
240 ====
241
242 More comprehensive tests.
243
244 CHANGELOG
245 2005/07/31
246 Can now pass mulitple args to ``pathjoin``.
247 Finalised as version 0.1.0
248
249 2005/06/18
250 Changes by Nicola Larosa
251 Code cleanup
252 lines shortened
253 comments on line above code
254 empty comments in empty lines
255
256 2005/05/28
257 Added relpath to __all__
258
259
260 TODO
261 Move into pythonutils
262 relpathto could call relpath (and so be shorter)
263 nativejoin could accept multiple paths
264 Could tslash be more elegant ?
265 """
266

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26