zipio.py
9.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
"""
A helper module that can work with paths
that can refer to data inside a zipfile
XXX: Need to determine if isdir("zipfile.zip")
should return True or False. Currently returns
True, but that might do the wrong thing with
data-files that are zipfiles.
"""
import os as _os
import zipfile as _zipfile
import errno as _errno
import time as _time
import sys as _sys
import stat as _stat
_DFLT_DIR_MODE = (
_stat.S_IXOTH
| _stat.S_IXGRP
| _stat.S_IXUSR
| _stat.S_IROTH
| _stat.S_IRGRP
| _stat.S_IRUSR)
_DFLT_FILE_MODE = (
_stat.S_IROTH
| _stat.S_IRGRP
| _stat.S_IRUSR)
if _sys.version_info[0] == 2:
from StringIO import StringIO as _BaseStringIO
from StringIO import StringIO as _BaseBytesIO
class _StringIO (_BaseStringIO):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
return False
class _BytesIO (_BaseBytesIO):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
return False
else:
from io import StringIO as _StringIO
from io import BytesIO as _BytesIO
def _locate(path):
full_path = path
if _os.path.exists(path):
return path, None
else:
rest = []
root = _os.path.splitdrive(path)
while path and path != root:
path, bn = _os.path.split(path)
rest.append(bn)
if _os.path.exists(path):
break
if path == root:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
if not _os.path.isfile(path):
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
rest.reverse()
return path, '/'.join(rest).strip('/')
_open = open
def open(path, mode='r'):
if 'w' in mode or 'a' in mode:
raise IOError(
_errno.EINVAL, path, "Write access not supported")
elif 'r+' in mode:
raise IOError(
_errno.EINVAL, path, "Write access not supported")
full_path = path
path, rest = _locate(path)
if not rest:
return _open(path, mode)
else:
try:
zf = _zipfile.ZipFile(path, 'r')
except _zipfile.error:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
try:
data = zf.read(rest)
except (_zipfile.error, KeyError):
zf.close()
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
zf.close()
if mode == 'rb':
return _BytesIO(data)
else:
if _sys.version_info[0] == 3:
data = data.decode('ascii')
return _StringIO(data)
def listdir(path):
full_path = path
path, rest = _locate(path)
if not rest and not _os.path.isfile(path):
return _os.listdir(path)
else:
try:
zf = _zipfile.ZipFile(path, 'r')
except _zipfile.error:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
result = set()
seen = False
try:
for nm in zf.namelist():
if rest is None:
seen = True
value = nm.split('/')[0]
if value:
result.add(value)
elif nm.startswith(rest):
if nm == rest:
seen = True
value = ''
pass
elif nm[len(rest)] == '/':
seen = True
value = nm[len(rest)+1:].split('/')[0]
else:
value = None
if value:
result.add(value)
except _zipfile.error:
zf.close()
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
zf.close()
if not seen:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
return list(result)
def isfile(path):
full_path = path
path, rest = _locate(path)
if not rest:
ok = _os.path.isfile(path)
if ok:
try:
zf = _zipfile.ZipFile(path, 'r')
return False
except (_zipfile.error, IOError):
return True
return False
zf = None
try:
zf = _zipfile.ZipFile(path, 'r')
zf.getinfo(rest)
zf.close()
return True
except (KeyError, _zipfile.error):
if zf is not None:
zf.close()
# Check if this is a directory
try:
zf.getinfo(rest + '/')
except KeyError:
pass
else:
return False
rest = rest + '/'
for nm in zf.namelist():
if nm.startswith(rest):
# Directory
return False
# No trace in zipfile
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
def isdir(path):
full_path = path
path, rest = _locate(path)
if not rest:
ok = _os.path.isdir(path)
if not ok:
try:
zf = _zipfile.ZipFile(path, 'r')
except (_zipfile.error, IOError):
return False
return True
return True
zf = None
try:
try:
zf = _zipfile.ZipFile(path)
except _zipfile.error:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
try:
zf.getinfo(rest)
except KeyError:
pass
else:
# File found
return False
rest = rest + '/'
try:
zf.getinfo(rest)
except KeyError:
pass
else:
# Directory entry found
return True
for nm in zf.namelist():
if nm.startswith(rest):
return True
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
finally:
if zf is not None:
zf.close()
def islink(path):
full_path = path
path, rest = _locate(path)
if not rest:
return _os.path.islink(path)
try:
zf = _zipfile.ZipFile(path)
except _zipfile.error:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
try:
try:
zf.getinfo(rest)
except KeyError:
pass
else:
# File
return False
rest += '/'
try:
zf.getinfo(rest)
except KeyError:
pass
else:
# Directory
return False
for nm in zf.namelist():
if nm.startswith(rest):
# Directory without listing
return False
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
finally:
zf.close()
def readlink(path):
full_path = path
path, rest = _locate(path)
if rest:
# No symlinks inside zipfiles
raise OSError(
_errno.ENOENT, full_path,
"No such file or directory")
return _os.readlink(path)
def getmode(path):
full_path = path
path, rest = _locate(path)
if not rest:
return _stat.S_IMODE(_os.stat(path).st_mode)
zf = None
try:
zf = _zipfile.ZipFile(path)
info = None
try:
info = zf.getinfo(rest)
except KeyError:
pass
if info is None:
try:
info = zf.getinfo(rest + '/')
except KeyError:
pass
if info is None:
rest = rest + '/'
for nm in zf.namelist():
if nm.startswith(rest):
break
else:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
# Directory exists, but has no entry of its own.
return _DFLT_DIR_MODE
# The mode is stored without file-type in external_attr.
if (info.external_attr >> 16) != 0:
return _stat.S_IMODE(info.external_attr >> 16)
else:
return _DFLT_FILE_MODE
finally:
if zf is not None:
zf.close()
def getmtime(path):
full_path = path
path, rest = _locate(path)
if not rest:
return _os.path.getmtime(path)
zf = None
try:
zf = _zipfile.ZipFile(path)
info = None
try:
info = zf.getinfo(rest)
except KeyError:
pass
if info is None:
try:
info = zf.getinfo(rest + '/')
except KeyError:
pass
if info is None:
rest = rest + '/'
for nm in zf.namelist():
if nm.startswith(rest):
break
else:
raise IOError(
_errno.ENOENT, full_path,
"No such file or directory")
# Directory exists, but has no entry of its
# own, fake mtime by using the timestamp of
# the zipfile itself.
return _os.path.getmtime(path)
return _time.mktime(info.date_time + (0, 0, -1))
finally:
if zf is not None:
zf.close()