Mercurial > ~astiob > upreckon > hgweb
annotate upreckon/files.py @ 246:1bc89faac941 2.04
Fixed: match='re' could produce duplicate test identifiers
files.Files.regexp(pattern) now makes sure to return only one
metafile for each matching virtual path, namely, the one that would
be returned for that virtual path by files.Files.from_virtual_path.
author | Oleg Oshmyan <chortos@inbox.lv> |
---|---|
date | Thu, 03 Oct 2013 01:19:09 +0300 |
parents | 7827e63cd148 |
children | f5847d29e838 |
rev | line source |
---|---|
194
8c30a2c8a09e
Updated copyright years in files.py
Oleg Oshmyan <chortos@inbox.lv>
parents:
193
diff
changeset
|
1 # Copyright (c) 2010-2011 Chortos-2 <chortos@inbox.lv> |
16 | 2 |
21 | 3 """File access routines and classes with support for archives.""" |
4 | |
5 from __future__ import division, with_statement | |
6 | |
146
d5b6708c1955
Distutils support, reorganization and cleaning up
Oleg Oshmyan <chortos@inbox.lv>
parents:
133
diff
changeset
|
7 from .compat import * |
212
1cbe2c428942
Cleaned up import statements
Oleg Oshmyan <chortos@inbox.lv>
parents:
209
diff
changeset
|
8 import contextlib, os, posixpath, re, shutil |
21 | 9 |
10 # You don't need to know about anything else. | |
193
a76cdc26ba9d
Added conf. var. match and match='regexp' for non-archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
178
diff
changeset
|
11 __all__ = 'File', 'regexp' |
21 | 12 |
13 # In these two variables, use full stops no matter what os.extsep is; | |
14 # all full stops will be converted to os.extsep on the fly | |
15 archives = 'tests.tar', 'tests.zip', 'tests.tgz', 'tests.tar.gz', 'tests.tbz2', 'tests.tar.bz2' | |
16 formats = {} | |
17 | |
18 class Archive(object): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
19 __slots__ = () |
21 | 20 |
21 if ABCMeta: | |
22 __metaclass__ = ABCMeta | |
23 | |
24 def __new__(cls, path): | |
25 """ | |
26 Create a new instance of the archive class corresponding | |
27 to the file name in the given path. | |
28 """ | |
29 if cls is not Archive: | |
30 return object.__new__(cls) | |
31 else: | |
32 # Do this by hand rather than through os.path.splitext | |
33 # because we support multi-dotted file name extensions | |
34 ext = path.partition(os.path.extsep)[2] | |
35 while ext: | |
36 if ext in formats: | |
37 return formats[ext](path) | |
38 ext = ext.partition(os.path.extsep)[2] | |
39 raise LookupError("unsupported archive file name extension in file name '%s'" % filename) | |
40 | |
41 @abstractmethod | |
42 def __init__(self, path): raise NotImplementedError | |
43 | |
44 @abstractmethod | |
45 def extract(self, name, target): raise NotImplementedError | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
46 |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
47 @abstractmethod |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
48 def open(self, name): raise NotImplementedError |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
49 |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
50 @abstractmethod |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
51 def exists(self, name): raise NotImplementedError |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
52 |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
53 @abstractmethod |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
54 def listdir(self, name): raise NotImplementedError |
16 | 55 |
21 | 56 try: |
57 import tarfile | |
31 | 58 except ImportError: |
59 TarArchive = None | |
60 else: | |
21 | 61 class TarArchive(Archive): |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
62 __slots__ = '_tarfile', '_files', '_dirs', '_names' |
21 | 63 |
64 def __init__(self, path): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
65 self._tarfile = tarfile.open(path) |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
66 files, dirs = {}, set(('/',)) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
67 for member in self._tarfile.getmembers(): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
68 cutname = posixpath.normpath('/' + member.name) |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
69 if cutname == '/': |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
70 continue |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
71 if member.isfile(): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
72 files[cutname] = member |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
73 cutname = posixpath.dirname(cutname) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
74 elif not member.isdir(): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
75 continue |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
76 while cutname != '/': |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
77 dirs.add(cutname) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
78 cutname = posixpath.dirname(cutname) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
79 self._files = files |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
80 self._dirs = frozenset(dirs) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
81 self._names = self._dirs | frozenset(files) |
21 | 82 |
83 def extract(self, name, target): | |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
84 member = self._files[posixpath.normpath('/' + name)] |
21 | 85 member.name = target |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
86 self._tarfile.extract(member) |
21 | 87 |
88 def open(self, name): | |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
89 name = posixpath.normpath('/' + name) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
90 return self._tarfile.extractfile(self._files[name]) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
91 |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
92 def exists(self, name): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
93 return posixpath.normpath('/' + name) in self._names |
21 | 94 |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
95 def listdir(self, name): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
96 normname = posixpath.normpath('/' + name) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
97 if normname not in self._dirs: |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
98 raise KeyError('No such directory: %r' % name) |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
99 if normname != '/': |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
100 normname += '/' |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
101 nnlen = len(normname) |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
102 return [fname[nnlen:] for fname in self._names |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
103 if fname.startswith(normname) and |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
104 fname.find('/', nnlen) == -1] |
21 | 105 |
106 def __enter__(self): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
107 if hasattr(self._tarfile, '__enter__'): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
108 self._tarfile.__enter__() |
21 | 109 return self |
110 | |
111 def __exit__(self, exc_type, exc_value, traceback): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
112 if hasattr(self._tarfile, '__exit__'): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
113 return self._tarfile.__exit__(exc_type, exc_value, traceback) |
21 | 114 elif exc_type is None: |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
115 self._tarfile.close() |
21 | 116 else: |
117 # This code was shamelessly copied from tarfile.py of Python 2.7 | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
118 if not self._tarfile._extfileobj: |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
119 self._tarfile.fileobj.close() |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
120 self._tarfile.closed = True |
21 | 121 |
122 formats['tar'] = formats['tgz'] = formats['tar.gz'] = formats['tbz2'] = formats['tar.bz2'] = TarArchive | |
123 | |
124 try: | |
125 import zipfile | |
31 | 126 except ImportError: |
127 ZipArchive = None | |
128 else: | |
21 | 129 class ZipArchive(Archive): |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
130 __slots__ = '_zipfile', '_files', '_dirs', '_names' |
21 | 131 |
132 def __init__(self, path): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
133 self._zipfile = zipfile.ZipFile(path) |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
134 files, dirs = {}, set(('/',)) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
135 for member in self._zipfile.infolist(): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
136 cutname = posixpath.normpath('/' + member.filename) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
137 if not member.filename.endswith('/'): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
138 files[cutname] = member |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
139 cutname = posixpath.dirname(cutname) |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
140 while cutname != '/': |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
141 dirs.add(cutname) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
142 cutname = posixpath.dirname(cutname) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
143 self._files = files |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
144 self._dirs = frozenset(dirs) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
145 self._names = self._dirs | frozenset(files) |
21 | 146 |
147 def extract(self, name, target): | |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
148 member = self._files[posixpath.normpath('/' + name)] |
98
62a96d51bf94
Fixed ZipArchive.extract with relative paths on Windows
Oleg Oshmyan <chortos@inbox.lv>
parents:
91
diff
changeset
|
149 # FIXME: 2.5 lacks ZipFile.extract |
21 | 150 if os.path.isabs(target): |
151 # To my knowledge, this is as portable as it gets | |
152 path = os.path.join(os.path.splitdrive(target)[0], os.path.sep) | |
98
62a96d51bf94
Fixed ZipArchive.extract with relative paths on Windows
Oleg Oshmyan <chortos@inbox.lv>
parents:
91
diff
changeset
|
153 member.filename = os.path.relpath(target, path) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
154 self._zipfile.extract(member, path) |
21 | 155 else: |
98
62a96d51bf94
Fixed ZipArchive.extract with relative paths on Windows
Oleg Oshmyan <chortos@inbox.lv>
parents:
91
diff
changeset
|
156 member.filename = os.path.relpath(target) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
157 self._zipfile.extract(member) |
21 | 158 |
159 def open(self, name): | |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
160 name = posixpath.normpath('/' + name) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
161 # FIXME: 2.5 lacks ZipFile.open |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
162 return self._zipfile.open(self._files[name]) |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
163 |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
164 def exists(self, name): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
165 return posixpath.normpath('/' + name) in self._names |
21 | 166 |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
167 def listdir(self, name): |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
168 normname = posixpath.normpath('/' + name) |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
169 if normname not in self._dirs: |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
170 raise KeyError('No such directory: %r' % name) |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
171 if normname != '/': |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
172 normname += '/' |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
173 nnlen = len(normname) |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
174 return [fname[nnlen:] for fname in self._names |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
175 if fname.startswith(normname) and |
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
176 fname.find('/', nnlen) == -1] |
21 | 177 |
178 def __enter__(self): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
179 if hasattr(self._zipfile, '__enter__'): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
180 self._zipfile.__enter__() |
21 | 181 return self |
182 | |
183 def __exit__(self, exc_type, exc_value, traceback): | |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
184 if hasattr(self._zipfile, '__exit__'): |
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
185 return self._zipfile.__exit__(exc_type, exc_value, traceback) |
21 | 186 else: |
195
c2490e39fd70
Revamped the implementation of files.Archive subclasses
Oleg Oshmyan <chortos@inbox.lv>
parents:
194
diff
changeset
|
187 return self._zipfile.close() |
21 | 188 |
189 formats['zip'] = ZipArchive | |
190 | |
191 # Remove unsupported archive formats and replace full stops | |
192 # with the platform-dependent file name extension separator | |
193 def issupported(filename, formats=formats): | |
194 ext = filename.partition('.')[2] | |
195 while ext: | |
196 if ext in formats: return True | |
197 ext = ext.partition('.')[2] | |
198 return False | |
199 archives = [filename.replace('.', os.path.extsep) for filename in filter(issupported, archives)] | |
200 formats = dict((item[0].replace('.', os.path.extsep), item[1]) for item in items(formats)) | |
201 | |
202 open_archives = {} | |
203 | |
204 def open_archive(path): | |
205 if path in open_archives: | |
206 return open_archives[path] | |
207 else: | |
208 open_archives[path] = archive = Archive(path) | |
209 return archive | |
16 | 210 |
21 | 211 class File(object): |
208 | 212 __slots__ = ('virtual_path', 'archive', |
213 '_external_path', '_internal_path', '_has_tests') | |
21 | 214 |
208 | 215 def __init__(self, _virtual_path='', _external_path='', _internal_path='', |
216 _archive=None, _has_tests=False): | |
217 self.virtual_path = _virtual_path | |
218 self._external_path = _external_path | |
219 self._internal_path = _internal_path | |
220 self.archive = _archive | |
221 self._has_tests = _has_tests | |
222 if not _archive: | |
223 try: | |
224 self.archive = open_archive(_external_path) | |
225 except Exception: | |
226 pass | |
227 | |
228 @property | |
229 def full_real_path(self): | |
230 intpath = self._internal_path.split('/') if self._internal_path else () | |
231 return os.path.join(self._external_path, | |
232 *(filename.replace('.', os.path.extsep) | |
219
7827e63cd148
Corrected a wrongly indented line
Oleg Oshmyan <chortos@inbox.lv>
parents:
212
diff
changeset
|
233 for filename in intpath)) |
208 | 234 |
235 def exists(self): | |
236 if self.archive: | |
237 return self.archive.exists(self._internal_path) | |
21 | 238 else: |
208 | 239 return (not self._external_path or |
240 os.path.exists(self._external_path)) | |
21 | 241 |
242 def open(self): | |
243 if self.archive: | |
208 | 244 file = self.archive.open(self._internal_path) |
21 | 245 if hasattr(file, '__exit__'): |
246 return file | |
247 else: | |
248 return contextlib.closing(file) | |
249 else: | |
208 | 250 return open(self._external_path, 'rb') |
21 | 251 |
252 def copy(self, target): | |
253 if self.archive: | |
208 | 254 self.archive.extract(self._internal_path, target) |
255 else: | |
256 shutil.copy(self._external_path, target) | |
257 | |
258 def listdir(self): | |
259 if self.archive: | |
260 return self.archive.listdir(self._internal_path) | |
21 | 261 else: |
208 | 262 return os.listdir(self._external_path) |
263 | |
264 @classmethod | |
265 def from_virtual_path(cls, virtual_path, allow_root, msg): | |
266 metafile = cls()._realize_path(virtual_path.split('/'), allow_root) | |
267 if not metafile: | |
268 raise IOError("%s file with virtual path %r could not be found" % | |
269 (msg, virtual_path)) | |
270 assert metafile.virtual_path == virtual_path | |
271 return metafile | |
272 | |
273 def _realize_path(self, virtpath, allow_root): | |
274 if not self.exists(): | |
275 return None | |
276 elif not virtpath: | |
277 if allow_root or self._has_tests or self.archive: | |
278 return self | |
279 return None | |
280 cand = (self + virtpath[0])._realize_path(virtpath[1:], allow_root) | |
281 if cand: return cand | |
282 if not cand and not self._has_tests: | |
283 for metafile in self._add_tests(): | |
284 cand = metafile._realize_path(virtpath, allow_root) | |
285 if cand: return cand | |
286 if not cand and len(virtpath) > 1: | |
287 metafile = self._add_virtual(virtpath[0]) | |
288 cand = metafile._realize_path(virtpath[1:], allow_root) | |
289 if cand: return cand | |
290 | |
291 def _add_tests(self): | |
292 assert not self._has_tests | |
293 metafile = self.__add__('tests', False) | |
294 metafile._has_tests = True | |
295 yield metafile | |
296 if not self.archive: | |
297 for filename in archives: | |
298 yield self.__add__(filename, False) | |
299 | |
246
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
300 def _also_add_tests(self): |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
301 yield self |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
302 if not self._has_tests: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
303 for metafile in self._add_tests(): |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
304 yield metafile |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
305 |
208 | 306 def _add_virtual(self, filename): |
307 return File(posixpath.join(self.virtual_path, filename), | |
308 self._external_path, | |
309 self._internal_path, | |
310 self.archive, | |
311 self._has_tests) | |
312 | |
313 def __add__(self, filename, add_virtual=True): | |
314 if not isinstance(filename, basestring): | |
315 return NotImplemented | |
316 if add_virtual: | |
317 virtual_path = posixpath.join(self.virtual_path, filename) | |
318 else: | |
319 virtual_path = self.virtual_path | |
320 if self.archive: | |
321 return File(virtual_path, | |
322 self._external_path, | |
323 posixpath.join(self._internal_path, filename), | |
324 self.archive, | |
325 self._has_tests) | |
326 else: | |
327 filename = filename.replace('.', os.path.extsep) | |
328 return File(virtual_path, | |
329 os.path.join(self._external_path, filename), | |
330 _has_tests=self._has_tests) | |
196
67088c1765b4
Regexps now work with test archives
Oleg Oshmyan <chortos@inbox.lv>
parents:
195
diff
changeset
|
331 |
209
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
332 @classmethod |
246
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
333 def regexp(cls, pattern, _unique=True): |
209
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
334 if not pattern: |
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
335 yield cls('', os.curdir) |
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
336 return |
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
337 dirname, basename = posixpath.split(pattern) |
246
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
338 dirs = cls.regexp(dirname, False) |
209
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
339 reobj = re.compile(pattern + '$', re.UNICODE) |
246
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
340 if _unique: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
341 yielded = set() |
209
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
342 while dirs: |
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
343 newdirs = [] |
246
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
344 for testless_directory in dirs: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
345 for directory in testless_directory._also_add_tests(): |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
346 try: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
347 names = directory.listdir() |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
348 except Exception: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
349 continue |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
350 for name in names: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
351 dir_entry = directory + name |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
352 if re.match(reobj, dir_entry.virtual_path): |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
353 if not _unique: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
354 yield dir_entry |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
355 elif dir_entry.virtual_path not in yielded: |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
356 yield dir_entry |
1bc89faac941
Fixed: match='re' could produce duplicate test identifiers
Oleg Oshmyan <chortos@inbox.lv>
parents:
219
diff
changeset
|
357 yielded.add(dir_entry.virtual_path) |
209
c03a8113685d
Rewrote files.regexp as files.File.regexp (a class method)
Oleg Oshmyan <chortos@inbox.lv>
parents:
208
diff
changeset
|
358 dirs = newdirs |