PageRenderTime 118ms CodeModel.GetById 29ms RepoModel.GetById 2ms app.codeStats 0ms

/pypy/module/zipimport/interp_zipimport.py

https://bitbucket.org/pypy/pypy/
Python | 418 lines | 359 code | 41 blank | 18 comment | 57 complexity | 9d37d8385e3c1252d6a3c6556ad9dbd5 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from pypy.interpreter.baseobjspace import W_Root
  2. from pypy.interpreter.error import OperationError, oefmt
  3. from pypy.interpreter.gateway import interp2app, unwrap_spec
  4. from pypy.interpreter.typedef import TypeDef, GetSetProperty
  5. from pypy.interpreter.module import Module
  6. from pypy.module.imp import importing
  7. from pypy.module.zlib.interp_zlib import zlib_error
  8. from rpython.rlib.unroll import unrolling_iterable
  9. from rpython.rlib.rzipfile import RZipFile, BadZipfile
  10. from rpython.rlib.rzlib import RZlibError
  11. import os
  12. import stat
  13. ZIPSEP = '/'
  14. # note that zipfiles always use slash, but for OSes with other
  15. # separators, we need to pretend that we had the os.sep.
  16. ENUMERATE_EXTS = unrolling_iterable(
  17. [(True, True, ZIPSEP + '__init__.pyc'),
  18. (True, True, ZIPSEP + '__init__.pyo'),
  19. (False, True, ZIPSEP + '__init__.py'),
  20. (True, False, '.pyc'),
  21. (True, False, '.pyo'),
  22. (False, False, '.py')])
  23. class Cache:
  24. def __init__(self, space):
  25. self.w_error = space.new_exception_class("zipimport.ZipImportError",
  26. space.w_ImportError)
  27. def get_error(space):
  28. return space.fromcache(Cache).w_error
  29. class W_ZipCache(W_Root):
  30. def __init__(self):
  31. self.cache = {}
  32. def get(self, name):
  33. return self.cache[name]
  34. def set(self, name, w_importer):
  35. self.cache[name] = w_importer
  36. # -------------- dict-like interface -----------------
  37. # I don't care about speed of those, they're obscure anyway
  38. # THIS IS A TERRIBLE HACK TO BE CPYTHON COMPATIBLE
  39. @unwrap_spec(name=str)
  40. def getitem(self, space, name):
  41. try:
  42. w_zipimporter = self.cache[name]
  43. except KeyError:
  44. raise OperationError(space.w_KeyError, space.wrap(name))
  45. assert isinstance(w_zipimporter, W_ZipImporter)
  46. w = space.wrap
  47. w_d = space.newdict()
  48. for key, info in w_zipimporter.zip_file.NameToInfo.iteritems():
  49. if ZIPSEP != os.path.sep:
  50. key = key.replace(ZIPSEP, os.path.sep)
  51. space.setitem(w_d, w(key), space.newtuple([
  52. w(info.filename), w(info.compress_type), w(info.compress_size),
  53. w(info.file_size), w(info.file_offset), w(info.dostime),
  54. w(info.dosdate), w(info.CRC)]))
  55. return w_d
  56. def keys(self, space):
  57. return space.newlist([space.wrap(s)
  58. for s in self.cache.keys()])
  59. def values(self, space):
  60. keys = self.cache.keys()
  61. values_w = [self.getitem(space, key) for key in keys]
  62. return space.newlist(values_w)
  63. def items(self, space):
  64. w = space.wrap
  65. items_w = [space.newtuple([w(key), self.getitem(space, key)])
  66. for key in self.cache.keys()]
  67. return space.newlist(items_w)
  68. def iterkeys(self, space):
  69. return space.iter(self.keys(space))
  70. def itervalues(self, space):
  71. return space.iter(self.values(space))
  72. def iteritems(self, space):
  73. return space.iter(self.items(space))
  74. @unwrap_spec(name=str)
  75. def contains(self, space, name):
  76. return space.newbool(name in self.cache)
  77. def clear(self, space):
  78. self.cache = {}
  79. @unwrap_spec(name=str)
  80. def delitem(self, space, name):
  81. del self.cache[name]
  82. W_ZipCache.typedef = TypeDef(
  83. 'zip_dict',
  84. __getitem__ = interp2app(W_ZipCache.getitem),
  85. __contains__ = interp2app(W_ZipCache.contains),
  86. __iter__ = interp2app(W_ZipCache.iterkeys),
  87. items = interp2app(W_ZipCache.items),
  88. iteritems = interp2app(W_ZipCache.iteritems),
  89. keys = interp2app(W_ZipCache.keys),
  90. iterkeys = interp2app(W_ZipCache.iterkeys),
  91. values = interp2app(W_ZipCache.values),
  92. itervalues = interp2app(W_ZipCache.itervalues),
  93. clear = interp2app(W_ZipCache.clear),
  94. __delitem__ = interp2app(W_ZipCache.delitem),
  95. )
  96. zip_cache = W_ZipCache()
  97. class W_ZipImporter(W_Root):
  98. def __init__(self, space, name, filename, zip_file, prefix):
  99. self.space = space
  100. self.name = name
  101. self.filename = filename
  102. self.zip_file = zip_file
  103. self.prefix = prefix
  104. def getprefix(self, space):
  105. if ZIPSEP == os.path.sep:
  106. return space.wrap(self.prefix)
  107. return space.wrap(self.prefix.replace(ZIPSEP, os.path.sep))
  108. def _find_relative_path(self, filename):
  109. if filename.startswith(self.filename):
  110. filename = filename[len(self.filename):]
  111. if filename.startswith(os.path.sep) or filename.startswith(ZIPSEP):
  112. filename = filename[1:]
  113. if ZIPSEP != os.path.sep:
  114. filename = filename.replace(os.path.sep, ZIPSEP)
  115. return filename
  116. def corr_zname(self, fname):
  117. if ZIPSEP != os.path.sep:
  118. return fname.replace(ZIPSEP, os.path.sep)
  119. else:
  120. return fname
  121. def import_py_file(self, space, modname, filename, buf, pkgpath):
  122. w = space.wrap
  123. w_mod = w(Module(space, w(modname)))
  124. real_name = self.filename + os.path.sep + self.corr_zname(filename)
  125. space.setattr(w_mod, w('__loader__'), space.wrap(self))
  126. importing._prepare_module(space, w_mod, real_name, pkgpath)
  127. co_filename = self.make_co_filename(filename)
  128. code_w = importing.parse_source_module(space, co_filename, buf)
  129. importing.exec_code_module(space, w_mod, code_w)
  130. return w_mod
  131. def _parse_mtime(self, space, filename):
  132. w = space.wrap
  133. try:
  134. info = self.zip_file.NameToInfo[filename]
  135. t = info.date_time
  136. except KeyError:
  137. return 0
  138. else:
  139. w_mktime = space.getattr(space.getbuiltinmodule('time'),
  140. w('mktime'))
  141. # XXX this is incredible fishing around module limitations
  142. # in order to compare timestamps of .py and .pyc files
  143. # we need time.mktime support on rpython level
  144. all = [w(t[0]), w(t[1]), w(t[2]), w(t[3]), w(t[4]),
  145. w(t[5]), w(0), w(1), w(-1)]
  146. mtime = int(space.float_w(space.call_function(w_mktime, space.newtuple(all))))
  147. return mtime
  148. def check_newer_pyfile(self, space, filename, timestamp):
  149. # check if the timestamp stored in the .pyc is matching
  150. # the actual timestamp of the .py file, if any
  151. mtime = self._parse_mtime(space, filename)
  152. if mtime == 0:
  153. return False
  154. # Lenient date/time comparison function. The precision of the mtime
  155. # in the archive is lower than the mtime stored in a .pyc: we
  156. # must allow a difference of at most one second.
  157. d = mtime - timestamp
  158. if d < 0:
  159. d = -d
  160. return d > 1 # more than one second => different
  161. def can_use_pyc(self, space, filename, magic, timestamp):
  162. if magic != importing.get_pyc_magic(space):
  163. return False
  164. if self.check_newer_pyfile(space, filename[:-1], timestamp):
  165. return False
  166. return True
  167. def import_pyc_file(self, space, modname, filename, buf, pkgpath):
  168. w = space.wrap
  169. magic = importing._get_long(buf[:4])
  170. timestamp = importing._get_long(buf[4:8])
  171. if not self.can_use_pyc(space, filename, magic, timestamp):
  172. return None
  173. buf = buf[8:] # XXX ugly copy, should use sequential read instead
  174. w_mod = w(Module(space, w(modname)))
  175. real_name = self.filename + os.path.sep + self.corr_zname(filename)
  176. space.setattr(w_mod, w('__loader__'), space.wrap(self))
  177. importing._prepare_module(space, w_mod, real_name, pkgpath)
  178. result = importing.load_compiled_module(space, w(modname), w_mod,
  179. filename, magic, timestamp,
  180. buf)
  181. return result
  182. def have_modulefile(self, space, filename):
  183. if ZIPSEP != os.path.sep:
  184. filename = filename.replace(os.path.sep, ZIPSEP)
  185. try:
  186. self.zip_file.NameToInfo[filename]
  187. return True
  188. except KeyError:
  189. return False
  190. @unwrap_spec(fullname=str)
  191. def find_module(self, space, fullname, w_path=None):
  192. filename = self.make_filename(fullname)
  193. for _, _, ext in ENUMERATE_EXTS:
  194. if self.have_modulefile(space, filename + ext):
  195. return space.wrap(self)
  196. def make_filename(self, fullname):
  197. startpos = fullname.rfind('.') + 1 # 0 when not found
  198. assert startpos >= 0
  199. subname = fullname[startpos:]
  200. if ZIPSEP == os.path.sep:
  201. return self.prefix + subname.replace('.', '/')
  202. else:
  203. return self.prefix.replace(os.path.sep, ZIPSEP) + \
  204. subname.replace('.', '/')
  205. def make_co_filename(self, filename):
  206. """
  207. Return the filename to be used for compiling the module, i.e. what
  208. gets in code_object.co_filename. Something like
  209. 'myfile.zip/mymodule.py'
  210. """
  211. return self.filename + os.path.sep + filename
  212. @unwrap_spec(fullname=str)
  213. def load_module(self, space, fullname):
  214. w = space.wrap
  215. filename = self.make_filename(fullname)
  216. for compiled, is_package, ext in ENUMERATE_EXTS:
  217. fname = filename + ext
  218. try:
  219. buf = self.zip_file.read(fname)
  220. except (KeyError, OSError, BadZipfile):
  221. pass
  222. except RZlibError as e:
  223. # in this case, CPython raises the direct exception coming
  224. # from the zlib module: let's to the same
  225. raise zlib_error(space, e.msg)
  226. else:
  227. if is_package:
  228. pkgpath = (self.filename + os.path.sep +
  229. self.corr_zname(filename))
  230. else:
  231. pkgpath = None
  232. try:
  233. if compiled:
  234. w_result = self.import_pyc_file(space, fullname, fname,
  235. buf, pkgpath)
  236. if w_result is not None:
  237. return w_result
  238. else:
  239. return self.import_py_file(space, fullname, fname,
  240. buf, pkgpath)
  241. except:
  242. w_mods = space.sys.get('modules')
  243. space.call_method(w_mods, 'pop', w(fullname), space.w_None)
  244. raise
  245. raise oefmt(get_error(space), "can't find module '%s'", fullname)
  246. @unwrap_spec(filename=str)
  247. def get_data(self, space, filename):
  248. filename = self._find_relative_path(filename)
  249. w = space.wrap
  250. try:
  251. data = self.zip_file.read(filename)
  252. return w(data)
  253. except (KeyError, OSError, BadZipfile):
  254. raise oefmt(space.w_IOError, "Error reading file")
  255. except RZlibError as e:
  256. # in this case, CPython raises the direct exception coming
  257. # from the zlib module: let's to the same
  258. raise zlib_error(space, e.msg)
  259. @unwrap_spec(fullname=str)
  260. def get_code(self, space, fullname):
  261. filename = self.make_filename(fullname)
  262. for compiled, _, ext in ENUMERATE_EXTS:
  263. if self.have_modulefile(space, filename + ext):
  264. w_source = self.get_data(space, filename + ext)
  265. source = space.str_w(w_source)
  266. if compiled:
  267. magic = importing._get_long(source[:4])
  268. timestamp = importing._get_long(source[4:8])
  269. if not self.can_use_pyc(space, filename + ext,
  270. magic, timestamp):
  271. continue
  272. code_w = importing.read_compiled_module(
  273. space, filename + ext, source[8:])
  274. else:
  275. co_filename = self.make_co_filename(filename+ext)
  276. code_w = importing.parse_source_module(
  277. space, co_filename, source)
  278. return space.wrap(code_w)
  279. raise oefmt(get_error(space),
  280. "Cannot find source or code for %s in %s",
  281. filename, self.name)
  282. @unwrap_spec(fullname=str)
  283. def get_source(self, space, fullname):
  284. filename = self.make_filename(fullname)
  285. found = False
  286. for compiled, _, ext in ENUMERATE_EXTS:
  287. fname = filename + ext
  288. if self.have_modulefile(space, fname):
  289. if not compiled:
  290. return self.get_data(space, fname)
  291. else:
  292. found = True
  293. if found:
  294. return space.w_None
  295. raise oefmt(get_error(space),
  296. "Cannot find source for %s in %s", filename, self.name)
  297. @unwrap_spec(fullname=str)
  298. def get_filename(self, space, fullname):
  299. filename = self.make_filename(fullname)
  300. for _, is_package, ext in ENUMERATE_EXTS:
  301. if self.have_modulefile(space, filename + ext):
  302. return space.wrap(self.filename + os.path.sep +
  303. self.corr_zname(filename + ext))
  304. raise oefmt(get_error(space),
  305. "Cannot find module %s in %s", filename, self.name)
  306. @unwrap_spec(fullname=str)
  307. def is_package(self, space, fullname):
  308. filename = self.make_filename(fullname)
  309. for _, is_package, ext in ENUMERATE_EXTS:
  310. if self.have_modulefile(space, filename + ext):
  311. return space.wrap(is_package)
  312. raise oefmt(get_error(space),
  313. "Cannot find module %s in %s", filename, self.name)
  314. def getarchive(self, space):
  315. space = self.space
  316. return space.wrap(self.filename)
  317. @unwrap_spec(name='str0')
  318. def descr_new_zipimporter(space, w_type, name):
  319. ok = False
  320. parts_ends = [i for i in range(0, len(name))
  321. if name[i] == os.path.sep or name[i] == ZIPSEP]
  322. parts_ends.append(len(name))
  323. filename = "" # make annotator happy
  324. for i in parts_ends:
  325. filename = name[:i]
  326. if not filename:
  327. filename = os.path.sep
  328. try:
  329. s = os.stat(filename)
  330. except OSError:
  331. raise oefmt(get_error(space), "Cannot find name %s", filename)
  332. if not stat.S_ISDIR(s.st_mode):
  333. ok = True
  334. break
  335. if not ok:
  336. raise oefmt(get_error(space), "Did not find %s to be a valid zippath",
  337. name)
  338. try:
  339. w_result = zip_cache.get(filename)
  340. if w_result is None:
  341. raise oefmt(get_error(space),
  342. "Cannot import %s from zipfile, recursion detected or"
  343. "already tried and failed", name)
  344. except KeyError:
  345. zip_cache.cache[filename] = None
  346. try:
  347. zip_file = RZipFile(filename, 'r')
  348. except (BadZipfile, OSError):
  349. raise oefmt(get_error(space), "%s seems not to be a zipfile", filename)
  350. except RZlibError as e:
  351. # in this case, CPython raises the direct exception coming
  352. # from the zlib module: let's to the same
  353. raise zlib_error(space, e.msg)
  354. prefix = name[len(filename):]
  355. if prefix.startswith(os.path.sep) or prefix.startswith(ZIPSEP):
  356. prefix = prefix[1:]
  357. if prefix and not prefix.endswith(ZIPSEP) and not prefix.endswith(os.path.sep):
  358. prefix += ZIPSEP
  359. w_result = space.wrap(W_ZipImporter(space, name, filename, zip_file, prefix))
  360. zip_cache.set(filename, w_result)
  361. return w_result
  362. W_ZipImporter.typedef = TypeDef(
  363. 'zipimporter',
  364. __new__ = interp2app(descr_new_zipimporter),
  365. find_module = interp2app(W_ZipImporter.find_module),
  366. get_data = interp2app(W_ZipImporter.get_data),
  367. get_code = interp2app(W_ZipImporter.get_code),
  368. get_source = interp2app(W_ZipImporter.get_source),
  369. get_filename = interp2app(W_ZipImporter.get_filename),
  370. is_package = interp2app(W_ZipImporter.is_package),
  371. load_module = interp2app(W_ZipImporter.load_module),
  372. archive = GetSetProperty(W_ZipImporter.getarchive),
  373. prefix = GetSetProperty(W_ZipImporter.getprefix),
  374. )