pyimod03_importers.py 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2005-2021, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader.
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. """
  12. PEP-302 and PEP-451 importers for frozen applications.
  13. """
  14. # **NOTE** This module is used during bootstrap.
  15. # Import *ONLY* builtin modules.
  16. # List of built-in modules: sys.builtin_module_names
  17. import sys
  18. import _frozen_importlib
  19. import pyimod01_os_path as pyi_os_path
  20. from pyimod02_archive import ArchiveReadError, ZlibArchiveReader
  21. SYS_PREFIX = sys._MEIPASS + pyi_os_path.os_sep
  22. SYS_PREFIXLEN = len(SYS_PREFIX)
  23. # In Python 3, it is recommended to use class 'types.ModuleType' to create a new module. However, 'types' module is
  24. # not a built-in module. The 'types' module uses this trick with using type() function:
  25. imp_new_module = type(sys)
  26. if sys.flags.verbose and sys.stderr:
  27. def trace(msg, *a):
  28. sys.stderr.write(msg % a)
  29. sys.stderr.write("\n")
  30. else:
  31. def trace(msg, *a):
  32. pass
  33. class FrozenPackageImporter(object):
  34. """
  35. Wrapper class for FrozenImporter that imports one specific fullname from a module named by an alternate fullname.
  36. The alternate fullname is derived from the __path__ of the package module containing that module.
  37. This is called by FrozenImporter.find_module whenever a module is found as a result of searching module.__path__
  38. """
  39. def __init__(self, importer, entry_name):
  40. self._entry_name = entry_name
  41. self._importer = importer
  42. def load_module(self, fullname):
  43. # Deprecated in Python 3.4, see PEP-451.
  44. return self._importer.load_module(fullname, self._entry_name)
  45. def _decode_source(source_bytes):
  46. """
  47. Decode bytes representing source code and return the string. Universal newline support is used in the decoding.
  48. Based on CPython's implementation of the same functionality:
  49. https://github.com/python/cpython/blob/3.9/Lib/importlib/_bootstrap_external.py#L679-L688
  50. """
  51. # Local imports to avoid bootstrap issues
  52. # NOTE: both modules are listed in compat.PY3_BASE_MODULES and collected into base_library.zip.
  53. import io
  54. import tokenize
  55. source_bytes_readline = io.BytesIO(source_bytes).readline
  56. encoding = tokenize.detect_encoding(source_bytes_readline)
  57. newline_decoder = io.IncrementalNewlineDecoder(decoder=None, translate=True)
  58. return newline_decoder.decode(source_bytes.decode(encoding[0]))
  59. class FrozenImporter(object):
  60. """
  61. Load bytecode of Python modules from the executable created by PyInstaller.
  62. Python bytecode is zipped and appended to the executable.
  63. NOTE: PYZ format cannot be replaced by zipimport module.
  64. The problem is that we have no control over zipimport; for instance, it does not work if the zip file is embedded
  65. into a PKG that is appended to an executable, like we create in one-file mode.
  66. This is PEP-302 finder and loader class for the ``sys.meta_path`` hook. A PEP-302 finder requires method
  67. find_module() to return loader class with method load_module(). Both these methods are implemented in one class.
  68. This is also a PEP-451 finder and loader class for the ModuleSpec type import system. A PEP-451 finder requires
  69. method find_spec(), a PEP-451 loader requires methods exec_module(), load_module(9 and (optionally) create_module().
  70. All these methods are implemented in this one class.
  71. To use this class just call:
  72. FrozenImporter.install()
  73. """
  74. def __init__(self):
  75. """
  76. Load, unzip and initialize the Zip archive bundled with the executable.
  77. """
  78. # Examine all items in sys.path and the one like /path/executable_name?117568 is the correct executable with
  79. # the bundled zip archive. Use this value for the ZlibArchiveReader class, and remove this item from sys.path.
  80. # It was needed only for FrozenImporter class. Wrong path from sys.path raises an ArchiveReadError exception.
  81. for pyz_filepath in sys.path:
  82. try:
  83. # Unzip zip archive bundled with the executable.
  84. self._pyz_archive = ZlibArchiveReader(pyz_filepath)
  85. # Verify the integrity of the zip archive with Python modules.
  86. # This is already done when creating the ZlibArchiveReader instance.
  87. #self._pyz_archive.checkmagic()
  88. # As no Exception was raised, we can assume that ZlibArchiveReader was successfully loaded.
  89. # Let's remove 'pyz_filepath' from sys.path.
  90. sys.path.remove(pyz_filepath)
  91. # Some runtime hook might need access to the list of available frozen modules. Let's make them
  92. # accessible as a set().
  93. self.toc = set(self._pyz_archive.toc.keys())
  94. # Return - no error was raised.
  95. trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath)
  96. return
  97. except IOError:
  98. # Item from sys.path is not ZlibArchiveReader; let's try next one.
  99. continue
  100. except ArchiveReadError:
  101. # Item from sys.path is not ZlibArchiveReader; let's try next one.
  102. continue
  103. # sys.path does not contain the filename of the executable with the bundled zip archive. Raise import error.
  104. raise ImportError("Cannot load frozen modules.")
  105. # Private helper
  106. def _is_pep420_namespace_package(self, fullname):
  107. if fullname in self.toc:
  108. try:
  109. return self._pyz_archive.is_pep420_namespace_package(fullname)
  110. except Exception as e:
  111. raise ImportError('Loader FrozenImporter cannot handle module ' + fullname) from e
  112. else:
  113. raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)
  114. def find_module(self, fullname, path=None):
  115. # Deprecated in Python 3.4, see PEP-451
  116. """
  117. PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.
  118. fullname fully qualified name of the module
  119. path None for a top-level module, or package.__path__ for submodules or subpackages.
  120. Return a loader object if the module was found, or None if it was not. If find_module() raises an exception,
  121. it will be propagated to the caller, aborting the import.
  122. """
  123. module_loader = None # None means - no module found in this importer.
  124. if fullname in self.toc:
  125. # Tell the import machinery to use self.load_module() to load the module.
  126. module_loader = self
  127. trace("import %s # PyInstaller PYZ", fullname)
  128. elif path is not None:
  129. # Try to handle module.__path__ modifications by the modules themselves.
  130. # Reverse the fake __path__ we added to the package module to a dotted module name, and add the tail module
  131. # from fullname onto that to synthesize a new fullname.
  132. modname = fullname.split('.')[-1]
  133. for p in path:
  134. if not p.startswith(SYS_PREFIX):
  135. continue
  136. p = p[SYS_PREFIXLEN:]
  137. parts = p.split(pyi_os_path.os_sep)
  138. if not parts:
  139. continue
  140. if not parts[0]:
  141. parts = parts[1:]
  142. parts.append(modname)
  143. entry_name = ".".join(parts)
  144. if entry_name in self.toc:
  145. module_loader = FrozenPackageImporter(self, entry_name)
  146. trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)", entry_name, fullname, p)
  147. break
  148. # Release the interpreter's import lock.
  149. if module_loader is None:
  150. trace("# %s not found in PYZ", fullname)
  151. return module_loader
  152. def load_module(self, fullname, entry_name=None):
  153. # Deprecated in Python 3.4, see PEP-451
  154. """
  155. PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.
  156. Return the loaded module (instance of imp_new_module()) or raise an exception, preferably ImportError if an
  157. existing exception is not being propagated.
  158. When called from FrozenPackageImporter, `entry_name` is the name of the module as it is stored in the archive.
  159. This module will be loaded and installed into sys.modules using `fullname` as its name.
  160. """
  161. # Acquire the interpreter's import lock.
  162. module = None
  163. if entry_name is None:
  164. entry_name = fullname
  165. try:
  166. # PEP302: if there is an existing module object named 'fullname' in sys.modules, the loader must use that
  167. # existing module.
  168. module = sys.modules.get(fullname)
  169. # Module not in sys.modules - load it and add it to sys.modules.
  170. if module is None:
  171. # Load code object from the bundled ZIP archive.
  172. is_pkg, bytecode = self._pyz_archive.extract(entry_name)
  173. # Create new empty 'module' object.
  174. module = imp_new_module(fullname)
  175. # TODO: replace bytecode.co_filename by something more meaningful:
  176. # e.g., /absolute/path/frozen_executable/path/to/module/module_name.pyc
  177. # Paths from developer machine are masked.
  178. # Set __file__ attribute of a module relative to the executable, so that data files can be found.
  179. module.__file__ = self.get_filename(entry_name)
  180. #-- Set __path__ if 'fullname' is a package.
  181. # Python has modules and packages. A Python package is a container for several modules or packages.
  182. if is_pkg:
  183. # If a module has a __path__ attribute, the import mechanism will treat it as a package.
  184. #
  185. # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the correct path where PyInstaller
  186. # should find bundled dynamic libraries. In one-file mode it points to the tmp directory where
  187. # bundled files are extracted at execution time.
  188. #
  189. # __path__ cannot be empty list because 'wx' module prepends something to it. It cannot contain
  190. # value 'sys.prefix' because 'xml.etree.cElementTree' fails otherwise.
  191. #
  192. # Set __path__ to point to 'sys.prefix/package/subpackage'.
  193. module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]
  194. #-- Set __loader__
  195. # The attribute __loader__ improves support for module 'pkg_resources' and enables the following
  196. # functions within the frozen app: pkg_resources.resource_string(), pkg_resources.resource_stream().
  197. module.__loader__ = self
  198. #-- Set __package__
  199. # Accoring to PEP302, this attribute must be set. When it is present, relative imports will be based
  200. # on this attribute rather than the module __name__ attribute. More details can be found in PEP366.
  201. # For ordinary modules, this is set like: 'aa.bb.cc.dd' -> 'aa.bb.cc'
  202. if is_pkg:
  203. module.__package__ = fullname
  204. else:
  205. module.__package__ = fullname.rsplit('.', 1)[0]
  206. #-- Set __spec__
  207. # Python 3.4 introduced module attribute __spec__ to consolidate all module attributes.
  208. module.__spec__ = _frozen_importlib.ModuleSpec(entry_name, self, is_package=is_pkg)
  209. #-- Add module object to sys.modules dictionary.
  210. # Module object must be in sys.modules before the loader executes the module code. This is crucial
  211. # because the module code may (directly or indirectly) import itself; adding it to sys.modules
  212. # beforehand prevents unbounded recursion in the worst case and multiple loading in the best.
  213. sys.modules[fullname] = module
  214. # Run the module code.
  215. exec(bytecode, module.__dict__)
  216. # Reread the module from sys.modules in case it has changed itself.
  217. module = sys.modules[fullname]
  218. except Exception:
  219. # Remove 'fullname' from sys.modules if it was appended there.
  220. if fullname in sys.modules:
  221. sys.modules.pop(fullname)
  222. # TODO: do we need to raise different types of Exceptions for better debugging?
  223. # PEP302 requires to raise ImportError exception.
  224. #raise ImportError("Can't load frozen module: %s" % fullname)
  225. raise
  226. # Module returned only in case of no exception.
  227. return module
  228. #-- Optional Extensions to the PEP-302 Importer Protocol --
  229. def is_package(self, fullname):
  230. if fullname in self.toc:
  231. try:
  232. return self._pyz_archive.is_package(fullname)
  233. except Exception as e:
  234. raise ImportError('Loader FrozenImporter cannot handle module ' + fullname) from e
  235. else:
  236. raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)
  237. def get_code(self, fullname):
  238. """
  239. Get the code object associated with the module.
  240. ImportError should be raised if module not found.
  241. """
  242. try:
  243. if fullname == '__main__':
  244. # Special handling for __main__ module; the bootloader should store code object to _pyi_main_co
  245. # attribute of the module.
  246. return sys.modules['__main__']._pyi_main_co
  247. # extract() returns None if fullname is not in the archive, and the subsequent subscription attempt raises
  248. # exception, which is turned into ImportError.
  249. return self._pyz_archive.extract(fullname)[1]
  250. except Exception as e:
  251. raise ImportError('Loader FrozenImporter cannot handle module ' + fullname) from e
  252. def get_source(self, fullname):
  253. """
  254. Method should return the source code for the module as a string.
  255. But frozen modules does not contain source code.
  256. Return None, unless the corresponding source file was explicitly collected to the filesystem.
  257. """
  258. if fullname in self.toc:
  259. # Try loading the .py file from the filesystem (only for collected modules)
  260. if self.is_package(fullname):
  261. fullname += '.__init__'
  262. filename = pyi_os_path.os_path_join(SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep) + '.py')
  263. try:
  264. # Read in binary mode, then decode
  265. with open(filename, 'rb') as fp:
  266. source_bytes = fp.read()
  267. return _decode_source(source_bytes)
  268. except FileNotFoundError:
  269. pass
  270. return None
  271. else:
  272. # ImportError should be raised if module not found.
  273. raise ImportError('No module named ' + fullname)
  274. def get_data(self, path):
  275. """
  276. Returns the data as a string, or raises IOError if the "file" was not found. The data is always returned as if
  277. "binary" mode was used.
  278. This method is useful for getting resources with 'pkg_resources' that are bundled with Python modules in the
  279. PYZ archive.
  280. The 'path' argument is a path that can be constructed by munging module.__file__ (or pkg.__path__ items).
  281. """
  282. assert path.startswith(SYS_PREFIX)
  283. fullname = path[SYS_PREFIXLEN:]
  284. if fullname in self.toc:
  285. # If the file is in the archive, return this
  286. return self._pyz_archive.extract(fullname)[1]
  287. else:
  288. # Otherwise try to fetch it from the filesystem. Since __file__ attribute works properly, just try to open
  289. # and read it.
  290. with open(path, 'rb') as fp:
  291. return fp.read()
  292. def get_filename(self, fullname):
  293. """
  294. This method should return the value that __file__ would be set to if the named module was loaded. If the module
  295. is not found, an ImportError should be raised.
  296. """
  297. # The absolute absolute path to the executable is taken from sys.prefix. In onefile mode it points to the temp
  298. # directory where files are unpacked by PyInstaller. Then, append the appropriate suffix (__init__.pyc for a
  299. # package, or just .pyc for a module).
  300. # Method is_package() will raise ImportError if module not found.
  301. if self.is_package(fullname):
  302. filename = pyi_os_path.os_path_join(
  303. pyi_os_path.os_path_join(SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep)), '__init__.pyc'
  304. )
  305. else:
  306. filename = pyi_os_path.os_path_join(SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
  307. return filename
  308. def find_spec(self, fullname, path=None, target=None):
  309. """
  310. PEP-451 finder.find_spec() method for the ``sys.meta_path`` hook.
  311. fullname fully qualified name of the module
  312. path None for a top-level module, or package.__path__ for
  313. submodules or subpackages.
  314. target unused by this Finder
  315. Finders are still responsible for identifying, and typically creating, the loader that should be used to load a
  316. module. That loader will now be stored in the module spec returned by find_spec() rather than returned directly.
  317. As is currently the case without the PEP-452, if a loader would be costly to create, that loader can be designed
  318. to defer the cost until later.
  319. Finders must return ModuleSpec objects when find_spec() is called. This new method replaces find_module() and
  320. find_loader() (in the PathEntryFinder case). If a loader does not have find_spec(), find_module() and
  321. find_loader() are used instead, for backward-compatibility.
  322. """
  323. entry_name = None # None means - no module found in this importer.
  324. if fullname in self.toc:
  325. entry_name = fullname
  326. trace("import %s # PyInstaller PYZ", fullname)
  327. elif path is not None:
  328. # Try to handle module.__path__ modifications by the modules themselves.
  329. # Reverse the fake __path__ we added to the package module into a dotted module name, and add the tail
  330. # module from fullname onto that to synthesize a new fullname.
  331. modname = fullname.rsplit('.')[-1]
  332. for p in path:
  333. if not p.startswith(SYS_PREFIX):
  334. continue
  335. p = p[SYS_PREFIXLEN:]
  336. parts = p.split(pyi_os_path.os_sep)
  337. if not parts:
  338. continue
  339. if not parts[0]:
  340. parts = parts[1:]
  341. parts.append(modname)
  342. entry_name = ".".join(parts)
  343. if entry_name in self.toc:
  344. trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)", entry_name, fullname, p)
  345. break
  346. else:
  347. entry_name = None
  348. if entry_name is None:
  349. trace("# %s not found in PYZ", fullname)
  350. return None
  351. if self._is_pep420_namespace_package(entry_name):
  352. # PEP-420 namespace package; as per PEP 451, we need to return a spec with "loader" set to None
  353. # (a.k.a. not set)
  354. spec = _frozen_importlib.ModuleSpec(fullname, None, is_package=True)
  355. # Set submodule_search_locations, which seems to fill the __path__ attribute.
  356. spec.submodule_search_locations = [pyi_os_path.os_path_dirname(self.get_filename(entry_name))]
  357. return spec
  358. # origin has to be the filename
  359. origin = self.get_filename(entry_name)
  360. is_pkg = self.is_package(entry_name)
  361. spec = _frozen_importlib.ModuleSpec(
  362. fullname,
  363. self,
  364. is_package=is_pkg,
  365. origin=origin,
  366. # Provide the entry_name for the loader to use during loading.
  367. loader_state=entry_name
  368. )
  369. # Make the import machinery set __file__.
  370. # PEP 451 says: "has_location" is true if the module is locatable. In that case the spec's origin is used
  371. # as the location and __file__ is set to spec.origin. If additional location information is required
  372. # (e.g., zipimport), that information may be stored in spec.loader_state.
  373. spec.has_location = True
  374. # Set submodule_search_locations for packages. Seems to be required for importlib_resources from 3.2.0;
  375. # see issue #5395.
  376. if is_pkg:
  377. spec.submodule_search_locations = [pyi_os_path.os_path_dirname(self.get_filename(entry_name))]
  378. return spec
  379. def create_module(self, spec):
  380. """
  381. PEP-451 loader.create_module() method for the ``sys.meta_path`` hook.
  382. Loaders may also implement create_module() that will return a new module to exec. It may return None to indicate
  383. that the default module creation code should be used. One use case, though atypical, for create_module() is to
  384. provide a module that is a subclass of the builtin module type. Most loaders will not need to implement
  385. create_module().
  386. create_module() should properly handle the case where it is called more than once for the same spec/module. This
  387. may include returning None or raising ImportError.
  388. """
  389. # Contrary to what is defined in PEP-451, this method is not optional. We want the default results, so we simply
  390. # return None (which is handled for su my the import machinery).
  391. # See https://bugs.python.org/issue23014 for more information.
  392. return None
  393. def exec_module(self, module):
  394. """
  395. PEP-451 loader.exec_module() method for the ``sys.meta_path`` hook.
  396. Loaders will have a new method, exec_module(). Its only job is to "exec" the module and consequently populate
  397. the module's namespace. It is not responsible for creating or preparing the module object, nor for any cleanup
  398. afterward. It has no return value. exec_module() will be used during both loading and reloading.
  399. exec_module() should properly handle the case where it is called more than once. For some kinds of modules this
  400. may mean raising ImportError every time after the first time the method is called. This is particularly relevant
  401. for reloading, where some kinds of modules do not support in-place reloading.
  402. """
  403. spec = module.__spec__
  404. bytecode = self.get_code(spec.loader_state)
  405. # Set by the import machinery
  406. assert hasattr(module, '__file__')
  407. # If `submodule_search_locations` is not None, this is a package; set __path__.
  408. if spec.submodule_search_locations is not None:
  409. # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the correct path where PyInstaller should
  410. # find bundled dynamic libraries. In one-file mode it points to the tmp directory where bundled files are
  411. # extracted at execution time.
  412. #
  413. # __path__ cannot be empty list because 'wx' module prepends something to it. It cannot contain value
  414. # 'sys.prefix' because 'xml.etree.cElementTree' fails otherwise.
  415. #
  416. # Set __path__ to point to 'sys.prefix/package/subpackage'.
  417. module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]
  418. exec(bytecode, module.__dict__)
  419. def get_resource_reader(self, fullname):
  420. """
  421. Return importlib.resource-compatible resource reader.
  422. """
  423. return FrozenResourceReader(self, fullname)
  424. class FrozenResourceReader:
  425. """
  426. Resource reader for importlib.resources / importlib_resources support.
  427. Currently supports only on-disk resources (support for resources from the embedded archive is missing).
  428. However, this should cover the typical use cases (access to data files), as PyInstaller collects data files onto
  429. filesystem, and only .pyc modules are collected into embedded archive. One exception are resources collected from
  430. zipped eggs (which end up collected into embedded archive), but those should be rare anyway.
  431. When listing resources, source .py files will not be listed as they are not collected by default. Similarly,
  432. sub-directories that contained only .py files are not reconstructed on filesystem, so they will not be listed,
  433. either. If access to .py files is required for whatever reason, they need to be explicitly collected as data files
  434. anyway, which will place them on filesystem and make them appear as resources.
  435. For on-disk resources, we *must* return path compatible with pathlib.Path() in order to avoid copy to a temporary
  436. file, which might break under some circumstances, e.g., metpy with importlib_resources back-port, due to:
  437. https://github.com/Unidata/MetPy/blob/a3424de66a44bf3a92b0dcacf4dff82ad7b86712/src/metpy/plots/wx_symbols.py#L24-L25
  438. (importlib_resources tries to use 'fonts/wx_symbols.ttf' as a temporary filename suffix, which fails as it contains
  439. a separator).
  440. Furthermore, some packages expect files() to return either pathlib.Path or zipfile.Path, e.g.,
  441. https://github.com/tensorflow/datasets/blob/master/tensorflow_datasets/core/utils/resource_utils.py#L81-L97
  442. This makes implementation of mixed support for on-disk and embedded resources using importlib.abc.Traversable
  443. protocol rather difficult.
  444. So in order to maximize compatibility with unfrozen behavior, the below implementation is basically equivalent of
  445. importlib.readers.FileReader from python 3.10:
  446. https://github.com/python/cpython/blob/839d7893943782ee803536a47f1d4de160314f85/Lib/importlib/readers.py#L11
  447. and its underlying classes, importlib.abc.TraversableResources and importlib.abc.ResourceReader:
  448. https://github.com/python/cpython/blob/839d7893943782ee803536a47f1d4de160314f85/Lib/importlib/abc.py#L422
  449. https://github.com/python/cpython/blob/839d7893943782ee803536a47f1d4de160314f85/Lib/importlib/abc.py#L312
  450. """
  451. def __init__(self, importer, name):
  452. import pathlib # Local import to avoid bootstrap issues.
  453. self.importer = importer
  454. self.path = pathlib.Path(sys._MEIPASS).joinpath(*name.split('.'))
  455. def open_resource(self, resource):
  456. return self.files().joinpath(resource).open('rb')
  457. def resource_path(self, resource):
  458. return str(self.path.joinpath(resource))
  459. def is_resource(self, path):
  460. return self.files().joinpath(path).is_file()
  461. def contents(self):
  462. return (item.name for item in self.files().iterdir())
  463. def files(self):
  464. return self.path
  465. def install():
  466. """
  467. Install FrozenImporter class and other classes into the import machinery.
  468. This function installs the FrozenImporter class into the import machinery of the running process. The importer is
  469. added to sys.meta_path. It could be added to sys.path_hooks, but sys.meta_path is processed by Python before
  470. looking at sys.path!
  471. The order of processing import hooks in sys.meta_path:
  472. 1. built-in modules
  473. 2. modules from the bundled ZIP archive
  474. 3. C extension modules
  475. 4. Modules from sys.path
  476. """
  477. # Ensure Python looks in the bundled zip archive for modules before any other places.
  478. fimp = FrozenImporter()
  479. sys.meta_path.append(fimp)
  480. # On Windows there is importer _frozen_importlib.WindowsRegistryFinder that looks for Python modules in Windows
  481. # registry. The frozen executable should not look for anything in the Windows registry. Remove this importer
  482. # from sys.meta_path.
  483. for item in sys.meta_path:
  484. if hasattr(item, '__name__') and item.__name__ == 'WindowsRegistryFinder':
  485. sys.meta_path.remove(item)
  486. break
  487. # _frozen_importlib.PathFinder is also able to handle Python C extensions. However, PyInstaller needs its own
  488. # importer as it uses extension names like 'module.submodle.so' (instead of paths). As of Python 3.7.0b2, there
  489. # are several PathFinder instances (and duplicate ones) on sys.meta_path. This propobly is a bug, see
  490. # https://bugs.python.org/issue33128. Thus we need to move all of them to the end, and eliminate the duplicates.
  491. pathFinders = []
  492. for item in reversed(sys.meta_path):
  493. if getattr(item, '__name__', None) == 'PathFinder':
  494. sys.meta_path.remove(item)
  495. if item not in pathFinders:
  496. pathFinders.append(item)
  497. sys.meta_path.extend(reversed(pathFinders))
  498. # TODO: do we need _frozen_importlib.FrozenImporter in Python 3? Could it be also removed?
  499. # Set the FrozenImporter as loader for __main__, in order for python to treat __main__ as a module instead of
  500. # a built-in.
  501. try:
  502. sys.modules['__main__'].__loader__ = fimp
  503. except Exception:
  504. pass