CARVIEW |
Select Language
HTTP/2 302
server: nginx
date: Wed, 16 Jul 2025 22:40:28 GMT
content-type: text/plain; charset=utf-8
content-length: 0
x-archive-redirect-reason: found capture at 20091021043022
location: https://web.archive.org/web/20091021043022/https://wiki.python.org/moin/AlternativePathModule
server-timing: captures_list;dur=1.116658, exclusion.robots;dur=0.024464, exclusion.robots.policy;dur=0.011396, esindex;dur=0.014031, cdx.remote;dur=344.526744, LoadShardBlock;dur=1196.840093, PetaboxLoader3.resolve;dur=485.202270, PetaboxLoader3.datanode;dur=162.705135
x-app-server: wwwb-app214
x-ts: 302
x-tr: 1572
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=0
set-cookie: SERVER=wwwb-app214; path=/
x-location: All
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
HTTP/2 200
server: nginx
date: Wed, 16 Jul 2025 22:40:29 GMT
content-type: text/html; charset=utf-8
x-archive-orig-date: Wed, 21 Oct 2009 04:30:21 GMT
x-archive-orig-server: Apache/2.2.9 (Debian) mod_fastcgi/2.4.6 mod_python/3.3.1 Python/2.5.2 mod_wsgi/2.5
x-archive-orig-vary: Cookie,User-Agent,Accept-Language
x-archive-orig-content-length: 240012
x-archive-orig-connection: close
x-archive-guessed-content-type: text/html
x-archive-guessed-charset: utf-8
memento-datetime: Wed, 21 Oct 2009 04:30:22 GMT
link: ; rel="original", ; rel="timemap"; type="application/link-format", ; rel="timegate", ; rel="first memento"; datetime="Tue, 12 Sep 2006 20:17:23 GMT", ; rel="prev memento"; datetime="Sat, 08 Dec 2007 09:30:36 GMT", ; rel="memento"; datetime="Wed, 21 Oct 2009 04:30:22 GMT", ; rel="next memento"; datetime="Thu, 11 Nov 2010 01:51:11 GMT", ; rel="last memento"; datetime="Tue, 25 Jun 2024 22:58:44 GMT"
content-security-policy: default-src 'self' 'unsafe-eval' 'unsafe-inline' data: blob: archive.org web.archive.org web-static.archive.org wayback-api.archive.org athena.archive.org analytics.archive.org pragma.archivelab.org wwwb-events.archive.org
x-archive-src: 52_12_20091020224820_crawl103_IndexOnly-c/52_12_20091021042909_crawl103.arc.gz
server-timing: captures_list;dur=0.559691, exclusion.robots;dur=0.018351, exclusion.robots.policy;dur=0.008380, esindex;dur=0.014717, cdx.remote;dur=185.837451, LoadShardBlock;dur=202.715170, PetaboxLoader3.datanode;dur=271.838933, load_resource;dur=460.213693, PetaboxLoader3.resolve;dur=130.319502
x-app-server: wwwb-app214
x-ts: 200
x-tr: 1108
server-timing: TR;dur=0,Tw;dur=0,Tc;dur=0
x-location: All
x-rl: 0
x-na: 0
x-page-cache: MISS
server-timing: MISS
x-nid: DigitalOcean
referrer-policy: no-referrer-when-downgrade
permissions-policy: interest-cohort=()
content-encoding: gzip
AlternativePathModule - PythonInfo Wiki
1 """ path.py - An object representing a path to a file or a directory.
2
3 Based on the path module by Jason Orendorff
4 (https://www.jorendorff.com/articles/python/path)
5
6 Written by Noam Raphael to show the idea of using a tuple instead of
7 a string, and to reduce the number of methods.
8
9 Currently only implements posix and nt paths - more can be added.
10
11 """
12
13 import os
14 import stat
15 import itertools
16 import fnmatch
17 import re
18 import string
19
20 class StatWrapper(object):
21 """ A wrapper around stat_result objects which gives additional properties.
22
23 This object is a wrapper around a stat_result object. It allows access
24 to all the original object's attributes, and adds a few convinient
25 properties, by using the stat module.
26
27 This object should have been a subclass posix.stat_result - it simply
28 isn't possible currently. This functionality may also be integrated into
29 the original type.
30 """
31
32 __slots__ = ['_stat']
33
34 def __init__(self, stat):
35 self._stat = stat
36
37 def __getattribute__(self, attr, *default):
38 try:
39 return object.__getattribute__(self, attr, *default)
40 except AttributeError:
41 return getattr(self._stat, attr, *default)
42
43 # Mode properties
44
45 @property
46 def isdir(self):
47 return stat.S_ISDIR(self.st_mode)
48 @property
49 def isfile(self):
50 return stat.S_ISREG(self.st_mode)
51 @property
52 def islink(self):
53 return stat.S_ISLNK(self.st_mode)
54
55 # Easier names properties
56
57 @property
58 def size(self):
59 return self.st_size
60 @property
61 def mtime(self):
62 return self.st_mtime
63 @property
64 def atime(self):
65 return self.st_atime
66 @property
67 def ctime(self):
68 return self.st_ctime
69
70
71 class BasePath(tuple):
72 """ The base, abstract, path type.
73
74 The OS-specific path types inherit from it.
75 """
76
77 # ----------------------------------------------------------------
78 # We start with methods which don't use system calls - they just
79 # manipulate paths.
80
81 class _BaseRoot(object):
82 """ Represents a start location for a path.
83
84 A Root is an object which may be the first element of a path tuple,
85 and represents from where to start the path.
86
87 On posix, there's only one: ROOT (singleton).
88 On nt, there are a few:
89 CURROOT - the root of the current drive (singleton)
90 Drive(letter) - the root of a specific drive
91 UnrootedDrive(letter) - the current working directory on a specific
92 drive
93 UNCRoot(host, mountpoint) - a UNC mount point
94
95 The class for each OS has its own root classes, which should inherit
96 from _OSBaseRoot.
97
98 str(root) should return the string name of the root. The string should
99 identify the root: two root elements with the same string should have
100 the same meaning. To allow meaningful sorting of path objects, root
101 objects can be compared to strings and other root objects. They are
102 smaller than all strings, and are compared with other root objects
103 according to their string name.
104
105 Every Root object should contain the "isabs" attribute, which is True
106 if changes in the current working directory won't change the meaning
107 of the root and False otherwise. (CURROOT and UnrootedDrive aren't
108 absolute)
109 If isabs is True, it should also implement the abspath() method, which
110 should return an absolute path object, equivalent to the root when the
111 call was made.
112 """
113 isabs = None
114
115 def abspath(self):
116 if self.abspath:
117 raise NotImplementedError, 'This root is already absolute'
118 else:
119 raise NotImplementedError, 'abspath is abstract'
120
121 def __str__(self):
122 raise NotImplementedError, '__str__ is abstract'
123
124 def __cmp__(self, other):
125 if isinstance(other, str):
126 return -1
127 elif isinstance(other, BasePath._BaseRoot):
128 return cmp(str(self), str(other))
129 else:
130 raise TypeError, 'Comparison not defined'
131
132 def __hash__(self):
133 # This allows path objects to be hashable
134 return hash(str(self))
135
136 # _OSBaseRoot should be the base of the OS-specific root classes, which
137 # should inherit from _BaseRoot
138 _OSBaseRoot = None
139
140 # These string constants should be filled by subclasses - they are real
141 # directory names
142 curdir = None
143 pardir = None
144
145 # These string constants are used by default implementations of methods,
146 # but are not part of the interface - the whole idea is for the interface
147 # to hide those details.
148 _sep = None
149 _altsep = None
150
151 @staticmethod
152 def _parse_str(pathstr):
153 # Concrete path classes should implement _parse_str to get a path
154 # string and return an iterable over path elements.
155 raise NotImplementedError, '_parse_str is abstract'
156
157 @staticmethod
158 def normcasestr(string):
159 """ Normalize the case of one path element.
160
161 This default implementation returns string unchanged. On
162 case-insensitive platforms, it returns the normalized string.
163 """
164 return string
165
166 # We make this method a property, to show that it doesn't use any
167 # system calls.
168 # Case-sensitive subclasses can redefine it to return self.
169 @property
170 def normcase(self):
171 """ Return an equivalent path with case-normalized elements. """
172 if self.isrel:
173 return self.__class__(self.normcasestr(element)
174 for element in self)
175 else:
176 def gen():
177 it = iter(self)
178 yield it.next()
179 for element in it:
180 yield self.normcasestr(element)
181 return self.__class__(gen())
182
183 @classmethod
184 def _normalize_elements(cls, elements):
185 # This method gets an iterable over path elements.
186 # It should return an iterator over normalized path elements -
187 # that is, curdir elements should be ignored.
188
189 for i, element in enumerate(elements):
190 if isinstance(element, str):
191 if element != cls.curdir:
192 if (not element or
193 cls._sep in element or
194 (cls._altsep and cls._altsep in element)):
195 # Those elements will cause path(str(x)) != x
196 raise ValueError, "Element %r is invalid" % element
197 yield element
198 elif i == 0 and isinstance(element, cls._OSBaseRoot):
199 yield element
200 else:
201 raise TypeError, "Element %r is of a wrong type" % element
202
203 def __new__(cls, arg=None):
204 """ Create a new path object.
205
206 If arg isn't given, an empty path, which represents the current
207 working directory, is returned.
208 If arg is a string, it is parsed into a logical path.
209 If arg is an iterable over path elements, a new path is created from
210 them.
211 """
212 if arg is None:
213 return tuple.__new__(cls)
214 elif type(arg) is cls:
215 return arg
216 elif isinstance(arg, str):
217 return tuple.__new__(cls, cls._parse_str(arg))
218 else:
219 return tuple.__new__(cls, cls._normalize_elements(arg))
220
221 def __init__(self, arg=None):
222 # Since paths are immutable, we can cache the string representation
223 self._cached_str = None
224
225 def _build_str(self):
226 # Return a string representation of self.
227 #
228 # This is a default implementation, which may be overriden by
229 # subclasses (form example, MacPath)
230 if not self:
231 return self.curdir
232 elif isinstance(self[0], self._OSBaseRoot):
233 return str(self[0]) + self._sep.join(self[1:])
234 else:
235 return self._sep.join(self)
236
237 def __str__(self):
238 """ Return a string representation of self. """
239 if self._cached_str is None:
240 self._cached_str = self._build_str()
241 return self._cached_str
242
243 def __repr__(self):
244 # We want path, not the real class name.
245 return 'path(%r)' % str(self)
246
247 @property
248 def isabs(self):
249 """ Return whether this path represent an absolute path.
250
251 An absolute path is a path whose meaning doesn't change when the
252 the current working directory changes.
253
254 (Note that this is not the same as "not self.isrelative")
255 """
256 return len(self) > 0 and \
isinstance(self[0], self._OSBaseRoot) and \
self[0].isabs
257
258 @property
259 def isrel(self):
260 """ Return whether this path represents a relative path.
261
262 A relative path is a path without a root element, so it can be
263 concatenated to other paths.
264
265 (Note that this is not the same as "not self.isabs")
266 """
267 return len(self) == 0 or \
not isinstance(self[0], self._OSBaseRoot)
268
269 # Wrap a few tuple methods to return path objects
270
271 def __add__(self, other):
272 other = self.__class__(other)
273 if not other.isrel:
274 raise ValueError, "Right operand should be a relative path"
275 return self.__class__(itertools.chain(self, other))
276
277 def __radd__(self, other):
278 if not self.isrel:
279 raise ValueError, "Right operand should be a relative path"
280 other = self.__class__(other)
281 return self.__class__(itertools.chain(other, self))
282
283 def __getslice__(self, *args):
284 return self.__class__(tuple.__getslice__(self, *args))
285
286 def __mul__(self, *args):
287 if not self.isrel:
288 raise ValueError, "Only relative paths can be multiplied"
289 return self.__class__(tuple.__mul__(self, *args))
290
291 def __rmul__(self, *args):
292 if not self.isrel:
293 raise ValueError, "Only relative paths can be multiplied"
294 return self.__class__(tuple.__rmul__(self, *args))
295
296 def __eq__(self, other):
297 return tuple.__eq__(self, self.__class__(other))
298 def __ge__(self, other):
299 return tuple.__ge__(self, self.__class__(other))
300 def __gt__(self, other):
301 return tuple.__gt__(self, self.__class__(other))
302 def __le__(self, other):
303 return tuple.__le__(self, self.__class__(other))
304 def __lt__(self, other):
305 return tuple.__lt__(self, self.__class__(other))
306 def __ne__(self, other):
307 return tuple.__ne__(self, self.__class__(other))
308
309
310 # ----------------------------------------------------------------
311 # Now come the methods which use system calls.
312
313 # --- Path transformation which use system calls
314
315 @classmethod
316 def cwd(cls):
317 return cls(os.getcwd())
318
319 def chdir(self):
320 return os.chdir(str(self))
321
322 def abspath(self):
323 if not self:
324 return self.cwd()
325 if isinstance(self[0], self._OSBaseRoot):
326 if self[0].isabs:
327 return self
328 else:
329 return self[0].abspath() + self[1:]
330 else:
331 return self.cwd() + self
332
333 def realpath(self):
334 return self.__class__(os.path.realpath(str(self)))
335
336 def relpathto(self, dst):
337 """ Return a relative path from self to dest.
338
339 This method examines self.realpath() and dest.realpath(). If
340 they have the same root element, a path in the form
341 path([path.pardir, path.pardir, ..., dir1, dir2, ...])
342 is returned. If they have different root elements,
343 dest.realpath() is returned.
344 """
345 src = self.realpath()
346 dst = self.__class__(dst).realpath()
347
348 if src[0] == dst[0]:
349 # They have the same root
350
351 # find the length of the equal prefix
352 i = 1
353 while i < len(src) and i < len(dst) and \
self.normcasestr(src[i]) == self.normcasestr(dst[i]):
354 i += 1
355
356 return [self.pardir] * (len(src) - i) + dst[i:]
357
358 else:
359 # They don't have the same root
360 return dst
361
362
363
364
365 # --- Expand
366
367 def expanduser(self):
368 return path(os.path.expanduser(str(self)))
369
370 def expandvars(self):
371 return path(os.path.expandvars(str(self)))
372
373
374 # --- Info about the path
375
376 def stat(self):
377 return StatWrapper(os.stat(str(self)))
378
379 def exists(self):
380 try:
381 self.stat()
382 except OSError:
383 return False
384 else:
385 return True
386
387 def isdir(self):
388 try:
389 return self.stat().isdir
390 except OSError:
391 return False
392
393 def isfile(self):
394 try:
395 return self.stat().isfile
396 except OSError:
397 return False
398
399 def lstat(self):
400 return StatWrapper(os.lstat(str(self)))
401
402 def lexists(self):
403 try:
404 self.lstat()
405 except OSError:
406 return False
407 else:
408 return True
409
410 def lisdir(self):
411 try:
412 return self.stat().lisdir
413 except OSError:
414 return False
415
416 def lisfile(self):
417 try:
418 return self.stat().lisfile
419 except OSError:
420 return False
421
422 def islink(self):
423 try:
424 return self.lstat().islink
425 except OSError:
426 return False
427
428 def ismount(self):
429 return os.path.ismount(str(self))
430
431 def access(self, mode):
432 """ Return true if current user has access to this path.
433
434 mode - One of the constants os.F_OK, os.R_OK, os.W_OK, os.X_OK
435 """
436 return os.access(str(self), mode)
437
438 # Additional methods in subclasses:
439 # statvfs (PosixPath)
440 # pathconf (PosixPath, XXX MacPath)
441 # samefile (PosixPath, XXX MacPath)
442
443
444 # --- Modifying operations on files and directories
445
446 def utime(self, times):
447 """ Set the access and modified times of this file. """
448 os.utime(str(self), times)
449
450 def chmod(self, mode):
451 os.chmod(str(self), mode)
452
453 def rename(self, new):
454 os.rename(str(self), str(new))
455
456 # Additional methods in subclasses:
457 # chown (PosixPath, XXX MacPath)
458 # lchown (PosixPath, XXX MacPath)
459
460
461 # --- Create/delete operations on directories
462
463 def mkdir(self, mode=0777):
464 os.mkdir(str(self), mode)
465
466 def makedirs(self, mode=0777):
467 os.makedirs(str(self), mode)
468
469 def rmdir(self):
470 os.rmdir(str(self))
471
472 def removedirs(self, base=None):
473 """ Remove empty directories recursively.
474
475 If the directory is empty, remove it. If the parent directory becomes
476 empty, remove it too. Continue until a directory can't be removed,
477 because it's not empty or for other reasons.
478 If base is given, it should be a prefix of self. base won't be removed
479 even if it becomes empty.
480 Note: only directories explicitly listed in the path will be removed.
481 This means that if self is a relative path, predecesors of the
482 current working directory won't be removed.
483 """
484 if not self.stat().isdir:
485 raise OSError, 'removedirs only works on directories.'
486 base = self.__class__(base)
487 if base:
488 if not self[:len(base)] == base:
489 raise ValueError, 'base should be a prefix of self.'
490 stopat = len(base)
491 else:
492 stopat = 0
493 for i in xrange(len(self), stopat, -1):
494 try:
495 self[:i].rmdir()
496 except OSError:
497 break
498
499 def rmtree(self, *args):
500 return shutil.rmtree(str(self), *args)
501
502
503 # --- Modifying operations on files
504
505 def touch(self):
506 """ Set the access/modified times of this file to the current time.
507 Create the file if it does not exist.
508 """
509 fd = os.open(str(self), os.O_WRONLY | os.O_CREAT, 0666)
510 os.close(fd)
511 os.utime(str(self), None)
512
513 def remove(self):
514 os.remove(str(self))
515
516 def copy(self, dst, copystat=False):
517 """ Copy file from self to dst.
518
519 If copystat is False, copy data and mode bits ("cp self dst").
520 If copystat is True, copy data and all stat info ("cp -p self dst").
521
522 The destination may be a directory. If so, a file with the same base
523 name as self will be created in that directory.
524 """
525 dst = self.__class__(dst)
526 if dst.stat().isdir:
527 dst += self[-1]
528 shutil.copyfile(str(self), str(dst))
529 if copystat:
530 shutil.copystat(str(self), str(dst))
531 else:
532 shutil.copymode(str(self), str(dst))
533
534 def move(self, dst):
535 dst = self.__class__(dst)
536 return shutil.move(str(self), str(dst))
537
538
539 # --- Links
540
541 # In subclasses:
542 # link (PosixPath, XXX MacPath)
543 # writelink (PosixPath) - what about MacPath?
544 # readlink (PosixPath, XXX MacPath)
545 # readlinkpath (PosixPath, XXXMacPath)
546
547
548 # --- Extra
549
550 # In subclasses:
551 # mkfifo (PosixPath, XXX MacPath)
552 # mknod (PosixPath, XXX MacPath)
553 # chroot (PosixPath, XXX MacPath)
554 #
555 # startfile (NTPath)
556
557
558 # --- Globbing
559
560 # If the OS supports it, _id should be a function that gets a stat object
561 # and returns a unique id of a file.
562 # It the OS doesn't support it, it should be None.
563 _id = None
564
565 @staticmethod
566 def _match_element(comp_element, element):
567 # Does a filename match a compiled pattern element?
568 # The filename should be normcased.
569 if comp_element is None:
570 return True
571 elif isinstance(comp_element, str):
572 return comp_element == element
573 else:
574 return comp_element.match(element)
575
576 def _glob(cls, pth, comp_pattern, topdown, onlydirs, onlyfiles,
577 positions, on_path, stat):
578 """ The recursive function used by glob.
579
580 This version treats symbolic links as files. Broken symlinks won't be
581 listed.
582
583 pth is a dir in which we search.
584
585 comp_pattern is the compiled pattern. It's a sequence which should
586 consist of three kinds of elements:
587 * None - matches any number of subdirectories, including 0.
588 * a string - a normalized name, when exactly one name can be matched.
589 * a regexp - for testing if normalized names match.
590
591 positions is a sequence of positions on comp_pattern that children of
592 path may match. On the first call, if will be [0].
593
594 on_path is a set of inode identifiers on path, or None if circles
595 shouldn't be checked.
596
597 stat is the appropriate stat function - cls.stat or cls.lstat.
598 """
599
600 if len(positions) == 1 and isinstance(comp_pattern[positions[0]], str):
601 # We don't have to listdir if exactly one file name can match.
602 # Since we always stat the files, it's ok if the file doesn't exist.
603 listdir = [comp_pattern[positions[0]]]
604 else:
605 listdir = os.listdir(str(pth))
606 listdir.sort()
607
608 for subfile in listdir:
609 newpth = pth + subfile
610 # We don't strictly need to stat a file if we don't follow symlinks
611 # AND positions == [len(comp_pattern)-1] AND
612 # not isinstance(comp_pattern[-1], str), but do me a favour...
613 try:
614 st = stat(newpth)
615 except OSError:
616 continue
617 newpositions = []
618 subfilenorm = cls.normcasestr(subfile)
619
620 if topdown:
621 # If not topdown, it happens after we handle subdirs
622 if positions[-1] == len(comp_pattern) - 1:
623 if cls._match_element(comp_pattern[-1], subfilenorm):
624 if not ((onlydirs and not st.isdir) or
625 (onlyfiles and not st.isfile)):
626 yield newpth
627
628 for pos in reversed(positions):
629 if st.isdir:
630 comp_element = comp_pattern[pos]
631 if pos + 1 < len(comp_pattern):
632 if cls._match_element(comp_element, subfilenorm):
633 newpositions.append(pos + 1)
634 if comp_pattern[pos + 1] is None:
635 # We should stop at '..'
636 break
637 if comp_element is None:
638 newpositions.append(pos)
639 # We don't have to break - there are not supposed
640 # to be any positions before '..'.
641
642 if newpositions:
643 newpositions.reverse()
644
645 if on_path is not None:
646 newpath_id = cls._id(st)
647 if newpath_id in on_path:
648 raise OSError, "Circular path encountered"
649 on_path.add(newpath_id)
650
651 for x in cls._glob(newpth,
652 comp_pattern, topdown, onlydirs, onlyfiles,
653 newpositions, on_path, stat):
654 yield x
655
656 if on_path is not None:
657 on_path.remove(newpath_id)
658
659 if not topdown:
660 # If topdown, it happens after we handle subdirs
661 if positions[-1] == len(comp_pattern) - 1:
662 if cls._match_element(comp_pattern[-1], subfilenorm):
663 if not ((onlydirs and not st.isdir) or
664 (onlyfiles and not st.isfile)):
665 yield newpth
666
667 _magic_check = re.compile('[*?[]')
668
669 @classmethod
670 def _has_magic(cls, s):
671 return cls._magic_check.search(s) is not None
672
673 _cache = {}
674
675 @classmethod
676 def _compile_pattern(cls, pattern):
677 # Get a pattern, return the list of compiled pattern elements
678 # and the list of initial positions.
679 pattern = cls(pattern)
680 if not pattern.isrel:
681 raise ValueError, "pattern should be a relative path."
682
683 comp_pattern = []
684 last_was_none = False
685 for element in pattern:
686 element = cls.normcasestr(element)
687 if element == '**':
688 if not last_was_none:
689 comp_pattern.append(None)
690 else:
691 last_was_none = False
692 if not cls._has_magic(element):
693 comp_pattern.append(element)
694 else:
695 try:
696 r = cls._cache[element]
697 except KeyError:
698 r = re.compile(fnmatch.translate(element))
699 cls._cache[element] = r
700 comp_pattern.append(r)
701
702 if comp_pattern[0] is None and len(comp_pattern) > 1:
703 positions = [0, 1]
704 else:
705 positions = [0]
706
707 return comp_pattern, positions
708
709 def match(self, pattern):
710 """ Return whether self matches the given pattern.
711
712 pattern has the same meaning as in the glob method.
713 self should be relative.
714
715 This method doesn't use any system calls.
716 """
717 if not self.isrel:
718 raise ValueError, "self must be a relative path"
719 comp_pattern, positions = self._compile_pattern(pattern)
720
721 for element in self.normcase:
722 newpositions = []
723 for pos in reversed(positions):
724 if pos == len(comp_pattern):
725 # We matched the pattern but the path isn't finished -
726 # too bad
727 continue
728 comp_element = comp_pattern[pos]
729 if self._match_element(comp_element, element):
730 newpositions.append(pos + 1)
731 if comp_element is None:
732 newpositions.append(pos)
733 # No need to continue after a '**'
734 break
735 newpositions.reverse()
736 positions = newpositions
737 if not positions:
738 # No point in carrying on
739 break
740
741 return (len(comp_pattern) in positions)
742
743 def glob(self, pattern='*', topdown=True, onlydirs=False, onlyfiles=False):
744 """ Return an iterator over all files in self matching pattern.
745
746 pattern should be a relative path, which may include wildcards.
747 In addition to the regular shell wildcards, you can use '**', which
748 matches any number of directories, including 0.
749
750 If topdown is True (the default), a directory is yielded before its
751 descendents. If it's False, a directory is yielded after its
752 descendents.
753
754 If onlydirs is True, only directories will be yielded. If onlyfiles
755 is True, only regular files will be yielded.
756
757 This method treats symbolic links as regular files. Broken symlinks
758 won't be yielded.
759 """
760
761 if onlydirs and onlyfiles:
762 raise ValueError, \
"Only one of onlydirs and onlyfiles can be specified."
763
764 comp_pattern, positions = self._compile_pattern(pattern)
765
766 if self._id is not None and None in comp_pattern:
767 on_path = set([self._id(self.stat())])
768 else:
769 on_path = None
770
771 for x in self._glob(self, comp_pattern, topdown, onlydirs, onlyfiles,
772 positions, on_path, self.__class__.stat):
773 yield x
774
775 def lglob(self, pattern='*', topdown=True, onlydirs=False, onlyfiles=False):
776 """ Return an iterator over all files in self matching pattern.
777
778 pattern should be a relative path, which may include wildcards.
779 In addition to the regular shell wildcards, you can use '**', which
780 matches any number of directories, including 0.
781
782 If topdown is True (the default), a directory is yielded before its
783 descendents. If it's False, a directory is yielded after its
784 descendents.
785
786 If onlydirs is True, only directories will be yielded. If onlyfiles
787 is True, only regular files will be yielded.
788
789 This method treats symbolic links as special files - they won't be
790 followed, and they will be yielded even if they're broken.
791 """
792
793 if onlydirs and onlyfiles:
794 raise ValueError, \
"Only one of onlydirs and onlyfiles can be specified."
795
796 comp_pattern, positions = self._compile_pattern(pattern)
797
798 for x in self._glob(self, comp_pattern, topdown, onlydirs, onlyfiles,
799 positions, None, self.__class__.lstat):
800 yield x
801
802
803 class PosixPath(BasePath):
804 """ Represents POSIX paths. """
805
806 class _PosixRoot(BasePath._BaseRoot):
807 """ Represents the filesystem root (/).
808
809 There's only one root on posix systems, so this is a singleton.
810 """
811 instance = None
812 def __new__(cls):
813 if cls.instance is None:
814 instance = object.__new__(cls)
815 cls.instance = instance
816 return cls.instance
817
818 def __str__(self):
819 return '/'
820
821 def __repr__(self):
822 return 'path.ROOT'
823
824 isabs = True
825
826 _OSBaseRoot = _PosixRoot
827
828 ROOT = _PosixRoot()
829
830 # Public constants
831 curdir = '.'
832 pardir = '..'
833
834 # Private constants
835 _sep = '/'
836 _altsep = None
837
838 @classmethod
839 def _parse_str(cls, pathstr):
840 # get a path string and return an iterable over path elements.
841 if pathstr.startswith('/'):
842 if pathstr.startswith('//') and not pathstr.startswith('///'):
843 # Two initial slashes have application-specific meaning
844 # in POSIX, and it's not supported currently.
845 raise NotImplementedError, \
"Paths with two leading slashes aren't supported."
846 yield cls.ROOT
847 for element in pathstr.split('/'):
848 if element == '' or element == cls.curdir:
849 continue
850 # '..' aren't specially treated, since popping the last
851 # element isn't correct if the last element was a symbolic
852 # link.
853 yield element
854
855
856 # POSIX-specific methods
857
858
859 # --- Info about the path
860
861 def statvfs(self):
862 """ Perform a statvfs() system call on this path. """
863 return os.statvfs(str(self))
864
865 def pathconf(self, name):
866 return os.pathconf(str(self), name)
867
868 def samefile(self, other):
869 other = self.__class__(other)
870 s1 = self.stat()
871 s2 = other.stat()
872 return s1.st_ino == s2.st_ino and \
s1.st_dev == s2.st_dev
873
874
875 # --- Modifying operations on files and directories
876
877 def chown(self, uid=None, gid=None):
878 if uid is None:
879 uid = -1
880 if gid is None:
881 gid = -1
882 return os.chown(str(self), uid, gid)
883
884 def lchown(self, uid=None, gid=None):
885 if uid is None:
886 uid = -1
887 if gid is None:
888 gid = -1
889 return os.lchown(str(self), uid, gid)
890
891
892 # --- Links
893
894 def link(self, newpath):
895 """ Create a hard link at 'newpath', pointing to this file. """
896 os.link(str(self), str(newpath))
897
898 def writelink(self, src):
899 """ Create a symbolic link at self, pointing to src.
900
901 src may be any string. Note that if it's a relative path, it
902 will be interpreted relative to self, not relative to the current
903 working directory.
904 """
905 os.symlink(str(src), str(self))
906
907 def readlink(self):
908 """ Return the path to which this symbolic link points.
909
910 The result is a string, which may be an absolute path, a
911 relative path (which should be interpreted relative to self[:-1]),
912 or any arbitrary string.
913 """
914 return os.readlink(str(self))
915
916 def readlinkpath(self):
917 """ Return the path to which this symbolic link points. """
918 linkpath = self.__class__(self.readlink())
919 if linkpath.isrel:
920 return self + linkpath
921 else:
922 return linkpath
923
924
925 # --- Extra
926
927 def mkfifo(self, *args):
928 return os.mkfifo(str(self), *args)
929
930 def mknod(self, *args):
931 return os.mknod(str(self), *args)
932
933 def chroot(self):
934 return os.chroot(str(self))
935
936
937 # --- Globbing
938
939 @staticmethod
940 def _id(stat):
941 return (stat.st_ino, stat.st_dev)
942
943
944 class NTPath(BasePath):
945 """ Represents paths on Windows operating systems. """
946
947 class _NTBaseRoot(BasePath._BaseRoot):
948 """ The base class of all Windows root classes. """
949 pass
950
951 _OSBaseRoot = _NTBaseRoot
952
953 class _CurRootType(_NTBaseRoot):
954 """ Represents the root of the current working drive.
955
956 This class is a singleton. It represents the root of the current
957 working drive - paths starting with '\'.
958 """
959 instance = None
960 def __new__(cls):
961 if cls.instance is None:
962 instance = object.__new__(cls)
963 cls.instance = instance
964 return cls.instance
965
966 def __str__(self):
967 return '\\'
968
969 def __repr__(self):
970 return 'path.CURROOT'
971
972 isabs = False
973
974 def abspath(self):
975 from nt import _getfullpathname
976 return NTPath(_getfullpathname(str(self)))
977
978 CURROOT = _CurRootType()
979
980 class Drive(_NTBaseRoot):
981 """ Represents the root of a specific drive. """
982 def __init__(self, letter):
983 # Drive letter is normalized - we don't lose any information
984 if len(letter) != 1 or letter not in string.letters:
985 raise ValueError, 'Should get one letter'
986 self._letter = letter.lower()
987
988 @property
989 def letter(self):
990 # We use a property because we want the object to be immutable.
991 return self._letter
992
993 def __str__(self):
994 return '%s:\\' % self.letter
995
996 def __repr__(self):
997 return 'path.Drive(%r)' % self.letter
998
999 isabs = True
1000
1001 class UnrootedDrive(_NTBaseRoot):
1002 """ Represents the current working directory on a specific drive. """
1003 def __init__(self, letter):
1004 # Drive letter is normalized - we don't lose any information
1005 if len(letter) != 1 or letter not in string.letters:
1006 raise ValueError, 'Should get one letter'
1007 self._letter = letter.lower()
1008
1009 @property
1010 def letter(self):
1011 # We use a property because we want the object to be immutable.
1012 return self._letter
1013
1014 def __str__(self):
1015 return '%s:' % self.letter
1016
1017 def __repr__(self):
1018 return 'path.UnrootedDrive(%r)' % self.letter
1019
1020 isabs = False
1021
1022 def abspath(self):
1023 from nt import _getfullpathname
1024 return NTPath(_getfullpathname(str(self)))
1025
1026 class UNCRoot(_NTBaseRoot):
1027 """ Represents a UNC mount point. """
1028 def __init__(self, host, mountpoint):
1029 # Host and mountpoint are normalized - we don't lose any information
1030 self._host = host.lower()
1031 self._mountpoint = mountpoint.lower()
1032
1033 @property
1034 def host(self):
1035 # We use a property because we want the object to be immutable.
1036 return self._host
1037
1038 @property
1039 def mountpoint(self):
1040 # We use a property because we want the object to be immutable.
1041 return self._mountpoint
1042
1043 def __str__(self):
1044 return '\\\\%s\\%s\\' % (self.host, self.mountpoint)
1045
1046 def __repr__(self):
1047 return 'path.UNCRoot(%r, %r)' % (self.host, self.mountpoint)
1048
1049 isabs = True
1050
1051
1052 # Public constants
1053 curdir = '.'
1054 pardir = '..'
1055
1056 # Private constants
1057 _sep = '\\'
1058 _altsep = '/'
1059
1060 @staticmethod
1061 def normcasestr(string):
1062 """ Normalize the case of one path element.
1063
1064 On Windows, this returns string.lower()
1065 """
1066 return string.lower()
1067
1068 @classmethod
1069 def _parse_str(cls, pathstr):
1070 # get a path string and return an iterable over path elements.
1071
1072 # First, replace all backslashes with slashes.
1073 # I know that it should have been the other way round, but I can't
1074 # stand all those escapes.
1075
1076 pathstr = pathstr.replace('\\', '/')
1077
1078 # Handle the root element
1079
1080 if pathstr.startswith('/'):
1081 if pathstr.startswith('//'):
1082 # UNC Path
1083 if pathstr.startswith('///'):
1084 raise ValueError, \
"Paths can't start with more than two slashes"
1085 index = pathstr.find('/', 2)
1086 if index == -1:
1087 raise ValueError, \
"UNC host name should end with a slash"
1088 index2 = index+1
1089 while pathstr[index2:index2+1] == '/':
1090 index2 += 1
1091 if index2 == len(pathstr):
1092 raise ValueError, \
"UNC mount point is empty"
1093 index3 = pathstr.find('/', index2)
1094 if index3 == -1:
1095 index3 = len(pathstr)
1096 yield cls.UNCRoot(pathstr[2:index], pathstr[index2:index3])
1097 pathstr = pathstr[index3:]
1098 else:
1099 # CURROOT
1100 yield cls.CURROOT
1101 else:
1102 if pathstr[1:2] == ':':
1103 if pathstr[2:3] == '/':
1104 # Rooted drive
1105 yield cls.Drive(pathstr[0])
1106 pathstr = pathstr[3:]
1107 else:
1108 # Unrooted drive
1109 yield cls.UnrootedDrive(pathstr[0])
1110 pathstr = pathstr[2:]
1111
1112 # Handle all other elements
1113
1114 for element in pathstr.split('/'):
1115 if element == '' or element == cls.curdir:
1116 continue
1117 # We don't treat pardir specially, since in the presence of
1118 # links there's nothing to do about them.
1119 # Windows doesn't have links, but why not keep path handling
1120 # similiar?
1121 yield element
1122
1123
1124 # NT-specific methods
1125
1126 # --- Extra
1127
1128 def startfile(self):
1129 return os.startfile(str(self))
1130
1131 if os.name == 'posix':
1132 path = PosixPath
1133 elif os.name == 'nt':
1134 path = NTPath
1135 else:
1136 raise NotImplementedError, \
"The path object is currently not implemented for OS %r" % os.name
EditText (last edited 2008-12-17 21:18:52 by PaulBoddie)