You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

zipfile.py 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. "Read and write ZIP files."
  2. # Written by James C. Ahlstrom jim@interet.com
  3. # All rights transferred to CNRI pursuant to the Python contribution agreement
  4. import struct, os, time
  5. import binascii
  6. try:
  7. import zlib # We may need its compression method
  8. except ImportError:
  9. zlib = None
  10. __all__ = ["BadZipfile", "error", "ZIP_STORED", "ZIP_DEFLATED", "is_zipfile",
  11. "ZipInfo", "ZipFile", "PyZipFile"]
  12. class BadZipfile(Exception):
  13. pass
  14. error = BadZipfile # The exception raised by this module
  15. # constants for Zip file compression methods
  16. ZIP_STORED = 0
  17. ZIP_DEFLATED = 8
  18. # Other ZIP compression methods not supported
  19. # Here are some struct module formats for reading headers
  20. structEndArchive = "<4s4H2lH" # 9 items, end of archive, 22 bytes
  21. stringEndArchive = "PK\005\006" # magic number for end of archive record
  22. structCentralDir = "<4s4B4H3l5H2l"# 19 items, central directory, 46 bytes
  23. stringCentralDir = "PK\001\002" # magic number for central directory
  24. structFileHeader = "<4s2B4H3l2H" # 12 items, file header record, 30 bytes
  25. stringFileHeader = "PK\003\004" # magic number for file header
  26. # indexes of entries in the central directory structure
  27. _CD_SIGNATURE = 0
  28. _CD_CREATE_VERSION = 1
  29. _CD_CREATE_SYSTEM = 2
  30. _CD_EXTRACT_VERSION = 3
  31. _CD_EXTRACT_SYSTEM = 4 # is this meaningful?
  32. _CD_FLAG_BITS = 5
  33. _CD_COMPRESS_TYPE = 6
  34. _CD_TIME = 7
  35. _CD_DATE = 8
  36. _CD_CRC = 9
  37. _CD_COMPRESSED_SIZE = 10
  38. _CD_UNCOMPRESSED_SIZE = 11
  39. _CD_FILENAME_LENGTH = 12
  40. _CD_EXTRA_FIELD_LENGTH = 13
  41. _CD_COMMENT_LENGTH = 14
  42. _CD_DISK_NUMBER_START = 15
  43. _CD_INTERNAL_FILE_ATTRIBUTES = 16
  44. _CD_EXTERNAL_FILE_ATTRIBUTES = 17
  45. _CD_LOCAL_HEADER_OFFSET = 18
  46. # indexes of entries in the local file header structure
  47. _FH_SIGNATURE = 0
  48. _FH_EXTRACT_VERSION = 1
  49. _FH_EXTRACT_SYSTEM = 2 # is this meaningful?
  50. _FH_GENERAL_PURPOSE_FLAG_BITS = 3
  51. _FH_COMPRESSION_METHOD = 4
  52. _FH_LAST_MOD_TIME = 5
  53. _FH_LAST_MOD_DATE = 6
  54. _FH_CRC = 7
  55. _FH_COMPRESSED_SIZE = 8
  56. _FH_UNCOMPRESSED_SIZE = 9
  57. _FH_FILENAME_LENGTH = 10
  58. _FH_EXTRA_FIELD_LENGTH = 11
  59. # Used to compare file passed to ZipFile
  60. _STRING_TYPES = (type('s'), type(u's'))
  61. def is_zipfile(filename):
  62. """Quickly see if file is a ZIP file by checking the magic number.
  63. Will not accept a ZIP archive with an ending comment.
  64. """
  65. try:
  66. fpin = open(filename, "rb")
  67. fpin.seek(-22, 2) # Seek to end-of-file record
  68. endrec = fpin.read()
  69. fpin.close()
  70. if endrec[0:4] == "PK\005\006" and endrec[-2:] == "\000\000":
  71. return 1 # file has correct magic number
  72. except:
  73. pass
  74. class ZipInfo:
  75. """Class with attributes describing each file in the ZIP archive."""
  76. def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
  77. self.filename = filename # Name of the file in the archive
  78. self.date_time = date_time # year, month, day, hour, min, sec
  79. # Standard values:
  80. self.compress_type = ZIP_STORED # Type of compression for the file
  81. self.comment = "" # Comment for each file
  82. self.extra = "" # ZIP extra data
  83. self.create_system = 0 # System which created ZIP archive
  84. self.create_version = 20 # Version which created ZIP archive
  85. self.extract_version = 20 # Version needed to extract archive
  86. self.reserved = 0 # Must be zero
  87. self.flag_bits = 0 # ZIP flag bits
  88. self.volume = 0 # Volume number of file header
  89. self.internal_attr = 0 # Internal attributes
  90. self.external_attr = 0 # External file attributes
  91. # Other attributes are set by class ZipFile:
  92. # header_offset Byte offset to the file header
  93. # file_offset Byte offset to the start of the file data
  94. # CRC CRC-32 of the uncompressed file
  95. # compress_size Size of the compressed file
  96. # file_size Size of the uncompressed file
  97. def FileHeader(self):
  98. """Return the per-file header as a string."""
  99. dt = self.date_time
  100. dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
  101. dostime = dt[3] << 11 | dt[4] << 5 | dt[5] / 2
  102. if self.flag_bits & 0x08:
  103. # Set these to zero because we write them after the file data
  104. CRC = compress_size = file_size = 0
  105. else:
  106. CRC = self.CRC
  107. compress_size = self.compress_size
  108. file_size = self.file_size
  109. header = struct.pack(structFileHeader, stringFileHeader,
  110. self.extract_version, self.reserved, self.flag_bits,
  111. self.compress_type, dostime, dosdate, CRC,
  112. compress_size, file_size,
  113. len(self.filename), len(self.extra))
  114. return header + self.filename + self.extra
  115. class ZipFile:
  116. """ Class with methods to open, read, write, close, list zip files.
  117. z = ZipFile(file, mode="r", compression=ZIP_STORED)
  118. file: Either the path to the file, or a file-like object.
  119. If it is a path, the file will be opened and closed by ZipFile.
  120. mode: The mode can be either read "r", write "w" or append "a".
  121. compression: ZIP_STORED (no compression) or ZIP_DEFLATED (requires zlib).
  122. """
  123. fp = None # Set here since __del__ checks it
  124. def __init__(self, file, mode="r", compression=ZIP_STORED):
  125. """Open the ZIP file with mode read "r", write "w" or append "a"."""
  126. if compression == ZIP_STORED:
  127. pass
  128. elif compression == ZIP_DEFLATED:
  129. if not zlib:
  130. raise RuntimeError,\
  131. "Compression requires the (missing) zlib module"
  132. else:
  133. raise RuntimeError, "That compression method is not supported"
  134. self.debug = 0 # Level of printing: 0 through 3
  135. self.NameToInfo = {} # Find file info given name
  136. self.filelist = [] # List of ZipInfo instances for archive
  137. self.compression = compression # Method of compression
  138. self.mode = key = mode[0]
  139. # Check if we were passed a file-like object
  140. if type(file) in _STRING_TYPES:
  141. self._filePassed = 0
  142. self.filename = file
  143. modeDict = {'r' : 'rb', 'w': 'wb', 'a' : 'r+b'}
  144. self.fp = open(file, modeDict[mode])
  145. else:
  146. self._filePassed = 1
  147. self.fp = file
  148. self.filename = getattr(file, 'name', None)
  149. if key == 'r':
  150. self._GetContents()
  151. elif key == 'w':
  152. pass
  153. elif key == 'a':
  154. fp = self.fp
  155. fp.seek(-22, 2) # Seek to end-of-file record
  156. endrec = fp.read()
  157. if endrec[0:4] == stringEndArchive and \
  158. endrec[-2:] == "\000\000":
  159. self._GetContents() # file is a zip file
  160. # seek to start of directory and overwrite
  161. fp.seek(self.start_dir, 0)
  162. else: # file is not a zip file, just append
  163. fp.seek(0, 2)
  164. else:
  165. if not self._filePassed:
  166. self.fp.close()
  167. self.fp = None
  168. raise RuntimeError, 'Mode must be "r", "w" or "a"'
  169. def _GetContents(self):
  170. """Read the directory, making sure we close the file if the format
  171. is bad."""
  172. try:
  173. self._RealGetContents()
  174. except BadZipfile:
  175. if not self._filePassed:
  176. self.fp.close()
  177. self.fp = None
  178. raise
  179. def _RealGetContents(self):
  180. """Read in the table of contents for the ZIP file."""
  181. fp = self.fp
  182. fp.seek(-22, 2) # Start of end-of-archive record
  183. filesize = fp.tell() + 22 # Get file size
  184. endrec = fp.read(22) # Archive must not end with a comment!
  185. if endrec[0:4] != stringEndArchive or endrec[-2:] != "\000\000":
  186. raise BadZipfile, "File is not a zip file, or ends with a comment"
  187. endrec = struct.unpack(structEndArchive, endrec)
  188. if self.debug > 1:
  189. print endrec
  190. size_cd = endrec[5] # bytes in central directory
  191. offset_cd = endrec[6] # offset of central directory
  192. x = filesize - 22 - size_cd
  193. # "concat" is zero, unless zip was concatenated to another file
  194. concat = x - offset_cd
  195. if self.debug > 2:
  196. print "given, inferred, offset", offset_cd, x, concat
  197. # self.start_dir: Position of start of central directory
  198. self.start_dir = offset_cd + concat
  199. fp.seek(self.start_dir, 0)
  200. total = 0
  201. while total < size_cd:
  202. centdir = fp.read(46)
  203. total = total + 46
  204. if centdir[0:4] != stringCentralDir:
  205. raise BadZipfile, "Bad magic number for central directory"
  206. centdir = struct.unpack(structCentralDir, centdir)
  207. if self.debug > 2:
  208. print centdir
  209. filename = fp.read(centdir[_CD_FILENAME_LENGTH])
  210. # Create ZipInfo instance to store file information
  211. x = ZipInfo(filename)
  212. x.extra = fp.read(centdir[_CD_EXTRA_FIELD_LENGTH])
  213. x.comment = fp.read(centdir[_CD_COMMENT_LENGTH])
  214. total = (total + centdir[_CD_FILENAME_LENGTH]
  215. + centdir[_CD_EXTRA_FIELD_LENGTH]
  216. + centdir[_CD_COMMENT_LENGTH])
  217. x.header_offset = centdir[_CD_LOCAL_HEADER_OFFSET] + concat
  218. # file_offset must be computed below...
  219. (x.create_version, x.create_system, x.extract_version, x.reserved,
  220. x.flag_bits, x.compress_type, t, d,
  221. x.CRC, x.compress_size, x.file_size) = centdir[1:12]
  222. x.volume, x.internal_attr, x.external_attr = centdir[15:18]
  223. # Convert date/time code to (year, month, day, hour, min, sec)
  224. x.date_time = ( (d>>9)+1980, (d>>5)&0xF, d&0x1F,
  225. t>>11, (t>>5)&0x3F, (t&0x1F) * 2 )
  226. self.filelist.append(x)
  227. self.NameToInfo[x.filename] = x
  228. if self.debug > 2:
  229. print "total", total
  230. for data in self.filelist:
  231. fp.seek(data.header_offset, 0)
  232. fheader = fp.read(30)
  233. if fheader[0:4] != stringFileHeader:
  234. raise BadZipfile, "Bad magic number for file header"
  235. fheader = struct.unpack(structFileHeader, fheader)
  236. # file_offset is computed here, since the extra field for
  237. # the central directory and for the local file header
  238. # refer to different fields, and they can have different
  239. # lengths
  240. data.file_offset = (data.header_offset + 30
  241. + fheader[_FH_FILENAME_LENGTH]
  242. + fheader[_FH_EXTRA_FIELD_LENGTH])
  243. fname = fp.read(fheader[_FH_FILENAME_LENGTH])
  244. if fname != data.filename:
  245. raise RuntimeError, \
  246. 'File name in directory "%s" and header "%s" differ.' % (
  247. data.filename, fname)
  248. def namelist(self):
  249. """Return a list of file names in the archive."""
  250. l = []
  251. for data in self.filelist:
  252. l.append(data.filename)
  253. return l
  254. def infolist(self):
  255. """Return a list of class ZipInfo instances for files in the
  256. archive."""
  257. return self.filelist
  258. def printdir(self):
  259. """Print a table of contents for the zip file."""
  260. print "%-46s %19s %12s" % ("File Name", "Modified ", "Size")
  261. for zinfo in self.filelist:
  262. date = "%d-%02d-%02d %02d:%02d:%02d" % zinfo.date_time
  263. print "%-46s %s %12d" % (zinfo.filename, date, zinfo.file_size)
  264. def testzip(self):
  265. """Read all the files and check the CRC."""
  266. for zinfo in self.filelist:
  267. try:
  268. self.read(zinfo.filename) # Check CRC-32
  269. except:
  270. return zinfo.filename
  271. def getinfo(self, name):
  272. """Return the instance of ZipInfo given 'name'."""
  273. return self.NameToInfo[name]
  274. def read(self, name):
  275. """Return file bytes (as a string) for name."""
  276. if self.mode not in ("r", "a"):
  277. raise RuntimeError, 'read() requires mode "r" or "a"'
  278. if not self.fp:
  279. raise RuntimeError, \
  280. "Attempt to read ZIP archive that was already closed"
  281. zinfo = self.getinfo(name)
  282. filepos = self.fp.tell()
  283. self.fp.seek(zinfo.file_offset, 0)
  284. bytes = self.fp.read(zinfo.compress_size)
  285. self.fp.seek(filepos, 0)
  286. if zinfo.compress_type == ZIP_STORED:
  287. pass
  288. elif zinfo.compress_type == ZIP_DEFLATED:
  289. if not zlib:
  290. raise RuntimeError, \
  291. "De-compression requires the (missing) zlib module"
  292. # zlib compress/decompress code by Jeremy Hylton of CNRI
  293. dc = zlib.decompressobj(-15)
  294. bytes = dc.decompress(bytes)
  295. # need to feed in unused pad byte so that zlib won't choke
  296. ex = dc.decompress('Z') + dc.flush()
  297. if ex:
  298. bytes = bytes + ex
  299. else:
  300. raise BadZipfile, \
  301. "Unsupported compression method %d for file %s" % \
  302. (zinfo.compress_type, name)
  303. crc = binascii.crc32(bytes)
  304. if crc != zinfo.CRC:
  305. raise BadZipfile, "Bad CRC-32 for file %s" % name
  306. return bytes
  307. def _writecheck(self, zinfo):
  308. """Check for errors before writing a file to the archive."""
  309. if self.NameToInfo.has_key(zinfo.filename):
  310. if self.debug: # Warning for duplicate names
  311. print "Duplicate name:", zinfo.filename
  312. if self.mode not in ("w", "a"):
  313. raise RuntimeError, 'write() requires mode "w" or "a"'
  314. if not self.fp:
  315. raise RuntimeError, \
  316. "Attempt to write ZIP archive that was already closed"
  317. if zinfo.compress_type == ZIP_DEFLATED and not zlib:
  318. raise RuntimeError, \
  319. "Compression requires the (missing) zlib module"
  320. if zinfo.compress_type not in (ZIP_STORED, ZIP_DEFLATED):
  321. raise RuntimeError, \
  322. "That compression method is not supported"
  323. def write(self, filename, arcname=None, compress_type=None):
  324. """Put the bytes from filename into the archive under the name
  325. arcname."""
  326. st = os.stat(filename)
  327. mtime = time.localtime(st[8])
  328. date_time = mtime[0:6]
  329. # Create ZipInfo instance to store file information
  330. if arcname is None:
  331. zinfo = ZipInfo(filename, date_time)
  332. else:
  333. zinfo = ZipInfo(arcname, date_time)
  334. zinfo.external_attr = st[0] << 16 # Unix attributes
  335. if compress_type is None:
  336. zinfo.compress_type = self.compression
  337. else:
  338. zinfo.compress_type = compress_type
  339. self._writecheck(zinfo)
  340. fp = open(filename, "rb")
  341. zinfo.flag_bits = 0x00
  342. zinfo.header_offset = self.fp.tell() # Start of header bytes
  343. # Must overwrite CRC and sizes with correct data later
  344. zinfo.CRC = CRC = 0
  345. zinfo.compress_size = compress_size = 0
  346. zinfo.file_size = file_size = 0
  347. self.fp.write(zinfo.FileHeader())
  348. zinfo.file_offset = self.fp.tell() # Start of file bytes
  349. if zinfo.compress_type == ZIP_DEFLATED:
  350. cmpr = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  351. zlib.DEFLATED, -15)
  352. else:
  353. cmpr = None
  354. while 1:
  355. buf = fp.read(1024 * 8)
  356. if not buf:
  357. break
  358. file_size = file_size + len(buf)
  359. CRC = binascii.crc32(buf, CRC)
  360. if cmpr:
  361. buf = cmpr.compress(buf)
  362. compress_size = compress_size + len(buf)
  363. self.fp.write(buf)
  364. fp.close()
  365. if cmpr:
  366. buf = cmpr.flush()
  367. compress_size = compress_size + len(buf)
  368. self.fp.write(buf)
  369. zinfo.compress_size = compress_size
  370. else:
  371. zinfo.compress_size = file_size
  372. zinfo.CRC = CRC
  373. zinfo.file_size = file_size
  374. # Seek backwards and write CRC and file sizes
  375. position = self.fp.tell() # Preserve current position in file
  376. self.fp.seek(zinfo.header_offset + 14, 0)
  377. self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
  378. zinfo.file_size))
  379. self.fp.seek(position, 0)
  380. self.filelist.append(zinfo)
  381. self.NameToInfo[zinfo.filename] = zinfo
  382. def writestr(self, zinfo, bytes):
  383. """Write a file into the archive. The contents is the string
  384. 'bytes'."""
  385. self._writecheck(zinfo)
  386. zinfo.file_size = len(bytes) # Uncompressed size
  387. zinfo.CRC = binascii.crc32(bytes) # CRC-32 checksum
  388. if zinfo.compress_type == ZIP_DEFLATED:
  389. co = zlib.compressobj(zlib.Z_DEFAULT_COMPRESSION,
  390. zlib.DEFLATED, -15)
  391. bytes = co.compress(bytes) + co.flush()
  392. zinfo.compress_size = len(bytes) # Compressed size
  393. else:
  394. zinfo.compress_size = zinfo.file_size
  395. zinfo.header_offset = self.fp.tell() # Start of header bytes
  396. self.fp.write(zinfo.FileHeader())
  397. zinfo.file_offset = self.fp.tell() # Start of file bytes
  398. self.fp.write(bytes)
  399. if zinfo.flag_bits & 0x08:
  400. # Write CRC and file sizes after the file data
  401. self.fp.write(struct.pack("<lll", zinfo.CRC, zinfo.compress_size,
  402. zinfo.file_size))
  403. self.filelist.append(zinfo)
  404. self.NameToInfo[zinfo.filename] = zinfo
  405. def __del__(self):
  406. """Call the "close()" method in case the user forgot."""
  407. if self.fp and not self._filePassed:
  408. self.fp.close()
  409. self.fp = None
  410. def close(self):
  411. """Close the file, and for mode "w" and "a" write the ending
  412. records."""
  413. if self.mode in ("w", "a"): # write ending records
  414. count = 0
  415. pos1 = self.fp.tell()
  416. for zinfo in self.filelist: # write central directory
  417. count = count + 1
  418. dt = zinfo.date_time
  419. dosdate = (dt[0] - 1980) << 9 | dt[1] << 5 | dt[2]
  420. dostime = dt[3] << 11 | dt[4] << 5 | dt[5] / 2
  421. centdir = struct.pack(structCentralDir,
  422. stringCentralDir, zinfo.create_version,
  423. zinfo.create_system, zinfo.extract_version, zinfo.reserved,
  424. zinfo.flag_bits, zinfo.compress_type, dostime, dosdate,
  425. zinfo.CRC, zinfo.compress_size, zinfo.file_size,
  426. len(zinfo.filename), len(zinfo.extra), len(zinfo.comment),
  427. 0, zinfo.internal_attr, zinfo.external_attr,
  428. zinfo.header_offset)
  429. self.fp.write(centdir)
  430. self.fp.write(zinfo.filename)
  431. self.fp.write(zinfo.extra)
  432. self.fp.write(zinfo.comment)
  433. pos2 = self.fp.tell()
  434. # Write end-of-zip-archive record
  435. endrec = struct.pack(structEndArchive, stringEndArchive,
  436. 0, 0, count, count, pos2 - pos1, pos1, 0)
  437. self.fp.write(endrec)
  438. self.fp.flush()
  439. if not self._filePassed:
  440. self.fp.close()
  441. self.fp = None
  442. class PyZipFile(ZipFile):
  443. """Class to create ZIP archives with Python library files and packages."""
  444. def writepy(self, pathname, basename = ""):
  445. """Add all files from "pathname" to the ZIP archive.
  446. If pathname is a package directory, search the directory and
  447. all package subdirectories recursively for all *.py and enter
  448. the modules into the archive. If pathname is a plain
  449. directory, listdir *.py and enter all modules. Else, pathname
  450. must be a Python *.py file and the module will be put into the
  451. archive. Added modules are always module.pyo or module.pyc.
  452. This method will compile the module.py into module.pyc if
  453. necessary.
  454. """
  455. dir, name = os.path.split(pathname)
  456. if os.path.isdir(pathname):
  457. initname = os.path.join(pathname, "__init__.py")
  458. if os.path.isfile(initname):
  459. # This is a package directory, add it
  460. if basename:
  461. basename = "%s/%s" % (basename, name)
  462. else:
  463. basename = name
  464. if self.debug:
  465. print "Adding package in", pathname, "as", basename
  466. fname, arcname = self._get_codename(initname[0:-3], basename)
  467. if self.debug:
  468. print "Adding", arcname
  469. self.write(fname, arcname)
  470. dirlist = os.listdir(pathname)
  471. dirlist.remove("__init__.py")
  472. # Add all *.py files and package subdirectories
  473. for filename in dirlist:
  474. path = os.path.join(pathname, filename)
  475. root, ext = os.path.splitext(filename)
  476. if os.path.isdir(path):
  477. if os.path.isfile(os.path.join(path, "__init__.py")):
  478. # This is a package directory, add it
  479. self.writepy(path, basename) # Recursive call
  480. elif ext == ".py":
  481. fname, arcname = self._get_codename(path[0:-3],
  482. basename)
  483. if self.debug:
  484. print "Adding", arcname
  485. self.write(fname, arcname)
  486. else:
  487. # This is NOT a package directory, add its files at top level
  488. if self.debug:
  489. print "Adding files from directory", pathname
  490. for filename in os.listdir(pathname):
  491. path = os.path.join(pathname, filename)
  492. root, ext = os.path.splitext(filename)
  493. if ext == ".py":
  494. fname, arcname = self._get_codename(path[0:-3],
  495. basename)
  496. if self.debug:
  497. print "Adding", arcname
  498. self.write(fname, arcname)
  499. else:
  500. if pathname[-3:] != ".py":
  501. raise RuntimeError, \
  502. 'Files added with writepy() must end with ".py"'
  503. fname, arcname = self._get_codename(pathname[0:-3], basename)
  504. if self.debug:
  505. print "Adding file", arcname
  506. self.write(fname, arcname)
  507. def _get_codename(self, pathname, basename):
  508. """Return (filename, archivename) for the path.
  509. Given a module name path, return the correct file path and
  510. archive name, compiling if necessary. For example, given
  511. /python/lib/string, return (/python/lib/string.pyc, string).
  512. """
  513. file_py = pathname + ".py"
  514. file_pyc = pathname + ".pyc"
  515. file_pyo = pathname + ".pyo"
  516. if os.path.isfile(file_pyo) and \
  517. os.stat(file_pyo)[8] >= os.stat(file_py)[8]:
  518. fname = file_pyo # Use .pyo file
  519. elif not os.path.isfile(file_pyc) or \
  520. os.stat(file_pyc)[8] < os.stat(file_py)[8]:
  521. import py_compile
  522. if self.debug:
  523. print "Compiling", file_py
  524. py_compile.compile(file_py, file_pyc)
  525. fname = file_pyc
  526. else:
  527. fname = file_pyc
  528. archivename = os.path.split(fname)[1]
  529. if basename:
  530. archivename = "%s/%s" % (basename, archivename)
  531. return (fname, archivename)