⇤ ← Revision 1 as of 2006-01-26 18:38:05
17243
Comment: Added my reference implementation
|
17259
python format
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
#format PYTHON |
1 """ path.py - An object representing a path to a file or directory.
2
3 Example:
4
5 from path import path
6 d = path('/home/guido/bin')
7 for f in d.files('*.py'):
8 f.chmod(0755)
9
10 This module requires Python 2.2 or later.
11
12
13 URL: http://www.jorendorff.com/articles/python/path
14 Author: Jason Orendorff <jason@jorendorff.com> (and others - see the url!)
15 Date: 7 Mar 2004
16
17 Adapted for stdlib by: Reinhold Birkenfeld, July 2005
18 Modified by Björn Lindqvist <bjourne@gmail.com>, January 2006
19 """
20
21 # TODO
22 # - Better error message in listdir() when self isn't a
23 # directory. (On Windows, the error message really sucks.)
24 # - Make sure everything has a good docstring.
25 # - Add methods for regex find and replace.
26 # - Perhaps support arguments to touch().
27 # - Could add split() and join() methods that generate warnings.
28 # - Note: __add__() technically has a bug, I think, where
29 # it doesn't play nice with other types that implement
30 # __radd__(). Test this.
31
32 import fnmatch
33 import glob
34 import os
35 import shutil
36 import sys
37
38 __all__ = ['path']
39 __version__ = '2.0.4'
40
41 # Universal newline support
42 _textmode = 'r'
43 if hasattr(file, 'newlines'):
44 _textmode = 'U'
45
46 # Use unicode strings if possible
47 _base = str
48 if os.path.supports_unicode_filenames:
49 _base = unicode
50
51
52 class path(_base):
53 """ Represents a filesystem path.
54
55 For documentation on individual methods, consult their
56 counterparts in os.path.
57 """
58
59 # --- Special Python methods.
60 def __new__(typ, *args):
61 """
62 Creates a new path object concatenating the *args. *args
63 may only contain Path objects or strings. If *args is
64 empty, Path(os.curdir) is created.
65 """
66 if not args:
67 return typ(os.curdir)
68 for arg in args:
69 if not isinstance(arg, basestring):
70 raise ValueError("%s() arguments must be Path, str or "
71 "unicode" % typ.__name__)
72 if len(args) == 1:
73 return _base.__new__(typ, *args)
74 return typ(os.path.join(*args))
75
76 def __repr__(self):
77 return '%s(%r)' % (self.__class__.__name__, _base(self))
78
79 # Adding a path and a string yields a path.
80 def __add__(self, more):
81 return self.__class__(_base(self) + more)
82
83 def __radd__(self, other):
84 return self.__class__(other + _base(self))
85
86 @classmethod
87 def cwd(cls):
88 """ Return the current working directory as a path object. """
89 return path(os.getcwd())
90
91 # --- Operations on path strings.
92
93 def abspath(self):
94 return self.__class__(os.path.abspath(self))
95
96 def normcase(self):
97 return self.__class__(os.path.normcase(self))
98
99 def normpath(self):
100 return self.__class__(os.path.normpath(self))
101
102 def realpath(self):
103 return self.__class__(os.path.realpath(self))
104
105 def expanduser(self):
106 return self.__class__(os.path.expanduser(self))
107
108 def expandvars(self):
109 return self.__class__(os.path.expandvars(self))
110
111 def expand(self):
112 """ Clean up a filename by calling expandvars(),
113 expanduser(), and normpath() on it.
114
115 This is commonly everything needed to clean up a filename
116 read from a configuration file, for example.
117 """
118 return self.expandvars().expanduser().normpath()
119
120 def _get_namebase(self):
121 base, ext = os.path.splitext(self.name)
122 return base
123
124 def _get_ext(self):
125 f, ext = os.path.splitext(_base(self))
126 return ext
127
128 def _get_drive(self):
129 drive, r = os.path.splitdrive(self)
130 return self.__class__(drive)
131
132 def _get_dirname(self):
133 return self.__class__(os.path.dirname(self))
134
135 parent = property(
136 _get_dirname, None, None,
137 """ This path's parent directory, as a new path object.
138
139 For example, path('/usr/local/lib/libpython.so').parent == path('/usr/local/lib')
140 """)
141
142 name = property(
143 os.path.basename, None, None,
144 """ The name of this file or directory without the full path.
145
146 For example, path('/usr/local/lib/libpython.so').name == 'libpython.so'
147 """)
148
149 namebase = property(
150 _get_namebase, None, None,
151 """ The same as path.name, but with one file extension stripped off.
152
153 For example, path('/home/guido/python.tar.gz').name == 'python.tar.gz',
154 but path('/home/guido/python.tar.gz').namebase == 'python.tar'
155 """)
156
157 ext = property(
158 _get_ext, None, None,
159 """ The file extension, for example '.py'. """)
160
161 drive = property(
162 _get_drive, None, None,
163 """ The drive specifier, for example 'C:'.
164 This is always empty on systems that don't use drive specifiers.
165 """)
166
167 def splitpath(self):
168 """ p.splitpath() -> Return (p.parent, p.name). """
169 parent, child = os.path.split(self)
170 return self.__class__(parent), child
171
172 def stripext(self):
173 """ p.stripext() -> Remove one file extension from the path.
174
175 For example, path('/home/guido/python.tar.gz').stripext()
176 returns path('/home/guido/python.tar').
177 """
178 return path(os.path.splitext(self)[0])
179
180 if hasattr(os.path, 'splitunc'):
181 def splitunc(self):
182 unc, rest = os.path.splitunc(self)
183 return self.__class__(unc), rest
184
185 def _get_uncshare(self):
186 unc, r = os.path.splitunc(self)
187 return self.__class__(unc)
188
189 uncshare = property(
190 _get_uncshare, None, None,
191 """ The UNC mount point for this path.
192 This is empty for paths on local drives. """)
193
194 def splitall(self):
195 """ Return a list of the path components in this path.
196
197 The first item in the list will be a path. Its value will be
198 either os.curdir, os.pardir, empty, or the root directory of
199 this path (for example, '/' or 'C:\\'). The other items in
200 the list will be strings.
201
202 path.path(*result) will yield the original path.
203 """
204 parts = []
205 loc = self
206 while loc != os.curdir and loc != os.pardir:
207 prev = loc
208 loc, child = prev.splitpath()
209 loc = self.__class__(loc)
210 if loc == prev:
211 break
212 parts.append(child)
213 parts.append(loc)
214 parts.reverse()
215 return parts
216
217 def relpath(self):
218 """ Return this path as a relative path,
219 based from the current working directory.
220 """
221 return self.__class__.cwd().relpathto(self)
222
223 def relpathto(self, dest):
224 """ Return a relative path from self to dest.
225
226 If there is no relative path from self to dest, for example if
227 they reside on different drives in Windows, then this returns
228 dest.abspath().
229 """
230 origin = self.abspath()
231 dest = self.__class__(dest).abspath()
232
233 orig_list = origin.normcase().splitall()
234 # Don't normcase dest! We want to preserve the case.
235 dest_list = dest.splitall()
236
237 if orig_list[0] != os.path.normcase(dest_list[0]):
238 # Can't get here from there.
239 return dest
240
241 # Find the location where the two paths start to differ.
242 i = 0
243 for start_seg, dest_seg in zip(orig_list, dest_list):
244 if start_seg != os.path.normcase(dest_seg):
245 break
246 i += 1
247
248 # Now i is the point where the two paths diverge.
249 # Need a certain number of "os.pardir"s to work up
250 # from the origin to the point of divergence.
251 segments = [os.pardir] * (len(orig_list) - i)
252 # Need to add the diverging part of dest_list.
253 segments += dest_list[i:]
254 if len(segments) == 0:
255 # If they happen to be identical, use os.curdir.
256 return self.__class__(os.curdir)
257 else:
258 return self.__class__(os.path.join(*segments))
259
260
261 # --- Listing, searching, walking, and matching
262
263 def listdir(self, pattern=None):
264 """ D.listdir() -> List of items in this directory.
265
266 Use D.files() or D.dirs() instead if you want a listing
267 of just files or just subdirectories.
268
269 The elements of the list are path objects.
270
271 With the optional 'pattern' argument, this only lists
272 items whose names match the given pattern.
273 """
274 names = os.listdir(self)
275 if pattern is not None:
276 names = fnmatch.filter(names, pattern)
277 return [path(self, child) for child in names]
278
279 def dirs(self, pattern=None):
280 """ D.dirs() -> List of this directory's subdirectories.
281
282 The elements of the list are path objects.
283 This does not walk recursively into subdirectories
284 (but see path.walkdirs).
285
286 With the optional 'pattern' argument, this only lists
287 directories whose names match the given pattern. For
288 example, d.dirs('build-*').
289 """
290 return [p for p in self.listdir(pattern) if p.isdir()]
291
292 def files(self, pattern=None):
293 """ D.files() -> List of the files in this directory.
294
295 The elements of the list are path objects.
296 This does not walk into subdirectories (see path.walkfiles).
297
298 With the optional 'pattern' argument, this only lists files
299 whose names match the given pattern. For example,
300 d.files('*.pyc').
301 """
302
303 return [p for p in self.listdir(pattern) if p.isfile()]
304
305 def walk(self, pattern=None):
306 """ D.walk() -> iterator over files and subdirs, recursively.
307
308 The iterator yields path objects naming each child item of
309 this directory and its descendants. This requires that
310 D.isdir().
311
312 This performs a depth-first traversal of the directory tree.
313 Each directory is returned just before all its children.
314 """
315 for child in self.listdir():
316 if pattern is None or child.match(pattern):
317 yield child
318 if child.isdir():
319 for item in child.walk(pattern):
320 yield item
321
322 def walkdirs(self, pattern=None):
323 """ D.walkdirs() -> iterator over subdirs, recursively.
324
325 With the optional 'pattern' argument, this yields only
326 directories whose names match the given pattern. For
327 example, mydir.walkdirs('*test') yields only directories
328 with names ending in 'test'.
329 """
330 for child in self.dirs():
331 if pattern is None or child.match(pattern):
332 yield child
333 for subsubdir in child.walkdirs(pattern):
334 yield subsubdir
335
336 def walkfiles(self, pattern=None):
337 """ D.walkfiles() -> iterator over files in D, recursively.
338
339 The optional argument, pattern, limits the results to files
340 with names that match the pattern. For example,
341 mydir.walkfiles('*.tmp') yields only files with the .tmp
342 extension.
343 """
344 for child in self.listdir():
345 if child.isfile():
346 if pattern is None or child.match(pattern):
347 yield child
348 elif child.isdir():
349 for f in child.walkfiles(pattern):
350 yield f
351
352 def match(self, pattern):
353 """ Return True if self.name matches the given pattern.
354
355 pattern - A filename pattern with wildcards,
356 for example '*.py'.
357 """
358 return fnmatch.fnmatch(self.name, pattern)
359
360 def matchcase(self, pattern):
361 """ Test whether the path matches pattern, returning true or
362 false; the comparison is always case-sensitive.
363 """
364 return fnmatch.fnmatchcase(self.name, pattern)
365
366 def glob(self, pattern):
367 """ Return a list of path objects that match the pattern.
368
369 pattern - a path relative to this directory, with wildcards.
370
371 For example, path('/users').glob('*/bin/*') returns a list
372 of all the files users have in their bin directories.
373 """
374 return map(path, glob.glob(_base(path(self, pattern))))
375
376 # --- Methods for querying the filesystem.
377
378 exists = os.path.exists
379 isabs = os.path.isabs
380 isdir = os.path.isdir
381 isfile = os.path.isfile
382 islink = os.path.islink
383 ismount = os.path.ismount
384
385 if hasattr(os.path, 'samefile'):
386 samefile = os.path.samefile
387
388 getatime = os.path.getatime
389 atime = property(
390 getatime, None, None,
391 """ Last access time of the file. """)
392
393 getmtime = os.path.getmtime
394 mtime = property(
395 getmtime, None, None,
396 """ Last-modified time of the file. """)
397
398 getctime = os.path.getctime
399 ctime = property(
400 getctime, None, None,
401 """ Return the system's ctime which, on some systems (like
402 Unix) is the time of the last change, and, on others (like
403 Windows), is the creation time for path. The return value is a
404 number giving the number of seconds since the epoch (see the
405 time module). Raise os.error if the file does not exist or is
406 inaccessible.""")
407
408 getsize = os.path.getsize
409 size = property(
410 getsize, None, None,
411 """ Size of the file, in bytes. """)
412
413 if hasattr(os, 'access'):
414 def access(self, mode):
415 """ Return true if current user has access to this path.
416
417 mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
418 """
419 return os.access(self, mode)
420
421 def stat(self):
422 """ Perform a stat() system call on this path. """
423 return os.stat(self)
424
425 def lstat(self):
426 """ Like path.stat(), but do not follow symbolic links. """
427 return os.lstat(self)
428
429 if hasattr(os, 'statvfs'):
430 def statvfs(self):
431 """ Perform a statvfs() system call on this path. """
432 return os.statvfs(self)
433
434 if hasattr(os, 'pathconf'):
435 def pathconf(self, name):
436 return os.pathconf(self, name)
437
438
439 # --- Modifying operations on files and directories
440
441 def utime(self, times):
442 """ Set the access and modified times of this file. """
443 os.utime(self, times)
444
445 def chmod(self, mode):
446 os.chmod(self, mode)
447
448 if hasattr(os, 'chown'):
449 def chown(self, uid, gid):
450 os.chown(self, uid, gid)
451
452 def rename(self, new):
453 os.rename(self, new)
454
455 def renames(self, new):
456 os.renames(self, new)
457
458
459 # --- Create/delete operations on directories
460
461 def mkdir(self, mode=0777):
462 os.mkdir(self, mode)
463
464 def makedirs(self, mode=0777):
465 os.makedirs(self, mode)
466
467 def rmdir(self):
468 os.rmdir(self)
469
470 def removedirs(self):
471 os.removedirs(self)
472
473
474 # --- Modifying operations on files
475
476 def touch(self):
477 """ Set the access/modified times of this file to the current time.
478 Create the file if it does not exist.
479 """
480 fd = os.open(self, os.O_WRONLY | os.O_CREAT, 0666)
481 os.close(fd)
482 os.utime(self, None)
483
484 def remove(self):
485 os.remove(self)
486
487 def unlink(self):
488 os.unlink(self)
489
490
491 # --- Links
492
493 if hasattr(os, 'link'):
494 def link(self, newpath):
495 """ Create a hard link at 'newpath', pointing to this file. """
496 os.link(self, newpath)
497
498 if hasattr(os, 'symlink'):
499 def symlink(self, newlink):
500 """ Create a symbolic link at 'newlink', pointing here. """
501 os.symlink(self, newlink)
502
503 if hasattr(os, 'readlink'):
504 def readlink(self):
505 """ Return the path to which this symbolic link points.
506
507 The result may be an absolute or a relative path.
508 """
509 return self.__class__(os.readlink(self))
510
511 def readlinkabs(self):
512 """ Return the path to which this symbolic link points.
513
514 The result is always an absolute path.
515 """
516 p = self.readlink()
517 if p.isabs():
518 return p
519 else:
520 return (self.parent / p).abspath()
521
522
523 # --- High-level functions from shutil
524
525 copyfile = shutil.copyfile
526 copymode = shutil.copymode
527 copystat = shutil.copystat
528 copy = shutil.copy
529 copy2 = shutil.copy2
530 copytree = shutil.copytree
531 if hasattr(shutil, 'move'):
532 move = shutil.move
533 rmtree = shutil.rmtree
534
535
536 # --- Special stuff from os
537
538 if hasattr(os, 'chroot'):
539 def chroot(self):
540 os.chroot(self)
541
542 if hasattr(os, 'startfile'):
543 def startfile(self):
544 os.startfile(self)