comparison upreckon/files.py @ 196:67088c1765b4

Regexps now work with test archives Excuse me while I rewrite files.{File,regexp} almost from scratch...
author Oleg Oshmyan <chortos@inbox.lv>
date Mon, 15 Aug 2011 19:52:58 +0300
parents c2490e39fd70
children 79f4f2fdeead
comparison
equal deleted inserted replaced
195:c2490e39fd70 196:67088c1765b4
61 class TarArchive(Archive): 61 class TarArchive(Archive):
62 __slots__ = '_tarfile', '_files', '_dirs', '_names' 62 __slots__ = '_tarfile', '_files', '_dirs', '_names'
63 63
64 def __init__(self, path): 64 def __init__(self, path):
65 self._tarfile = tarfile.open(path) 65 self._tarfile = tarfile.open(path)
66 files, dirs = {}, set() 66 files, dirs = {}, set(('/',))
67 for member in self._tarfile.getmembers(): 67 for member in self._tarfile.getmembers():
68 cutname = posixpath.normpath(member.name).lstrip('/') 68 cutname = posixpath.normpath('/' + member.name)
69 while cutname.startswith('../'): 69 if cutname == '/':
70 cutname = cutname[3:]
71 if cutname in ('.', '..'):
72 continue 70 continue
73 if member.isfile(): 71 if member.isfile():
74 files[cutname] = member 72 files[cutname] = member
75 cutname = posixpath.dirname(cutname) 73 cutname = posixpath.dirname(cutname)
76 elif not member.isdir(): 74 elif not member.isdir():
77 continue 75 continue
78 while cutname: 76 while cutname != '/':
79 dirs.add(cutname) 77 dirs.add(cutname)
80 cutname = posixpath.dirname(cutname) 78 cutname = posixpath.dirname(cutname)
81 self._files = files 79 self._files = files
82 self._dirs = frozenset(dirs) 80 self._dirs = frozenset(dirs)
83 self._names = self._dirs | frozenset(files) 81 self._names = self._dirs | frozenset(files)
84 82
85 def extract(self, name, target): 83 def extract(self, name, target):
86 member = self._files[posixpath.normpath(name)] 84 member = self._files[posixpath.normpath('/' + name)]
87 member.name = target 85 member.name = target
88 self._tarfile.extract(member) 86 self._tarfile.extract(member)
89 87
90 def open(self, name): 88 def open(self, name):
91 name = posixpath.normpath(name) 89 name = posixpath.normpath('/' + name)
92 return self._tarfile.extractfile(self._files[name]) 90 return self._tarfile.extractfile(self._files[name])
93 91
94 def exists(self, name): 92 def exists(self, name):
95 return posixpath.normpath(name) in self._names 93 return posixpath.normpath('/' + name) in self._names
96 94
97 def listdir(self, name): 95 def listdir(self, name):
98 normname = posixpath.normpath(name) 96 normname = posixpath.normpath('/' + name)
99 if normname not in self._dirs: 97 if normname not in self._dirs:
100 raise KeyError('No such directory: %r' % name) 98 raise KeyError('No such directory: %r' % name)
101 normname += '/' 99 if normname != '/':
102 len_normname = len(normname) 100 normname += '/'
103 return [fname for fname in self._names 101 nnlen = len(normname)
104 if fname.startswith(normname) and 102 return [fname[nnlen:] for fname in self._names
105 fname.find('/', len_normname) == -1] 103 if fname.startswith(normname) and
104 fname.find('/', nnlen) == -1]
106 105
107 def __enter__(self): 106 def __enter__(self):
108 if hasattr(self._tarfile, '__enter__'): 107 if hasattr(self._tarfile, '__enter__'):
109 self._tarfile.__enter__() 108 self._tarfile.__enter__()
110 return self 109 return self
130 class ZipArchive(Archive): 129 class ZipArchive(Archive):
131 __slots__ = '_zipfile', '_files', '_dirs', '_names' 130 __slots__ = '_zipfile', '_files', '_dirs', '_names'
132 131
133 def __init__(self, path): 132 def __init__(self, path):
134 self._zipfile = zipfile.ZipFile(path) 133 self._zipfile = zipfile.ZipFile(path)
135 files, dirs = {}, set() 134 files, dirs = {}, set(('/',))
136 for member in self._zipfile.infolist(): 135 for member in self._zipfile.infolist():
137 cutname = posixpath.normpath(member.filename).lstrip('/') 136 cutname = posixpath.normpath('/' + member.filename)
138 while cutname.startswith('../'): 137 if cutname == '/':
139 cutname = cutname[3:]
140 if cutname in ('.', '..'):
141 continue 138 continue
142 if not member.filename.endswith('/'): 139 if not member.filename.endswith('/'):
143 files[cutname] = member 140 files[cutname] = member
144 cutname = posixpath.dirname(cutname) 141 cutname = posixpath.dirname(cutname)
145 while cutname: 142 while cutname != '/':
146 dirs.add(cutname) 143 dirs.add(cutname)
147 cutname = posixpath.dirname(cutname) 144 cutname = posixpath.dirname(cutname)
148 self._files = files 145 self._files = files
149 self._dirs = frozenset(dirs) 146 self._dirs = frozenset(dirs)
150 self._names = self._dirs | frozenset(files) 147 self._names = self._dirs | frozenset(files)
151 148
152 def extract(self, name, target): 149 def extract(self, name, target):
153 member = self._files[posixpath.normpath(name)] 150 member = self._files[posixpath.normpath('/' + name)]
154 # FIXME: 2.5 lacks ZipFile.extract 151 # FIXME: 2.5 lacks ZipFile.extract
155 if os.path.isabs(target): 152 if os.path.isabs(target):
156 # To my knowledge, this is as portable as it gets 153 # To my knowledge, this is as portable as it gets
157 path = os.path.join(os.path.splitdrive(target)[0], os.path.sep) 154 path = os.path.join(os.path.splitdrive(target)[0], os.path.sep)
158 member.filename = os.path.relpath(target, path) 155 member.filename = os.path.relpath(target, path)
160 else: 157 else:
161 member.filename = os.path.relpath(target) 158 member.filename = os.path.relpath(target)
162 self._zipfile.extract(member) 159 self._zipfile.extract(member)
163 160
164 def open(self, name): 161 def open(self, name):
165 name = posixpath.normpath(name) 162 name = posixpath.normpath('/' + name)
166 # FIXME: 2.5 lacks ZipFile.open 163 # FIXME: 2.5 lacks ZipFile.open
167 return self._zipfile.open(self._files[name]) 164 return self._zipfile.open(self._files[name])
168 165
169 def exists(self, name): 166 def exists(self, name):
170 return posixpath.normpath(name) in self._names 167 return posixpath.normpath('/' + name) in self._names
171 168
172 def listdir(self, name): 169 def listdir(self, name):
173 normname = posixpath.normpath(name) 170 normname = posixpath.normpath('/' + name)
174 if normname not in self._dirs: 171 if normname not in self._dirs:
175 raise KeyError('No such directory: %r' % name) 172 raise KeyError('No such directory: %r' % name)
176 normname += '/' 173 if normname != '/':
177 len_normname = len(normname) 174 normname += '/'
178 return [fname for fname in self._names 175 nnlen = len(normname)
179 if fname.startswith(normname) and 176 return [fname[nnlen:] for fname in self._names
180 fname.find('/', len_normname) == -1] 177 if fname.startswith(normname) and
178 fname.find('/', nnlen) == -1]
181 179
182 def __enter__(self): 180 def __enter__(self):
183 if hasattr(self._zipfile, '__enter__'): 181 if hasattr(self._zipfile, '__enter__'):
184 self._zipfile.__enter__() 182 self._zipfile.__enter__()
185 return self 183 return self
289 if self.archive: 287 if self.archive:
290 self.archive.extract(self.real_path, target) 288 self.archive.extract(self.real_path, target)
291 else: 289 else:
292 shutil.copy(self.real_path, target) 290 shutil.copy(self.real_path, target)
293 291
294 def regexp(pattern, hastests=False, droplast=False): 292 class RegexpMatchFile(object):
295 # FIXME: add test archives 293 __slots__ = 'virtual_path', 'real_path', 'hastests', 'archive'
294
295 def __init__(self, virtual_path, real_path, hastests=False, archive=None):
296 self.virtual_path = virtual_path
297 self.real_path = real_path
298 self.hastests = hastests
299 self.archive = archive
300
301 def regexp(pattern):
296 if not pattern: 302 if not pattern:
297 yield os.curdir, '' 303 yield RegexpMatchFile('', os.curdir)
298 return 304 return
299 dirname, basename = posixpath.split(pattern) 305 dirname, basename = posixpath.split(pattern)
300 if hastests: 306 dirs = regexp(dirname)
301 dirnames = regexp(dirname, True)
302 else:
303 dirnames = itertools.chain(regexp(dirname), regexp(posixpath.join(dirname, 'tests'), True, True))
304 reobj = re.compile(pattern + '$', re.UNICODE) 307 reobj = re.compile(pattern + '$', re.UNICODE)
305 for dirname, vdirname in dirnames: 308 while dirs:
306 try: 309 newdirs = []
307 names = os.listdir(dirname) 310 for directory in dirs:
308 except OSError: 311 if directory.archive:
309 continue 312 try:
310 for name in names: 313 names = directory.archive.listdir(directory.real_path)
311 path = os.path.join(dirname, name) 314 except KeyError:
312 vpath = posixpath.join(vdirname, name) 315 continue
313 if re.match(reobj, vpath): 316 join = posixpath.join
314 yield path, vpath if not droplast else vdirname 317 else:
318 try:
319 names = os.listdir(directory.real_path)
320 except OSError:
321 continue
322 join = posixpath.join
323 for name in names:
324 path = join(directory.real_path, name)
325 vpath = posixpath.join(directory.virtual_path, name)
326 if re.match(reobj, vpath):
327 yield RegexpMatchFile(vpath, path, directory.hastests, directory.archive)
328 if not directory.hastests:
329 if name == 'tests':
330 newdirs.append(RegexpMatchFile(directory.virtual_path, path, True, directory.archive))
331 if not directory.archive and name in archives:
332 newdirs.append(RegexpMatchFile(directory.virtual_path, '', False, open_archive(path)))
333 dirs = newdirs