blob: 1c2b0dee7f86695d4d82e4685ae10e53834f2175 [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
Guido van Rossum32abe6f2000-03-31 17:30:02 +000095 def __init__(self, filename, mode="r", compression=ZIP_STORED):
Fred Drake484d7352000-10-02 21:14:52 +000096 """Open the ZIP file with mode read "r", write "w" or append "a"."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +000097 if compression == ZIP_STORED:
98 pass
99 elif compression == ZIP_DEFLATED:
100 if not zlib:
101 raise RuntimeError,\
Fred Drake5db246d2000-09-29 20:44:48 +0000102 "Compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000103 else:
104 raise RuntimeError, "That compression method is not supported"
Tim Peterse1190062001-01-15 03:34:38 +0000105 self.debug = 0 # Level of printing: 0 through 3
106 self.NameToInfo = {} # Find file info given name
107 self.filelist = [] # List of ZipInfo instances for archive
108 self.compression = compression # Method of compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000109 self.filename = filename
110 self.mode = key = mode[0]
111 if key == 'r':
112 self.fp = open(filename, "rb")
113 self._GetContents()
114 elif key == 'w':
115 self.fp = open(filename, "wb")
116 elif key == 'a':
117 fp = self.fp = open(filename, "r+b")
Tim Peterse1190062001-01-15 03:34:38 +0000118 fp.seek(-22, 2) # Seek to end-of-file record
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000119 endrec = fp.read()
120 if endrec[0:4] == stringEndArchive and \
121 endrec[-2:] == "\000\000":
Tim Peterse1190062001-01-15 03:34:38 +0000122 self._GetContents() # file is a zip file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000123 # seek to start of directory and overwrite
124 fp.seek(self.start_dir, 0)
Tim Peterse1190062001-01-15 03:34:38 +0000125 else: # file is not a zip file, just append
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000126 fp.seek(0, 2)
127 else:
128 raise RuntimeError, 'Mode must be "r", "w" or "a"'
129
130 def _GetContents(self):
Fred Drake484d7352000-10-02 21:14:52 +0000131 """Read in the table of contents for the ZIP file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000132 fp = self.fp
Tim Peterse1190062001-01-15 03:34:38 +0000133 fp.seek(-22, 2) # Start of end-of-archive record
134 filesize = fp.tell() + 22 # Get file size
135 endrec = fp.read(22) # Archive must not end with a comment!
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000136 if endrec[0:4] != stringEndArchive or endrec[-2:] != "\000\000":
137 raise BadZipfile, "File is not a zip file, or ends with a comment"
138 endrec = struct.unpack(structEndArchive, endrec)
139 if self.debug > 1:
140 print endrec
Tim Peterse1190062001-01-15 03:34:38 +0000141 size_cd = endrec[5] # bytes in central directory
142 offset_cd = endrec[6] # offset of central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000143 x = filesize - 22 - size_cd
144 # "concat" is zero, unless zip was concatenated to another file
145 concat = x - offset_cd
146 if self.debug > 2:
147 print "given, inferred, offset", offset_cd, x, concat
148 # self.start_dir: Position of start of central directory
149 self.start_dir = offset_cd + concat
150 fp.seek(self.start_dir, 0)
151 total = 0
152 while total < size_cd:
153 centdir = fp.read(46)
154 total = total + 46
155 if centdir[0:4] != stringCentralDir:
156 raise BadZipfile, "Bad magic number for central directory"
157 centdir = struct.unpack(structCentralDir, centdir)
158 if self.debug > 2:
159 print centdir
160 filename = fp.read(centdir[12])
161 # Create ZipInfo instance to store file information
162 x = ZipInfo(filename)
163 x.extra = fp.read(centdir[13])
164 x.comment = fp.read(centdir[14])
165 total = total + centdir[12] + centdir[13] + centdir[14]
166 x.header_offset = centdir[18] + concat
167 x.file_offset = x.header_offset + 30 + centdir[12] + centdir[13]
168 (x.create_version, x.create_system, x.extract_version, x.reserved,
169 x.flag_bits, x.compress_type, t, d,
170 x.CRC, x.compress_size, x.file_size) = centdir[1:12]
171 x.volume, x.internal_attr, x.external_attr = centdir[15:18]
172 # Convert date/time code to (year, month, day, hour, min, sec)
173 x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
Fred Drake414ca662000-06-13 18:49:53 +0000174 t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000175 self.filelist.append(x)
176 self.NameToInfo[x.filename] = x
177 if self.debug > 2:
178 print "total", total
179 for data in self.filelist:
180 fp.seek(data.header_offset, 0)
181 fheader = fp.read(30)
182 if fheader[0:4] != stringFileHeader:
183 raise BadZipfile, "Bad magic number for file header"
184 fheader = struct.unpack(structFileHeader, fheader)
185 fname = fp.read(fheader[10])
186 if fname != data.filename:
187 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000188 'File name in directory "%s" and header "%s" differ.' % (
189 data.filename, fname)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000190
191 def namelist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000192 """Return a list of file names in the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000193 l = []
194 for data in self.filelist:
195 l.append(data.filename)
196 return l
197
198 def infolist(self):
Fred Drake484d7352000-10-02 21:14:52 +0000199 """Return a list of class ZipInfo instances for files in the
200 archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000201 return self.filelist
202
203 def printdir(self):
Fred Drake484d7352000-10-02 21:14:52 +0000204 """Print a table of contents for the zip file."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000205 print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
206 for zinfo in self.filelist:
207 date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
208 print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
209
210 def testzip(self):
Fred Drake484d7352000-10-02 21:14:52 +0000211 """Read all the files and check the CRC."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000212 for zinfo in self.filelist:
213 try:
Tim Peterse1190062001-01-15 03:34:38 +0000214 self.read(zinfo.filename) # Check CRC-32
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000215 except:
216 return zinfo.filename
217
218 def getinfo(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000219 """Return the instance of ZipInfo given 'name'."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000220 return self.NameToInfo[name]
221
222 def read(self, name):
Fred Drake484d7352000-10-02 21:14:52 +0000223 """Return file bytes (as a string) for name."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000224 if self.mode not in ("r", "a"):
225 raise RuntimeError, 'read() requires mode "r" or "a"'
226 if not self.fp:
227 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000228 "Attempt to read ZIP archive that was already closed"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000229 zinfo = self.getinfo(name)
230 filepos = self.fp.tell()
231 self.fp.seek(zinfo.file_offset, 0)
232 bytes = self.fp.read(zinfo.compress_size)
233 self.fp.seek(filepos, 0)
234 if zinfo.compress_type == ZIP_STORED:
235 pass
236 elif zinfo.compress_type == ZIP_DEFLATED:
237 if not zlib:
238 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000239 "De-compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000240 # zlib compress/decompress code by Jeremy Hylton of CNRI
241 dc = zlib.decompressobj(-15)
242 bytes = dc.decompress(bytes)
243 # need to feed in unused pad byte so that zlib won't choke
244 ex = dc.decompress('Z') + dc.flush()
245 if ex:
246 bytes = bytes + ex
247 else:
248 raise BadZipfile, \
Fred Drake5db246d2000-09-29 20:44:48 +0000249 "Unsupported compression method %d for file %s" % \
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000250 (zinfo.compress_type, name)
251 crc = binascii.crc32(bytes)
252 if crc != zinfo.CRC:
253 raise BadZipfile, "Bad CRC-32 for file %s" % name
254 return bytes
255
256 def _writecheck(self, zinfo):
Fred Drake484d7352000-10-02 21:14:52 +0000257 """Check for errors before writing a file to the archive."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000258 if self.NameToInfo.has_key(zinfo.filename):
Tim Peterse1190062001-01-15 03:34:38 +0000259 if self.debug: # Warning for duplicate names
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000260 print "Duplicate name:", zinfo.filename
261 if self.mode not in ("w", "a"):
262 raise RuntimeError, 'write() requires mode "w" or "a"'
263 if not self.fp:
264 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000265 "Attempt to write ZIP archive that was already closed"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000266 if zinfo.compress_type == ZIP_DEFLATED and not zlib:
267 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000268 "Compression requires the (missing) zlib module"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000269 if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
270 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000271 "That compression method is not supported"
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000272
273 def write(self, filename, arcname=None, compress_type=None):
Fred Drake484d7352000-10-02 21:14:52 +0000274 """Put the bytes from filename into the archive under the name
275 arcname."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000276 st = os.stat(filename)
277 mtime = time.localtime(st[8])
278 date_time = mtime[0:6]
279 # Create ZipInfo instance to store file information
280 if arcname is None:
Tim Peterse1190062001-01-15 03:34:38 +0000281 zinfo = ZipInfo(filename, date_time)
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000282 else:
Tim Peterse1190062001-01-15 03:34:38 +0000283 zinfo = ZipInfo(arcname, date_time)
284 zinfo.external_attr = st[0] << 16 # Unix attributes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000285 if compress_type is None:
Tim Peterse1190062001-01-15 03:34:38 +0000286 zinfo.compress_type = self.compression
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000287 else:
Tim Peterse1190062001-01-15 03:34:38 +0000288 zinfo.compress_type = compress_type
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000289 self._writecheck(zinfo)
290 fp = open(filename, "rb")
291 zinfo.flag_bits = 0x08
Tim Peterse1190062001-01-15 03:34:38 +0000292 zinfo.header_offset = self.fp.tell() # Start of header bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000293 self.fp.write(zinfo.FileHeader())
Tim Peterse1190062001-01-15 03:34:38 +0000294 zinfo.file_offset = self.fp.tell() # Start of file bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000295 CRC = 0
296 compress_size = 0
297 file_size = 0
298 if zinfo.compress_type == ZIP_DEFLATED:
299 cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
300 zlib.DEFLATED, -15)
301 else:
302 cmpr = None
303 while 1:
304 buf = fp.read(1024 * 8)
305 if not buf:
306 break
307 file_size = file_size + len(buf)
308 CRC = binascii.crc32(buf, CRC)
309 if cmpr:
310 buf = cmpr.compress(buf)
311 compress_size = compress_size + len(buf)
312 self.fp.write(buf)
313 fp.close()
314 if cmpr:
315 buf = cmpr.flush()
316 compress_size = compress_size + len(buf)
317 self.fp.write(buf)
318 zinfo.compress_size = compress_size
319 else:
320 zinfo.compress_size = file_size
321 zinfo.CRC = CRC
322 zinfo.file_size = file_size
323 # Write CRC and file sizes after the file data
324 self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
325 zinfo.file_size))
326 self.filelist.append(zinfo)
327 self.NameToInfo[zinfo.filename] = zinfo
328
329 def writestr(self, zinfo, bytes):
Fred Drake484d7352000-10-02 21:14:52 +0000330 """Write a file into the archive. The contents is the string
331 'bytes'."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000332 self._writecheck(zinfo)
Tim Peterse1190062001-01-15 03:34:38 +0000333 zinfo.file_size = len(bytes) # Uncompressed size
334 zinfo.CRC = binascii.crc32(bytes) # CRC-32 checksum
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000335 if zinfo.compress_type == ZIP_DEFLATED:
336 co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
337 zlib.DEFLATED, -15)
338 bytes = co.compress(bytes) + co.flush()
Tim Peterse1190062001-01-15 03:34:38 +0000339 zinfo.compress_size = len(bytes) # Compressed size
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000340 else:
341 zinfo.compress_size = zinfo.file_size
Tim Peterse1190062001-01-15 03:34:38 +0000342 zinfo.header_offset = self.fp.tell() # Start of header bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000343 self.fp.write(zinfo.FileHeader())
Tim Peterse1190062001-01-15 03:34:38 +0000344 zinfo.file_offset = self.fp.tell() # Start of file bytes
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000345 self.fp.write(bytes)
346 if zinfo.flag_bits & 0x08:
Tim Peterse1190062001-01-15 03:34:38 +0000347 # Write CRC and file sizes after the file data
348 self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
349 zinfo.file_size))
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000350 self.filelist.append(zinfo)
351 self.NameToInfo[zinfo.filename] = zinfo
352
353 def __del__(self):
Fred Drake484d7352000-10-02 21:14:52 +0000354 """Call the "close()" method in case the user forgot."""
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000355 if self.fp:
356 self.fp.close()
357 self.fp = None
358
359 def close(self):
Fred Drake484d7352000-10-02 21:14:52 +0000360 """Close the file, and for mode "w" and "a" write the ending
361 records."""
Tim Peterse1190062001-01-15 03:34:38 +0000362 if self.mode in ("w", "a"): # write ending records
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000363 count = 0
364 pos1 = self.fp.tell()
Tim Peterse1190062001-01-15 03:34:38 +0000365 for zinfo in self.filelist: # write central directory
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000366 count = count + 1
367 dt = zinfo.date_time
368 dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
369 dostime = dt[3] << 11 | dt[4] << 5 | dt[5] / 2
370 centdir = struct.pack(structCentralDir,
371 stringCentralDir, zinfo.create_version,
372 zinfo.create_system, zinfo.extract_version, zinfo.reserved,
373 zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
374 zinfo.CRC, zinfo.compress_size, zinfo.file_size,
375 len(zinfo.filename), len(zinfo.extra), len(zinfo.comment),
376 0, zinfo.internal_attr, zinfo.external_attr,
377 zinfo.header_offset)
378 self.fp.write(centdir)
379 self.fp.write(zinfo.filename)
380 self.fp.write(zinfo.extra)
381 self.fp.write(zinfo.comment)
382 pos2 = self.fp.tell()
383 # Write end-of-zip-archive record
384 endrec = struct.pack(structEndArchive, stringEndArchive,
385 0, 0, count, count, pos2 - pos1, pos1, 0)
386 self.fp.write(endrec)
387 self.fp.close()
388 self.fp = None
389
390
391class PyZipFile(ZipFile):
Fred Drake484d7352000-10-02 21:14:52 +0000392 """Class to create ZIP archives with Python library files and packages."""
393
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000394 def writepy(self, pathname, basename = ""):
395 """Add all files from "pathname" to the ZIP archive.
396
Fred Drake484d7352000-10-02 21:14:52 +0000397 If pathname is a package directory, search the directory and
398 all package subdirectories recursively for all *.py and enter
399 the modules into the archive. If pathname is a plain
400 directory, listdir *.py and enter all modules. Else, pathname
401 must be a Python *.py file and the module will be put into the
402 archive. Added modules are always module.pyo or module.pyc.
403 This method will compile the module.py into module.pyc if
404 necessary.
405 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000406 dir, name = os.path.split(pathname)
407 if os.path.isdir(pathname):
408 initname = os.path.join(pathname, "__init__.py")
409 if os.path.isfile(initname):
410 # This is a package directory, add it
411 if basename:
412 basename = "%s/%s" % (basename, name)
413 else:
414 basename = name
415 if self.debug:
416 print "Adding package in", pathname, "as", basename
417 fname, arcname = self._get_codename(initname[0:-3], basename)
418 if self.debug:
419 print "Adding", arcname
420 self.write(fname, arcname)
421 dirlist = os.listdir(pathname)
422 dirlist.remove("__init__.py")
423 # Add all *.py files and package subdirectories
424 for filename in dirlist:
425 path = os.path.join(pathname, filename)
426 root, ext = os.path.splitext(filename)
427 if os.path.isdir(path):
428 if os.path.isfile(os.path.join(path, "__init__.py")):
429 # This is a package directory, add it
430 self.writepy(path, basename) # Recursive call
431 elif ext == ".py":
432 fname, arcname = self._get_codename(path[0:-3],
433 basename)
434 if self.debug:
435 print "Adding", arcname
436 self.write(fname, arcname)
437 else:
438 # This is NOT a package directory, add its files at top level
439 if self.debug:
440 print "Adding files from directory", pathname
441 for filename in os.listdir(pathname):
442 path = os.path.join(pathname, filename)
443 root, ext = os.path.splitext(filename)
444 if ext == ".py":
445 fname, arcname = self._get_codename(path[0:-3],
446 basename)
447 if self.debug:
448 print "Adding", arcname
449 self.write(fname, arcname)
450 else:
451 if pathname[-3:] != ".py":
452 raise RuntimeError, \
Fred Drake5db246d2000-09-29 20:44:48 +0000453 'Files added with writepy() must end with ".py"'
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000454 fname, arcname = self._get_codename(pathname[0:-3], basename)
455 if self.debug:
456 print "Adding file", arcname
457 self.write(fname, arcname)
458
459 def _get_codename(self, pathname, basename):
460 """Return (filename, archivename) for the path.
461
Fred Drake484d7352000-10-02 21:14:52 +0000462 Given a module name path, return the correct file path and
463 archive name, compiling if necessary. For example, given
464 /python/lib/string, return (/python/lib/string.pyc, string).
465 """
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000466 file_py = pathname + ".py"
467 file_pyc = pathname + ".pyc"
468 file_pyo = pathname + ".pyo"
469 if os.path.isfile(file_pyo) and \
470 os.stat(file_pyo)[8] >= os.stat(file_py)[8]:
Tim Peterse1190062001-01-15 03:34:38 +0000471 fname = file_pyo # Use .pyo file
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000472 elif not os.path.isfile(file_pyc) or \
Fred Drake484d7352000-10-02 21:14:52 +0000473 os.stat(file_pyc)[8] < os.stat(file_py)[8]:
474 import py_compile
Guido van Rossum32abe6f2000-03-31 17:30:02 +0000475 if self.debug:
476 print "Compiling", file_py
477 py_compile.compile(file_py, file_pyc)
478 fname = file_pyc
479 else:
480 fname = file_pyc
481 archivename = os.path.split(fname)[1]
482 if basename:
483 archivename = "%s/%s" % (basename, archivename)
484 return (fname, archivename)