Mercurial > ~astiob > upreckon > hgweb
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 |