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


UCC Code Repository

Contents of /bunnyblog/modules/pathutils.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, 2 months ago) by svn-admin
File MIME type: text/x-python
File size: 12028 byte(s)
Re-import of repository after repository database corruption.

1 # 2005/08/28
2 # Version 0.2.1
3 # pathutils.py
4 # Functions useful for working with files and paths.
5 # http://www.voidspace.org.uk/python/recipebook.shtml#utils
6
7 # Copyright Michael Foord 2004
8 # Released subject to the BSD License
9 # Please see http://www.voidspace.org.uk/documents/BSD-LICENSE.txt
10
11 # For information about bugfixes, updates and support, please join the Pythonutils mailing list.
12 # http://voidspace.org.uk/mailman/listinfo/pythonutils_voidspace.org.uk
13 # Comments, suggestions and bug reports welcome.
14 # Scripts maintained at http://www.voidspace.org.uk/python/index.shtml
15 # E-mail fuzzyman@voidspace.org.uk
16
17 """
18 This module contains convenience functions for working with files and paths.
19 """
20
21 from __future__ import generators
22 import os
23 import sys
24
25 __all__ = (
26 'readlines',
27 'writelines',
28 'readbinary',
29 'writebinary',
30 'readfile',
31 'writefile',
32 'tslash',
33 'relpath',
34 'splitall',
35 'walkfiles',
36 'walkdirs',
37 'walkemptydirs',
38 'formatbytes',
39 'fullcopy',
40 'import_path',
41 )
42
43 ######################################
44 # Functions to read and write files in text and binary mode.
45
46 def readlines(filename):
47 """Passed a filename, it reads it, and returns a list of lines. (Read in text mode)"""
48 filehandle = open(filename, 'r')
49 outfile = filehandle.readlines()
50 filehandle.close()
51 return outfile
52
53 def writelines(filename, infile, newline=False):
54 """
55 Given a filename and a list of lines it writes the file. (In text mode)
56
57 If ``newline`` is ``True`` (default is ``False``) it adds a newline to each
58 line.
59 """
60 filehandle = open(filename, 'w')
61 if newline:
62 infile = [line + '\n' for line in infile]
63 filehandle.writelines(infile)
64 filehandle.close()
65
66 def readbinary(filename):
67 """Given a filename, read a file in binary mode. It returns a single string."""
68 filehandle = open(filename, 'rb')
69 thisfile = filehandle.read()
70 filehandle.close()
71 return thisfile
72
73 def writebinary(filename, infile):
74 """Given a filename and a string, write the file in binary mode. """
75 filehandle = open(filename, 'wb')
76 filehandle.write(infile)
77 filehandle.close()
78
79 def readfile(filename):
80 """Given a filename, read a file in text mode. It returns a single string."""
81 filehandle = open(filename, 'r')
82 outfile = filehandle.read()
83 filehandle.close()
84 return outfile
85
86 def writefile(filename, infile):
87 """Given a filename and a string, write the file in text mode."""
88 filehandle = open(filename, 'w')
89 filehandle.write(infile)
90 filehandle.close()
91
92 ####################################################################
93 # Some functions for dealing with paths
94
95 def tslash(apath):
96 """
97 Add a trailing slash (``/``) to a path if it lacks one.
98
99 It doesn't use ``os.sep`` because you end up in trouble on windoze, when you
100 want separators for URLs.
101 """
102 if apath and apath != '.' and not apath.endswith('/') and not apath.endswith('\\'):
103 return apath + '/'
104 else:
105 return apath
106
107 def relpath(origin, dest):
108 """
109 Return the relative path between origin and dest.
110
111 If it's not possible return dest.
112
113
114 If they are identical return ``os.curdir``
115
116 Adapted from `path.py`_ by Jason Orendorff.
117
118
119 .. _path.py: http://www.jorendorff.com/articles/python/path/
120 """
121 origin = os.path.abspath(origin).replace('\\', '/')
122 dest = os.path.abspath(dest).replace('\\', '/')
123
124 orig_list = splitall(os.path.normcase(origin))
125 # Don't normcase dest! We want to preserve the case.
126 dest_list = splitall(dest)
127
128 if orig_list[0] != os.path.normcase(dest_list[0]):
129 # Can't get here from there.
130 return dest
131
132 # Find the location where the two paths start to differ.
133 i = 0
134 for start_seg, dest_seg in zip(orig_list, dest_list):
135 if start_seg != os.path.normcase(dest_seg):
136 break
137 i += 1
138
139 # Now i is the point where the two paths diverge.
140 # Need a certain number of "os.pardir"s to work up
141 # from the origin to the point of divergence.
142 segments = [os.pardir] * (len(orig_list) - i)
143 # Need to add the diverging part of dest_list.
144 segments += dest_list[i:]
145 if len(segments) == 0:
146 # If they happen to be identical, use os.curdir.
147 return os.curdir
148 else:
149 return os.path.join(*segments).replace('\\', '/')
150
151 def splitall(loc):
152 """
153 Return a list of the path components in loc. (Used by relpath_).
154
155 The first item in the list will be either ``os.curdir``, ``os.pardir``, empty,
156 or the root directory of loc (for example, ``/`` or ``C:\\).
157
158 The other items in the list will be strings.
159
160 Adapted from path.py by Jason Orendorff.
161 """
162 parts = []
163 while loc != os.curdir and loc != os.pardir:
164 prev = loc
165 loc, child = os.path.split(prev)
166 if loc == prev:
167 break
168 parts.append(child)
169 parts.append(loc)
170 parts.reverse()
171 return parts
172
173 #######################################################################
174 # a pre 2.3 walkfiles function - adapted from the path module by Jason Orendorff
175
176 join = os.path.join
177 isdir = os.path.isdir
178 isfile = os.path.isfile
179
180 def walkfiles(thisdir):
181 """
182 walkfiles(D) -> iterator over files in D, recursively. Yields full file paths.
183
184 Adapted from path.py by Jason Orendorff.
185 """
186 for child in os.listdir(thisdir):
187 thischild = join(thisdir, child)
188 if isfile(thischild):
189 yield thischild
190 elif isdir(thischild):
191 for f in walkfiles(thischild):
192 yield f
193
194 def walkdirs(thisdir):
195 """
196 Walk through all the subdirectories in a tree. Recursively yields directory
197 names (full paths).
198 """
199 for child in os.listdir(thisdir):
200 thischild = join(thisdir, child)
201 if isfile(thischild):
202 continue
203 elif isdir(thischild):
204 for f in walkdirs(thischild):
205 yield f
206 yield thischild
207
208 def walkemptydirs(thisdir):
209 """
210 Recursively yield names of *empty* directories.
211
212 These are the only paths omitted when using ``walkfiles``.
213 """
214 if not os.listdir(thisdir): # if the directory is empty.. then yield it
215 yield thisdir
216 for child in os.listdir(thisdir):
217 thischild = join(thisdir, child)
218 if isdir(thischild):
219 for emptydir in walkemptydirs(thischild):
220 yield emptydir
221
222 ###############################################################
223 # formatbytes takes a filesize (as returned by os.getsize() )
224 # and formats it for display in one of two ways !!
225
226 def formatbytes(sizeint, configdict=None, **configs):
227 """
228 Given a file size as an integer, return a nicely formatted string that
229 represents the size. Has various options to control it's output.
230
231 You can pass in a dictionary of arguments or keyword arguments. Keyword
232 arguments override the dictionary and there are sensible defaults for options
233 you don't set.
234
235 Options and defaults are as follows :
236
237 * ``forcekb = False`` - If set this forces the output to be in terms
238 of kilobytes and bytes only.
239
240 * ``largestonly = True`` - If set, instead of outputting
241 ``1 Mbytes, 307 Kbytes, 478 bytes`` it outputs using only the largest
242 denominator - e.g. ``1.3 Mbytes`` or ``17.2 Kbytes``
243
244 * ``kiloname = 'Kbytes'`` - The string to use for kilobytes
245
246 * ``meganame = 'Mbytes'`` - The string to use for Megabytes
247
248 * ``bytename = 'bytes'`` - The string to use for bytes
249
250 * ``nospace = True`` - If set it outputs ``1Mbytes, 307Kbytes``,
251 notice there is no space.
252
253 Example outputs : ::
254
255 19Mbytes, 75Kbytes, 255bytes
256 2Kbytes, 0bytes
257 23.8Mbytes
258
259 .. note::
260
261 It currently uses the plural form even for singular.
262 """
263 defaultconfigs = { 'forcekb' : False,
264 'largestonly' : True,
265 'kiloname' : 'Kbytes',
266 'meganame' : 'Mbytes',
267 'bytename' : 'bytes',
268 'nospace' : True}
269 if configdict is None:
270 configdict = {}
271 for entry in configs:
272 # keyword parameters override the dictionary passed in
273 configdict[entry] = configs[entry]
274 #
275 for keyword in defaultconfigs:
276 if not configdict.has_key(keyword):
277 configdict[keyword] = defaultconfigs[keyword]
278 #
279 if configdict['nospace']:
280 space = ''
281 else:
282 space = ' '
283 #
284 mb, kb, rb = bytedivider(sizeint)
285 if configdict['largestonly']:
286 if mb and not configdict['forcekb']:
287 return stringround(mb, kb)+ space + configdict['meganame']
288 elif kb or configdict['forcekb']:
289 if mb and configdict['forcekb']:
290 kb += 1024*mb
291 return stringround(kb, rb) + space+ configdict['kiloname']
292 else:
293 return str(rb) + space + configdict['bytename']
294 else:
295 outstr = ''
296 if mb and not configdict['forcekb']:
297 outstr = str(mb) + space + configdict['meganame'] +', '
298 if kb or configdict['forcekb'] or mb:
299 if configdict['forcekb']:
300 kb += 1024*mb
301 outstr += str(kb) + space + configdict['kiloname'] +', '
302 return outstr + str(rb) + space + configdict['bytename']
303
304 def stringround(main, rest):
305 """
306 Given a file size in either (mb, kb) or (kb, bytes) - round it
307 appropriately.
308 """
309 # divide an int by a float... get a float
310 value = main + rest/1024.0
311 return str(round(value, 1))
312
313 def bytedivider(nbytes):
314 """
315 Given an integer (probably a long integer returned by os.getsize() )
316 it returns a tuple of (megabytes, kilobytes, bytes).
317
318 This can be more easily converted into a formatted string to display the
319 size of the file.
320 """
321 mb, remainder = divmod(nbytes, 1048576)
322 kb, rb = divmod(remainder, 1024)
323 return (mb, kb, rb)
324
325 ########################################
326
327 def fullcopy(src, dst):
328 """
329 Copy file from src to dst.
330
331 If the dst directory doesn't exist, we will attempt to create it using makedirs.
332 """
333 import shutil
334 if not os.path.isdir(os.path.dirname(dst)):
335 os.makedirs(os.path.dirname(dst))
336 shutil.copy(src, dst)
337
338 #######################################
339
340 def import_path(fullpath, strict=True):
341 """
342 Import a file from the full path. Allows you to import from anywhere,
343 something ``__import__`` does not do.
344
345 If strict is ``True`` (the default), raise an ``ImportError`` if the module
346 is found in the "wrong" directory.
347
348 Taken from firedrop2_ by `Hans Nowak`_
349
350 .. _firedrop2: http://www.voidspace.org.uk/python/firedrop2/
351 .. _Hans Nowak: http://zephyrfalcon.org
352 """
353 path, filename = os.path.split(fullpath)
354 filename, ext = os.path.splitext(filename)
355 sys.path.insert(0, path)
356 try:
357 module = __import__(filename)
358 except ImportError:
359 del sys.path[0]
360 raise
361 del sys.path[0]
362 #
363 if strict:
364 path = os.path.split(module.__file__)[0]
365 # FIXME: doesn't *startswith* allow room for errors ?
366 if not fullpath.startswith(path):
367 raise ImportError, "Module '%s' found, but not in '%s'" % (
368 filename, fullpath)
369 #
370 return module
371
372 """
373
374 Changelog
375 ==========
376 2005/08/28 Version 0.2.1
377 Added import_path
378 Added __all__
379 Code cleanup
380
381 2005/06/01 Version 0.2.0
382 Added walkdirs generator.
383
384
385 2005/03/11 Version 0.1.1
386 Added rounding to formatbytes and improved bytedivider with divmod.
387 Now explicit keyword parameters override the configdict in formatbytes.
388
389 2005/02/18 Version 0.1.0
390 The first numbered version.
391 """

Managed by UCC Webmasters ViewVC Help
Powered by ViewVC 1.1.26