blob: 2e5bfac55b12f6d3298a3d61b34c261a0f4341fa [file] [log] [blame]
Fred Drake484d7352000-10-02 21:14:52 +00001"Read and write ZIP files."
Guido van Rossum32abe6f2000-03-31 17:30:02 +00002# Written by James C. Ahlstrom jim@interet.com
3# All rights transferred to CNRI pursuant to the Python contribution agreement
4
5import struct, os, time
Fred Drake484d7352000-10-02 21:14:52 +00006import binascii
Guido van Rossum32abe6f2000-03-31 17:30:02 +00007
8try:
Tim Peterse1190062001-01-15 03:34:38 +00009 import zlib # We may need its compression method
Guido van Rossum32abe6f2000-03-31 17:30:02 +000010except:
11 zlib = None
12
Fred Drake5db246d2000-09-29 20:44:48 +000013class BadZipfile(Exception):
Guido van Rossum32abe6f2000-03-31 17:30:02 +000014 pass
Tim Peterse1190062001-01-15 03:34:38 +000015error = BadZipfile # The exception raised by this module
Guido van Rossum32abe6f2000-03-31 17:30:02 +000016
17# constants for Zip file compression methods
18ZIP_STORED = 0
19ZIP_DEFLATED = 8
20# Other ZIP compression methods not supported
21
22# Here are some struct module formats for reading headers
23structEndArchive = "<4s4H2lH" # 9 items, end of archive, 22 bytes
24stringEndArchive = "PK\005\006" # magic number for end of archive record
25structCentralDir = "<4s4B4H3l5H2l"# 19 items, central directory, 46 bytes
26stringCentralDir = "PK\001\002" # magic number for central directory
27structFileHeader = "<4s2B4H3l2H" # 12 items, file header record, 30 bytes
28stringFileHeader = "PK\003\004" # magic number for file header
29
Fred Drake484d7352000-10-02 21:14:52 +000030
Guido van Rossum32abe6f2000-03-31 17:30:02 +000031def is_zipfile(filename):
32 """Quickly see if file is a ZIP file by checking the magic number.
33
Fred Drake484d7352000-10-02 21:14:52 +000034 Will not accept a ZIP archive with an ending comment.
35 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +000036 try:
37 fpin = open(filename, "rb")
Tim Peterse1190062001-01-15 03:34:38 +000038 fpin.seek(-22, 2) # Seek to end-of-file record
Guido van Rossum32abe6f2000-03-31 17:30:02 +000039 endrec = fpin.read()
40 fpin.close()
41 if endrec[0:4] == "PK\005\006" and endrec[-2:] == "\000\000":
Tim Peterse1190062001-01-15 03:34:38 +000042 return 1 # file has correct magic number
Guido van Rossum32abe6f2000-03-31 17:30:02 +000043 except:
44 pass
45
Fred Drake484d7352000-10-02 21:14:52 +000046
Guido van Rossum32abe6f2000-03-31 17:30:02 +000047class ZipInfo:
Fred Drake484d7352000-10-02 21:14:52 +000048 """Class with attributes describing each file in the ZIP archive."""
49
Guido van Rossum32abe6f2000-03-31 17:30:02 +000050 def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
Tim Peterse1190062001-01-15 03:34:38 +000051 self.filename = filename # Name of the file in the archive
52 self.date_time = date_time # year, month, day, hour, min, sec
Guido van Rossum32abe6f2000-03-31 17:30:02 +000053 # Standard values:
Tim Peterse1190062001-01-15 03:34:38 +000054 self.compress_type = ZIP_STORED # Type of compression for the file
55 self.comment = "" # Comment for each file
56 self.extra = "" # ZIP extra data
57 self.create_system = 0 # System which created ZIP archive
58 self.create_version = 20 # Version which created ZIP archive
59 self.extract_version = 20 # Version needed to extract archive
60 self.reserved = 0 # Must be zero
61 self.flag_bits = 0 # ZIP flag bits
62 self.volume = 0 # Volume number of file header
63 self.internal_attr = 0 # Internal attributes
64 self.external_attr = 0 # External file attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +000065 # Other attributes are set by class ZipFile:
Tim Peterse1190062001-01-15 03:34:38 +000066 # header_offset Byte offset to the file header
67 # file_offset Byte offset to the start of the file data
68 # CRC CRC-32 of the uncompressed file
69 # compress_size Size of the compressed file
70 # file_size Size of the uncompressed file
Guido van Rossum32abe6f2000-03-31 17:30:02 +000071
72 def FileHeader(self):
Fred Drake484d7352000-10-02 21:14:52 +000073 """Return the per-file header as a string."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +000074 dt = self.date_time
75 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
76 dostime = dt[3] << 11 | dt[4] << 5 | dt[5] / 2
77 if self.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +000078 # Set these to zero because we write them after the file data
79 CRC = compress_size = file_size = 0
Guido van Rossum32abe6f2000-03-31 17:30:02 +000080 else:
Tim Peterse1190062001-01-15 03:34:38 +000081 CRC = self.CRC
82 compress_size = self.compress_size
83 file_size = self.file_size
Guido van Rossum32abe6f2000-03-31 17:30:02 +000084 header = struct.pack(structFileHeader, stringFileHeader,
85 self.extract_version, self.reserved, self.flag_bits,
86 self.compress_type, dostime, dosdate, CRC,
87 compress_size, file_size,
88 len(self.filename), len(self.extra))
89 return header + self.filename + self.extra
90
91
92class ZipFile:
Fred Drake484d7352000-10-02 21:14:52 +000093 """Class with methods to open, read, write, close, list zip files."""
94
Fred Drake90eac282001-02-28 05:29:34 +000095 fp = None # Set here since __del__ checks it
96
Guido van Rossum32abe6f2000-03-31 17:30:02 +000097 def __init__(self, filename, mode="r", compression=ZIP_STORED):
Fred Drake484d7352000-10-02 21:14:52 +000098 """Open the ZIP file with mode read "r", write "w" or append "a"."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +000099 if compression == ZIP_STORED:
100 pass
101 elif compression == ZIP_DEFLATED:
102 if not zlib:
103 raise RuntimeError,\
Fred Drake5db246d2000-09-29 20:44:48 +0000104 "Compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000105 else:
106 raise RuntimeError, "That compression method is not supported"
Tim Peterse1190062001-01-15 03:34:38 +0000107 self.debug = 0 # Level of printing: 0 through 3
108 self.NameToInfo = {} # Find file info given name
109 self.filelist = [] # List of ZipInfo instances for archive
110 self.compression = compression # Method of compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000111 self.filename = filename
112 self.mode = key = mode[0]
113 if key == 'r':
114 self.fp = open(filename, "rb")
115 self._GetContents()
116 elif key == 'w':
117 self.fp = open(filename, "wb")
118 elif key == 'a':
119 fp = self.fp = open(filename, "r+b")
Tim Peterse1190062001-01-15 03:34:38 +0000120 fp.seek(-22, 2) # Seek to end-of-file record
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000121 endrec = fp.read()
122 if endrec[0:4] == stringEndArchive and \
123 endrec[-2:] == "\000\000":
Tim Peterse1190062001-01-15 03:34:38 +0000124 self._GetContents() # file is a zip file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000125 # seek to start of directory and overwrite
126 fp.seek(self.start_dir, 0)
Tim Peterse1190062001-01-15 03:34:38 +0000127 else: # file is not a zip file, just append
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000128 fp.seek(0, 2)
129 else:
130 raise RuntimeError, 'Mode must be "r", "w" or "a"'
131
132 def _GetContents(self):
Fred Drake484d7352000-10-02 21:14:52 +0000133 """Read in the table of contents for the ZIP file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000134 fp = self.fp
Tim Peterse1190062001-01-15 03:34:38 +0000135 fp.seek(-22, 2) # Start of end-of-archive record
136 filesize = fp.tell() + 22 # Get file size
137 endrec = fp.read(22) # Archive must not end with a comment!
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000138 if endrec[0:4] != stringEndArchive or endrec[-2:] != "\000\000":
139 raise BadZipfile, "File is not a zip file, or ends with a comment"
140 endrec = struct.unpack(structEndArchive, endrec)
141 if self.debug > 1:
142 print endrec
Tim Peterse1190062001-01-15 03:34:38 +0000143 size_cd = endrec[5] # bytes in central directory
144 offset_cd = endrec[6] # offset of central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000145 x = filesize - 22 - size_cd
146 # "concat" is zero, unless zip was concatenated to another file
147 concat = x - offset_cd
148 if self.debug > 2:
149 print "given, inferred, offset", offset_cd, x, concat
150 # self.start_dir: Position of start of central directory
151 self.start_dir = offset_cd + concat
152 fp.seek(self.start_dir, 0)
153 total = 0
154 while total < size_cd:
155 centdir = fp.read(46)
156 total = total + 46
157 if centdir[0:4] != stringCentralDir:
158 raise BadZipfile, "Bad magic number for central directory"
159 centdir = struct.unpack(structCentralDir, centdir)
160 if self.debug > 2:
161 print centdir
162 filename = fp.read(centdir[12])
163 # Create ZipInfo instance to store file information
164 x = ZipInfo(filename)
165 x.extra = fp.read(centdir[13])
166 x.comment = fp.read(centdir[14])
167 total = total + centdir[12] + centdir[13] + centdir[14]
168 x.header_offset = centdir[18] + concat
169 x.file_offset = x.header_offset + 30 + centdir[12] + centdir[13]
170 (x.create_version, x.create_system, x.extract_version, x.reserved,
171 x.flag_bits, x.compress_type, t, d,
172 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
173 x.volume, x.internal_attr, x.external_attr = centdir[15:18]
174 # Convert date/time code to (year, month, day, hour, min, sec)
175 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
Fred Drake414ca662000-06-13 18:49:53 +0000176 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000177 self.filelist.append(x)
178 self.NameToInfo[x.filename] = x
179 if self.debug > 2:
180 print "total", total
181 for data in self.filelist:
182 fp.seek(data.header_offset, 0)
183 fheader = fp.read(30)
184 if fheader[0:4] != stringFileHeader:
185 raise BadZipfile, "Bad magic number for file header"
186 fheader = struct.unpack(structFileHeader, fheader)
187 fname = fp.read(fheader[10])
188 if fname != data.filename:
189 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000190 'File name in directory "%s" and header "%s" differ.' % (
191 data.filename, fname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000192
193 def namelist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000194 """Return a list of file names in the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000195 l = []
196 for data in self.filelist:
197 l.append(data.filename)
198 return l
199
200 def infolist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000201 """Return a list of class ZipInfo instances for files in the
202 archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000203 return self.filelist
204
205 def printdir(self):
Fred Drake484d7352000-10-02 21:14:52 +0000206 """Print a table of contents for the zip file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000207 print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
208 for zinfo in self.filelist:
209 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
210 print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
211
212 def testzip(self):
Fred Drake484d7352000-10-02 21:14:52 +0000213 """Read all the files and check the CRC."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000214 for zinfo in self.filelist:
215 try:
Tim Peterse1190062001-01-15 03:34:38 +0000216 self.read(zinfo.filename) # Check CRC-32
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000217 except:
218 return zinfo.filename
219
220 def getinfo(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000221 """Return the instance of ZipInfo given 'name'."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000222 return self.NameToInfo[name]
223
224 def read(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000225 """Return file bytes (as a string) for name."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000226 if self.mode not in ("r", "a"):
227 raise RuntimeError, 'read() requires mode "r" or "a"'
228 if not self.fp:
229 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000230 "Attempt to read ZIP archive that was already closed"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000231 zinfo = self.getinfo(name)
232 filepos = self.fp.tell()
233 self.fp.seek(zinfo.file_offset, 0)
234 bytes = self.fp.read(zinfo.compress_size)
235 self.fp.seek(filepos, 0)
236 if zinfo.compress_type == ZIP_STORED:
237 pass
238 elif zinfo.compress_type == ZIP_DEFLATED:
239 if not zlib:
240 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000241 "De-compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000242 # zlib compress/decompress code by Jeremy Hylton of CNRI
243 dc = zlib.decompressobj(-15)
244 bytes = dc.decompress(bytes)
245 # need to feed in unused pad byte so that zlib won't choke
246 ex = dc.decompress('Z') + dc.flush()
247 if ex:
248 bytes = bytes + ex
249 else:
250 raise BadZipfile, \
Fred Drake5db246d2000-09-29 20:44:48 +0000251 "Unsupported compression method %d for file %s" % \
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000252 (zinfo.compress_type, name)
253 crc = binascii.crc32(bytes)
254 if crc != zinfo.CRC:
255 raise BadZipfile, "Bad CRC-32 for file %s" % name
256 return bytes
257
258 def _writecheck(self, zinfo):
Fred Drake484d7352000-10-02 21:14:52 +0000259 """Check for errors before writing a file to the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000260 if self.NameToInfo.has_key(zinfo.filename):
Tim Peterse1190062001-01-15 03:34:38 +0000261 if self.debug: # Warning for duplicate names
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000262 print "Duplicate name:", zinfo.filename
263 if self.mode not in ("w", "a"):
264 raise RuntimeError, 'write() requires mode "w" or "a"'
265 if not self.fp:
266 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000267 "Attempt to write ZIP archive that was already closed"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000268 if zinfo.compress_type == ZIP_DEFLATED and not zlib:
269 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000270 "Compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000271 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
272 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000273 "That compression method is not supported"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000274
275 def write(self, filename, arcname=None, compress_type=None):
Fred Drake484d7352000-10-02 21:14:52 +0000276 """Put the bytes from filename into the archive under the name
277 arcname."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000278 st = os.stat(filename)
279 mtime = time.localtime(st[8])
280 date_time = mtime[0:6]
281 # Create ZipInfo instance to store file information
282 if arcname is None:
Tim Peterse1190062001-01-15 03:34:38 +0000283 zinfo = ZipInfo(filename, date_time)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000284 else:
Tim Peterse1190062001-01-15 03:34:38 +0000285 zinfo = ZipInfo(arcname, date_time)
286 zinfo.external_attr = st[0] << 16 # Unix attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000287 if compress_type is None:
Tim Peterse1190062001-01-15 03:34:38 +0000288 zinfo.compress_type = self.compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000289 else:
Tim Peterse1190062001-01-15 03:34:38 +0000290 zinfo.compress_type = compress_type
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000291 self._writecheck(zinfo)
292 fp = open(filename, "rb")
293 zinfo.flag_bits = 0x08
Tim Peterse1190062001-01-15 03:34:38 +0000294 zinfo.header_offset = self.fp.tell() # Start of header bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000295 self.fp.write(zinfo.FileHeader())
Tim Peterse1190062001-01-15 03:34:38 +0000296 zinfo.file_offset = self.fp.tell() # Start of file bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000297 CRC = 0
298 compress_size = 0
299 file_size = 0
300 if zinfo.compress_type == ZIP_DEFLATED:
301 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
302 zlib.DEFLATED, -15)
303 else:
304 cmpr = None
305 while 1:
306 buf = fp.read(1024 * 8)
307 if not buf:
308 break
309 file_size = file_size + len(buf)
310 CRC = binascii.crc32(buf, CRC)
311 if cmpr:
312 buf = cmpr.compress(buf)
313 compress_size = compress_size + len(buf)
314 self.fp.write(buf)
315 fp.close()
316 if cmpr:
317 buf = cmpr.flush()
318 compress_size = compress_size + len(buf)
319 self.fp.write(buf)
320 zinfo.compress_size = compress_size
321 else:
322 zinfo.compress_size = file_size
323 zinfo.CRC = CRC
324 zinfo.file_size = file_size
325 # Write CRC and file sizes after the file data
326 self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
327 zinfo.file_size))
328 self.filelist.append(zinfo)
329 self.NameToInfo[zinfo.filename] = zinfo
330
331 def writestr(self, zinfo, bytes):
Fred Drake484d7352000-10-02 21:14:52 +0000332 """Write a file into the archive. The contents is the string
333 'bytes'."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000334 self._writecheck(zinfo)
Tim Peterse1190062001-01-15 03:34:38 +0000335 zinfo.file_size = len(bytes) # Uncompressed size
336 zinfo.CRC = binascii.crc32(bytes) # CRC-32 checksum
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000337 if zinfo.compress_type == ZIP_DEFLATED:
338 co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
339 zlib.DEFLATED, -15)
340 bytes = co.compress(bytes) + co.flush()
Tim Peterse1190062001-01-15 03:34:38 +0000341 zinfo.compress_size = len(bytes) # Compressed size
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000342 else:
343 zinfo.compress_size = zinfo.file_size
Tim Peterse1190062001-01-15 03:34:38 +0000344 zinfo.header_offset = self.fp.tell() # Start of header bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000345 self.fp.write(zinfo.FileHeader())
Tim Peterse1190062001-01-15 03:34:38 +0000346 zinfo.file_offset = self.fp.tell() # Start of file bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000347 self.fp.write(bytes)
348 if zinfo.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +0000349 # Write CRC and file sizes after the file data
350 self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
351 zinfo.file_size))
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000352 self.filelist.append(zinfo)
353 self.NameToInfo[zinfo.filename] = zinfo
354
355 def __del__(self):
Fred Drake484d7352000-10-02 21:14:52 +0000356 """Call the "close()" method in case the user forgot."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000357 if self.fp:
358 self.fp.close()
359 self.fp = None
360
361 def close(self):
Fred Drake484d7352000-10-02 21:14:52 +0000362 """Close the file, and for mode "w" and "a" write the ending
363 records."""
Tim Peterse1190062001-01-15 03:34:38 +0000364 if self.mode in ("w", "a"): # write ending records
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000365 count = 0
366 pos1 = self.fp.tell()
Tim Peterse1190062001-01-15 03:34:38 +0000367 for zinfo in self.filelist: # write central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000368 count = count + 1
369 dt = zinfo.date_time
370 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
371 dostime = dt[3] << 11 | dt[4] << 5 | dt[5] / 2
372 centdir = struct.pack(structCentralDir,
373 stringCentralDir, zinfo.create_version,
374 zinfo.create_system, zinfo.extract_version, zinfo.reserved,
375 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
376 zinfo.CRC, zinfo.compress_size, zinfo.file_size,
377 len(zinfo.filename), len(zinfo.extra), len(zinfo.comment),
378 0, zinfo.internal_attr, zinfo.external_attr,
379 zinfo.header_offset)
380 self.fp.write(centdir)
381 self.fp.write(zinfo.filename)
382 self.fp.write(zinfo.extra)
383 self.fp.write(zinfo.comment)
384 pos2 = self.fp.tell()
385 # Write end-of-zip-archive record
386 endrec = struct.pack(structEndArchive, stringEndArchive,
387 0, 0, count, count, pos2 - pos1, pos1, 0)
388 self.fp.write(endrec)
389 self.fp.close()
390 self.fp = None
391
392
393class PyZipFile(ZipFile):
Fred Drake484d7352000-10-02 21:14:52 +0000394 """Class to create ZIP archives with Python library files and packages."""
395
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000396 def writepy(self, pathname, basename = ""):
397 """Add all files from "pathname" to the ZIP archive.
398
Fred Drake484d7352000-10-02 21:14:52 +0000399 If pathname is a package directory, search the directory and
400 all package subdirectories recursively for all *.py and enter
401 the modules into the archive. If pathname is a plain
402 directory, listdir *.py and enter all modules. Else, pathname
403 must be a Python *.py file and the module will be put into the
404 archive. Added modules are always module.pyo or module.pyc.
405 This method will compile the module.py into module.pyc if
406 necessary.
407 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000408 dir, name = os.path.split(pathname)
409 if os.path.isdir(pathname):
410 initname = os.path.join(pathname, "__init__.py")
411 if os.path.isfile(initname):
412 # This is a package directory, add it
413 if basename:
414 basename = "%s/%s" % (basename, name)
415 else:
416 basename = name
417 if self.debug:
418 print "Adding package in", pathname, "as", basename
419 fname, arcname = self._get_codename(initname[0:-3], basename)
420 if self.debug:
421 print "Adding", arcname
422 self.write(fname, arcname)
423 dirlist = os.listdir(pathname)
424 dirlist.remove("__init__.py")
425 # Add all *.py files and package subdirectories
426 for filename in dirlist:
427 path = os.path.join(pathname, filename)
428 root, ext = os.path.splitext(filename)
429 if os.path.isdir(path):
430 if os.path.isfile(os.path.join(path, "__init__.py")):
431 # This is a package directory, add it
432 self.writepy(path, basename) # Recursive call
433 elif ext == ".py":
434 fname, arcname = self._get_codename(path[0:-3],
435 basename)
436 if self.debug:
437 print "Adding", arcname
438 self.write(fname, arcname)
439 else:
440 # This is NOT a package directory, add its files at top level
441 if self.debug:
442 print "Adding files from directory", pathname
443 for filename in os.listdir(pathname):
444 path = os.path.join(pathname, filename)
445 root, ext = os.path.splitext(filename)
446 if ext == ".py":
447 fname, arcname = self._get_codename(path[0:-3],
448 basename)
449 if self.debug:
450 print "Adding", arcname
451 self.write(fname, arcname)
452 else:
453 if pathname[-3:] != ".py":
454 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000455 'Files added with writepy() must end with ".py"'
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000456 fname, arcname = self._get_codename(pathname[0:-3], basename)
457 if self.debug:
458 print "Adding file", arcname
459 self.write(fname, arcname)
460
461 def _get_codename(self, pathname, basename):
462 """Return (filename, archivename) for the path.
463
Fred Drake484d7352000-10-02 21:14:52 +0000464 Given a module name path, return the correct file path and
465 archive name, compiling if necessary. For example, given
466 /python/lib/string, return (/python/lib/string.pyc, string).
467 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000468 file_py = pathname + ".py"
469 file_pyc = pathname + ".pyc"
470 file_pyo = pathname + ".pyo"
471 if os.path.isfile(file_pyo) and \
472 os.stat(file_pyo)[8] >= os.stat(file_py)[8]:
Tim Peterse1190062001-01-15 03:34:38 +0000473 fname = file_pyo # Use .pyo file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000474 elif not os.path.isfile(file_pyc) or \
Fred Drake484d7352000-10-02 21:14:52 +0000475 os.stat(file_pyc)[8] < os.stat(file_py)[8]:
476 import py_compile
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000477 if self.debug:
478 print "Compiling", file_py
479 py_compile.compile(file_py, file_pyc)
480 fname = file_pyc
481 else:
482 fname = file_pyc
483 archivename = os.path.split(fname)[1]
484 if basename:
485 archivename = "%s/%s" % (basename, archivename)
486 return (fname, archivename)