示例#1
0
class FrozenImporter(object):
    """
    Load bytecode of Python modules from the executable created by PyInstaller.

    Python bytecode is zipped and appended to the executable.

    NOTE: PYZ format cannot be replaced by zipimport module.

    The problem is that we have no control over zipimport; for instance,
    it doesn't work if the zip file is embedded into a PKG appended
    to an executable, like we create in one-file.

    This is PEP-302 finder and loader class for the ``sys.meta_path`` hook.
    A PEP-302 finder requires method find_module() to return loader
    class with method load_module(). Both these methods are implemented
    in one class.

    This is also a PEP-451 finder and loader class for the ModuleSpec type
    import system. A PEP-451 finder requires method find_spec(), a PEP-451
    loader requires methods exec_module(), load_module(9 and (optionally)
    create_module(). All these methods are implemented in this one class.

    To use this class just call

        FrozenImporter.install()
    """
    def __init__(self):
        """
        Load, unzip and initialize the Zip archive bundled with the executable.
        """
        # Examine all items in sys.path and the one like /path/executable_name?117568
        # is the correct executable with bundled zip archive. Use this value
        # for the ZlibArchiveReader class and remove this item from sys.path.
        # It was needed only for FrozenImporter class. Wrong path from sys.path
        # Raises ArchiveReadError exception.
        for pyz_filepath in sys.path:
            # We need to acquire the interpreter's import lock here
            # because ZlibArchiveReader() seeks through and reads from the
            # zip archive.
            imp_lock()
            try:
                # Unzip zip archive bundled with the executable.
                self._pyz_archive = ZlibArchiveReader(pyz_filepath)
                # Verify the integrity of the zip archive with Python modules.
                # This is already done when creating the ZlibArchiveReader instance.
                #self._pyz_archive.checkmagic()

                # End this method since no Exception was raised we can assume
                # ZlibArchiveReader was successfully loaded. Let's remove 'pyz_filepath'
                # from sys.path.
                sys.path.remove(pyz_filepath)
                # Some runtime hook might need access to the list of available
                # frozen module. Let's make them accessible as a set().
                self.toc = set(self._pyz_archive.toc.keys())
                # Return - no error was raised.
                trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath)
                return
            except IOError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            except ArchiveReadError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            finally:
                imp_unlock()
        # sys.path does not contain filename of executable with bundled zip archive.
        # Raise import error.
        raise ImportError("Can't load frozen modules.")

    def __call__(self, path):
        """
        PEP-302 sys.path_hook processor. is_py2: This is only needed for Python
        2; see comments at `path_hook installation`_.

        sys.path_hook is a list of callables, which will be checked in
        sequence to determine if they can handle a given path item.
        """

        if path.startswith(SYS_PREFIX):
            fullname = path[SYS_PREFIXLEN + 1:].replace(
                pyi_os_path.os_sep, '.')
            loader = self.find_module(fullname)
            if loader is not None:
                return loader
        raise ImportError(path)

    def find_module(self, fullname, path=None):
        # Deprecated in Python 3.4, see PEP-451
        """
        PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__
                     for submodules or subpackages.

        Return a loader object if the module was found, or None if it wasn't.
        If find_module() raises an exception, it will be propagated to the
        caller, aborting the import.
        """

        # Acquire the interpreter's import lock for the current thread. This
        # lock should be used by import hooks to ensure thread-safety when
        # importing modules.

        imp_lock()
        module_loader = None  # None means - no module found in this importer.

        if fullname in self.toc:
            # Tell the import machinery to use self.load_module() to load the module.
            module_loader = self
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.split('.')[-1]

            for p in path:
                p = p[SYS_PREFIXLEN + 1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts: continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = ".".join(parts)
                if entry_name in self.toc:
                    module_loader = FrozenPackageImporter(self, entry_name)
                    trace(
                        "import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                        entry_name, fullname, p)
                    break
        # Release the interpreter's import lock.
        imp_unlock()
        if module_loader is None:
            trace("# %s not found in PYZ", fullname)
        return module_loader

    def load_module(self, fullname, entry_name=None):
        # Deprecated in Python 3.4, see PEP-451
        """
        PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.

        Return the loaded module (instance of imp_new_module()) or raises
        an exception, preferably ImportError if an existing exception
        is not being propagated.

        When called from FrozenPackageImporter, `entry_name` is the name of the
        module as it is stored in the archive. This module will be loaded and installed
        into sys.modules using `fullname` as its name
        """
        # Acquire the interpreter's import lock.
        imp_lock()
        module = None
        if entry_name is None:
            entry_name = fullname
        try:
            # PEP302 If there is an existing module object named 'fullname'
            # in sys.modules, the loader must use that existing module.
            module = sys.modules.get(fullname)

            # Module not in sys.modules - load it and it to sys.modules.
            if module is None:
                # Load code object from the bundled ZIP archive.
                is_pkg, bytecode = self._pyz_archive.extract(entry_name)
                # Create new empty 'module' object.
                module = imp_new_module(fullname)

                # TODO Replace bytecode.co_filename by something more meaningful:
                # e.g. /absolute/path/frozen_executable/path/to/module/module_name.pyc
                # Paths from developer machine are masked.

                # Set __file__ attribute of a module relative to the
                # executable so that data files can be found.
                module.__file__ = self.get_filename(entry_name)

                ### Set __path__  if 'fullname' is a package.
                # Python has modules and packages. A Python package is container
                # for several modules or packages.
                if is_pkg:

                    # If a module has a __path__ attribute, the import mechanism
                    # will treat it as a package.
                    #
                    # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the
                    # correct path where PyInstaller should find bundled dynamic
                    # libraries. In one-file mode it points to the tmp directory where
                    # bundled files are extracted at execution time.
                    #
                    # __path__ cannot be empty list because 'wx' module prepends something to it.
                    # It cannot contain value 'sys.prefix' because 'xml.etree.cElementTree' fails
                    # Otherwise.
                    #
                    # Set __path__ to point to 'sys.prefix/package/subpackage'.
                    module.__path__ = [
                        pyi_os_path.os_path_dirname(module.__file__)
                    ]

                ### Set __loader__
                # The attribute __loader__ improves support for module 'pkg_resources' and
                # with the frozen apps the following functions are working:
                # pkg_resources.resource_string(), pkg_resources.resource_stream().
                module.__loader__ = self

                ### Set __package__
                # Accoring to PEP302 this attribute must be set.
                # When it is present, relative imports will be based on this
                # attribute rather than the module __name__ attribute.
                # More details can be found in PEP366.
                # For ordinary modules this is set like:
                #     'aa.bb.cc.dd'  ->  'aa.bb.cc'
                if is_pkg:
                    module.__package__ = fullname
                else:
                    module.__package__ = fullname.rsplit('.', 1)[0]

                ### Set __spec__ for Python 3.4+
                # In Python 3.4 was introduced module attribute __spec__ to
                # consolidate all module attributes.
                if sys.version_info[0:2] > (3, 3):
                    module.__spec__ = _frozen_importlib.ModuleSpec(
                        entry_name, self, is_package=is_pkg)

                ### Add module object to sys.modules dictionary.
                # Module object must be in sys.modules before the loader
                # executes the module code. This is crucial because the module
                # code may (directly or indirectly) import itself; adding it
                # to sys.modules beforehand prevents unbounded recursion in the
                # worst case and multiple loading in the best.
                sys.modules[fullname] = module

                # Run the module code.
                exec(bytecode, module.__dict__)
                # Reread the module from sys.modules in case it's changed itself
                module = sys.modules[fullname]

        except Exception:
            # Remove 'fullname' from sys.modules if it was appended there.
            if fullname in sys.modules:
                sys.modules.pop(fullname)
            # TODO Do we need to raise different types of Exceptions for better debugging?
            # PEP302 requires to raise ImportError exception.
            #raise ImportError("Can't load frozen module: %s" % fullname)

            raise

        finally:
            # Release the interpreter's import lock.
            imp_unlock()

        # Module returned only in case of no exception.
        return module

    ### Optional Extensions to the PEP-302 Importer Protocol

    def is_package(self, fullname):
        if fullname in self.toc:
            try:
                return self._pyz_archive.is_package(fullname)
            except Exception:
                raise ImportError(
                    'Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_code(self, fullname):
        """
        Get the code object associated with the module.

        ImportError should be raised if module not found.
        """
        try:
            # extract() returns None if fullname not in the archive, thus the
            # next line will raise an execpion which will be catched just
            # below and raise the ImportError.
            return self._pyz_archive.extract(fullname)[1]
        except:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_source(self, fullname):
        """
        Method should return the source code for the module as a string.
        But frozen modules does not contain source code.

        Return None.
        """
        if fullname in self.toc:
            return None
        else:
            # ImportError should be raised if module not found.
            raise ImportError('No module named ' + fullname)

    def get_data(self, path):
        """
        This returns the data as a string, or raise IOError if the "file"
        wasn't found. The data is always returned as if "binary" mode was used.

        This method is useful getting resources with 'pkg_resources' that are
        bundled with Python modules in the PYZ archive.

        The 'path' argument is a path that can be constructed by munging
        module.__file__ (or pkg.__path__ items)
        """
        assert path.startswith(SYS_PREFIX + pyi_os_path.os_sep)
        fullname = path[SYS_PREFIXLEN + 1:]
        if fullname in self.toc:
            # If the file is in the archive, return this
            return self._pyz_archive.extract(fullname)[1]
        else:
            # Otherwise try to fetch it from the filesystem. Since
            # __file__ attribute works properly just try to open and
            # read it.
            with open(path, 'rb') as fp:
                return fp.read()

    def get_filename(self, fullname):
        """
        This method should return the value that __file__ would be set to
        if the named module was loaded. If the module is not found, then
        ImportError should be raised.
        """
        # The absolute absolute path to the executable is taken from
        # sys.prefix. In onefile mode it points to the temp directory where
        # files are unpacked by PyInstaller. Then, append the appropriate
        # suffix (__init__.pyc for a package, or just .pyc for a module).
        # Method is_package() will raise ImportError if module not found.
        if self.is_package(fullname):
            filename = pyi_os_path.os_path_join(
                pyi_os_path.os_path_join(
                    SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep)),
                '__init__.pyc')
        else:
            filename = pyi_os_path.os_path_join(
                SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
        return filename

    def find_spec(self, fullname, path=None, target=None):
        """
        PEP-451 finder.find_spec() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__ for
                     submodules or subpackages.
        target       unused by this Finder

        Finders are still responsible for identifying, and typically creating,
        the loader that should be used to load a module. That loader will now
        be stored in the module spec returned by find_spec() rather than
        returned directly. As is currently the case without the PEP-452, if a
        loader would be costly to create, that loader can be designed to defer
        the cost until later.

        Finders must return ModuleSpec objects when find_spec() is called.
        This new method replaces find_module() and find_loader() (in the
        PathEntryFinder case). If a loader does not have find_spec(),
        find_module() and find_loader() are used instead, for
        backward-compatibility.
        """
        entry_name = None  # None means - no module found in this importer.

        if fullname in self.toc:
            entry_name = fullname
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.rsplit('.')[-1]

            for p in path:
                p = p[SYS_PREFIXLEN + 1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts: continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = ".".join(parts)
                if entry_name in self.toc:
                    trace(
                        "import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                        entry_name, fullname, p)
                    break
            else:
                entry_name = None

        if entry_name is None:
            trace("# %s not found in PYZ", fullname)
            return None

        # origin has to be the filename
        origin = self.get_filename(entry_name)
        is_pkg = self.is_package(entry_name)

        spec = _frozen_importlib.ModuleSpec(
            fullname,
            self,
            is_package=is_pkg,
            origin=origin,
            # Provide the entry_name for the loader to use during loading
            loader_state=entry_name)

        # Make the import machinery set __file__.
        # PEP 451 says: "has_location" is true if the module is locatable. In
        # that case the spec's origin is used as the location and __file__ is
        # set to spec.origin. If additional location information is required
        # (e.g. zipimport), that information may be stored in
        # spec.loader_state.
        spec.has_location = True
        return spec

    def create_module(self, spec):
        """
        PEP-451 loader.create_module() method for the ``sys.meta_path`` hook.

        Loaders may also implement create_module() that will return a new
        module to exec. It may return None to indicate that the default module
        creation code should be used. One use case, though atypical, for
        create_module() is to provide a module that is a subclass of the
        builtin module type. Most loaders will not need to implement
        create_module(),

        create_module() should properly handle the case where it is called
        more than once for the same spec/module. This may include returning
        None or raising ImportError.
        """
        # Opposed to what is defined in PEP-451, this method is not optional.
        # We want the default results, so we simply return None (which is
        # handled for su my the import machinery). See
        # https://bugs.python.org/issue23014 for more information.
        return None

    def exec_module(self, module):
        """
        PEP-451 loader.exec_module() method for the ``sys.meta_path`` hook.

        Loaders will have a new method, exec_module(). Its only job is to
        "exec" the module and consequently populate the module's namespace. It
        is not responsible for creating or preparing the module object, nor
        for any cleanup afterward. It has no return value. exec_module() will
        be used during both loading and reloading.

        exec_module() should properly handle the case where it is called more
        than once. For some kinds of modules this may mean raising ImportError
        every time after the first time the method is called. This is
        particularly relevant for reloading, where some kinds of modules do
        not support in-place reloading.
        """
        spec = module.__spec__
        bytecode = self.get_code(spec.loader_state)

        # Set by the import machinery
        assert hasattr(module, '__file__')

        # If `submodule_search_locations` is not None, this is a package;
        # set __path__.
        if spec.submodule_search_locations is not None:
            # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to
            # the correct path where PyInstaller should find bundled dynamic
            # libraries. In one-file mode it points to the tmp directory where
            # bundled files are extracted at execution time.
            #
            # __path__ cannot be empty list because 'wx' module prepends
            # something to it. It cannot contain value 'sys.prefix' because
            # 'xml.etree.cElementTree' fails otherwise.
            #
            # Set __path__ to point to 'sys.prefix/package/subpackage'.
            module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]

        exec(bytecode, module.__dict__)
示例#2
0
class FrozenImporter(object):
    """
    Load bytecode of Python modules from the executable created by PyInstaller.

    Python bytecode is zipped and appended to the executable.

    NOTE: PYZ format cannot be replaced by zipimport module.

    The problem is that we have no control over zipimport; for instance,
    it doesn't work if the zip file is embedded into a PKG appended
    to an executable, like we create in one-file.

    This is PEP-302 finder and loader class for the ``sys.meta_path`` hook.
    A PEP-302 finder requires method find_module() to return loader
    class with method load_module(). Both these methods are implemented
    in one class.


    To use this class just call

        FrozenImporter.install()
    """
    def __init__(self):
        """
        Load, unzip and initialize the Zip archive bundled with the executable.
        """
        # Examine all items in sys.path and the one like /path/executable_name?117568
        # is the correct executable with bundled zip archive. Use this value
        # for the ZlibArchiveReader class and remove this item from sys.path.
        # It was needed only for FrozenImporter class. Wrong path from sys.path
        # Raises ArchiveReadError exception.
        for pyz_filepath in sys.path:
            # We need to acquire the interpreter's import lock here
            # because ZlibArchiveReader() seeks through and reads from the
            # zip archive.
            imp_lock()
            try:
                # Unzip zip archive bundled with the executable.
                self._pyz_archive = ZlibArchiveReader(pyz_filepath)
                # Verify the integrity of the zip archive with Python modules.
                # This is already done when creating the ZlibArchiveReader instance.
                #self._pyz_archive.checkmagic()

                # End this method since no Exception was raised we can assume
                # ZlibArchiveReader was successfully loaded. Let's remove 'pyz_filepath'
                # from sys.path.
                sys.path.remove(pyz_filepath)
                # Some runtime hook might need access to the list of available
                # frozen module. Let's make them accessible as a set().
                self.toc = set(self._pyz_archive.toc.keys())
                # Return - no error was raised.
                trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath)
                return
            except IOError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            except ArchiveReadError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            finally:
                imp_unlock()
        # sys.path does not contain filename of executable with bundled zip archive.
        # Raise import error.
        raise ImportError("Can't load frozen modules.")

    def __call__(self, path):
        """
        PEP-302 sys.path_hook processor.

        sys.path_hook is a list of callables, which will be checked in
        sequence to determine if they can handle a given path item.
        """

        if path.startswith(SYS_PREFIX):
            fullname = path[SYS_PREFIXLEN + 1:].replace(
                pyi_os_path.os_sep, '.')
            loader = self.find_module(fullname)
            if loader is not None:
                return loader
        raise ImportError(path)

    def find_module(self, fullname, path=None):
        """
        PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__ for submodules or subpackages.

        Return a loader object if the module was found, or None if it wasn't. If find_module() raises
        an exception, it will be propagated to the caller, aborting the import.
        """
        # Acquire the interpreter's import lock for the current thread. This
        # lock should be used by import hooks to ensure thread-safety when
        # importing modules.

        imp_lock()
        module_loader = None  # None means - no module found in this importer.

        if fullname in self.toc:
            # Tell the import machinery to use self.load_module() to load the module.
            module_loader = self
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.split('.')[-1]

            for p in path:
                p = p.replace(SYS_PREFIX, "")
                parts = p.split(pyi_os_path.os_sep)
                if not len(parts): continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                real_fullname = ".".join(parts)
                if real_fullname in self.toc:
                    module_loader = FrozenPackageImporter(self, real_fullname)
                    trace(
                        "import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                        real_fullname, fullname, p)
                    break
        # Release the interpreter's import lock.
        imp_unlock()
        if module_loader is None:
            trace("# %s not found in PYZ", fullname)
        return module_loader

    def load_module(self, fullname, real_fullname=None):
        """
        PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.

        Return the loaded module (instance of imp_new_module()) or raises
        an exception, preferably ImportError if an existing exception
        is not being propagated.

        When called from FrozenPackageImporter, `real_fullname` is the name of the
        module as it is stored in the archive. This module will be loaded and installed
        into sys.modules using `fullname` as its name
        """
        # Acquire the interpreter's import lock.
        imp_lock()
        module = None
        if real_fullname is None:
            real_fullname = fullname
        try:
            # PEP302 If there is an existing module object named 'fullname'
            # in sys.modules, the loader must use that existing module.
            module = sys.modules.get(fullname)

            # Module not in sys.modules - load it and it to sys.modules.
            if module is None:
                # Load code object from the bundled ZIP archive.
                is_pkg, bytecode = self._pyz_archive.extract(real_fullname)
                # Create new empty 'module' object.
                module = imp_new_module(fullname)

                # TODO Replace bytecode.co_filename by something more meaningful:
                # e.g. /absolute/path/frozen_executable/path/to/module/module_name.pyc
                # Paths from developer machine are masked.

                ### Set __file__ attribute of a module relative to the executable
                # so that data files can be found. The absolute absolute path
                # to the executable is taken from sys.prefix. In onefile mode it
                # points to the temp directory where files are unpacked by PyInstaller.
                # Then, append the appropriate suffix (__init__.pyc for a package, or just .pyc for a module).
                if is_pkg:
                    module.__file__ = pyi_os_path.os_path_join(
                        pyi_os_path.os_path_join(
                            SYS_PREFIX,
                            fullname.replace('.', pyi_os_path.os_sep)),
                        '__init__.pyc')
                else:
                    module.__file__ = pyi_os_path.os_path_join(
                        SYS_PREFIX,
                        fullname.replace('.', pyi_os_path.os_sep) + '.pyc')

                ### Set __path__  if 'fullname' is a package.
                # Python has modules and packages. A Python package is container
                # for several modules or packages.
                if is_pkg:

                    # If a module has a __path__ attribute, the import mechanism
                    # will treat it as a package.
                    #
                    # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the
                    # correct path where PyInstaller should find bundled dynamic
                    # libraries. In one-file mode it points to the tmp directory where
                    # bundled files are extracted at execution time.
                    #
                    # __path__ cannot be empty list because 'wx' module prepends something to it.
                    # It cannot contain value 'sys.prefix' because 'xml.etree.cElementTree' fails
                    # Otherwise.
                    #
                    # Set __path__ to point to 'sys.prefix/package/subpackage'.
                    module.__path__ = [
                        pyi_os_path.os_path_dirname(module.__file__)
                    ]

                ### Set __loader__
                # The attribute __loader__ improves support for module 'pkg_resources' and
                # with the frozen apps the following functions are working:
                # pkg_resources.resource_string(), pkg_resources.resource_stream().
                module.__loader__ = self

                ### Set __package__
                # Accoring to PEP302 this attribute must be set.
                # When it is present, relative imports will be based on this
                # attribute rather than the module __name__ attribute.
                # More details can be found in PEP366.
                # For ordinary modules this is set like:
                #     'aa.bb.cc.dd'  ->  'aa.bb.cc'
                if is_pkg:
                    module.__package__ = fullname
                else:
                    module.__package__ = fullname.rsplit('.', 1)[0]

                ### Set __spec__ for Python 3.4+
                # In Python 3.4 was introduced module attribute __spec__ to
                # consolidate all module attributes.
                if sys.version_info[0:2] > (3, 3):
                    module.__spec__ = _frozen_importlib.ModuleSpec(
                        real_fullname, self, is_package=is_pkg)

                ### Add module object to sys.modules dictionary.
                # Module object must be in sys.modules before the loader
                # executes the module code. This is crucial because the module
                # code may (directly or indirectly) import itself; adding it
                # to sys.modules beforehand prevents unbounded recursion in the
                # worst case and multiple loading in the best.
                sys.modules[fullname] = module

                # Run the module code.
                exec(bytecode, module.__dict__)
                # Reread the module from sys.modules in case it's changed itself
                module = sys.modules[fullname]

        except Exception:
            # Remove 'fullname' from sys.modules if it was appended there.
            if fullname in sys.modules:
                sys.modules.pop(fullname)
            # TODO Do we need to raise different types of Exceptions for better debugging?
            # PEP302 requires to raise ImportError exception.
            #raise ImportError("Can't load frozen module: %s" % fullname)

            raise

        finally:
            # Release the interpreter's import lock.
            imp_unlock()

        # Module returned only in case of no exception.
        return module

    ### Optional Extensions to the PEP-302 Importer Protocol

    def is_package(self, fullname):
        """
        Return always False since built-in modules are never packages.
        """
        if fullname in self.toc:
            try:
                is_pkg, bytecode = self._pyz_archive.extract(fullname)
                return bool(is_pkg)
            except Exception:
                raise ImportError(
                    'Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_code(self, fullname):
        """
        Get the code object associated with the module.

        ImportError should be raised if module not found.
        """
        if fullname in self.toc:
            try:
                is_pkg, bytecode = self._pyz_archive.extract(fullname)
                return bytecode
            except Exception:
                raise ImportError(
                    'Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_source(self, fullname):
        """
        Method should return the source code for the module as a string.
        But frozen modules does not contain source code.

        Return None.
        """
        if fullname in self.toc:
            return None
        else:
            # ImportError should be raised if module not found.
            raise ImportError('No module named ' + fullname)

    def get_data(self, path):
        """
        This returns the data as a string, or raise IOError if the "file"
        wasn't found. The data is always returned as if "binary" mode was used.

        This method is useful getting resources with 'pkg_resources' that are
        bundled with Python modules in the PYZ archive.

        The 'path' argument is a path that can be constructed by munging
        module.__file__ (or pkg.__path__ items)
        """
        assert path.startswith(SYS_PREFIX + pyi_os_path.os_sep)
        fullname = path[len(SYS_PREFIX) + 1:]
        if fullname in self.toc:
            # If the file is in the archive, return this
            return self._pyz_archive.extract(fullname)[1]
        else:
            # Otherwise try to fetch it from the filesystem. Since
            # __file__ attribute works properly just try to open and
            # read it.
            with open(path, 'rb') as fp:
                return fp.read()

    # TODO Do we really need to implement this method?
    def get_filename(self, fullname):
        """
        This method should return the value that __file__ would be set to
        if the named module was loaded. If the module is not found, then
        ImportError should be raised.
        """
        # Then, append the appropriate suffix (__init__.pyc for a package, or just .pyc for a module).
        # Method is_package() will raise ImportError if module not found.
        if self.is_package(fullname):
            filename = pyi_os_path.os_path_join(
                pyi_os_path.os_path_join(
                    SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep)),
                '__init__.pyc')
        else:
            filename = pyi_os_path.os_path_join(
                SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
        return filename
class FrozenImporter(object):
    """
    Load bytecode of Python modules from the executable created by PyInstaller.

    Python bytecode is zipped and appended to the executable.

    NOTE: PYZ format cannot be replaced by zipimport module.

    The problem is that we have no control over zipimport; for instance,
    it doesn't work if the zip file is embedded into a PKG appended
    to an executable, like we create in one-file.

    This is PEP-302 finder and loader class for the ``sys.meta_path`` hook.
    A PEP-302 finder requires method find_module() to return loader
    class with method load_module(). Both these methods are implemented
    in one class.

    This is also a PEP-451 finder and loader class for the ModuleSpec type
    import system. A PEP-451 finder requires method find_spec(), a PEP-451
    loader requires methods exec_module(), load_module(9 and (optionally)
    create_module(). All these methods are implemented in this one class.

    To use this class just call

        FrozenImporter.install()
    """

    def __init__(self):
        """
        Load, unzip and initialize the Zip archive bundled with the executable.
        """
        # Examine all items in sys.path and the one like /path/executable_name?117568
        # is the correct executable with bundled zip archive. Use this value
        # for the ZlibArchiveReader class and remove this item from sys.path.
        # It was needed only for FrozenImporter class. Wrong path from sys.path
        # Raises ArchiveReadError exception.
        for pyz_filepath in sys.path:
            # We need to acquire the interpreter's import lock here
            # because ZlibArchiveReader() seeks through and reads from the
            # zip archive.
            imp_lock()
            try:
                # Unzip zip archive bundled with the executable.
                self._pyz_archive = ZlibArchiveReader(pyz_filepath)
                # Verify the integrity of the zip archive with Python modules.
                # This is already done when creating the ZlibArchiveReader instance.
                #self._pyz_archive.checkmagic()

                # End this method since no Exception was raised we can assume
                # ZlibArchiveReader was successfully loaded. Let's remove 'pyz_filepath'
                # from sys.path.
                sys.path.remove(pyz_filepath)
                # Some runtime hook might need access to the list of available
                # frozen module. Let's make them accessible as a set().
                self.toc = set(self._pyz_archive.toc.keys())
                # Return - no error was raised.
                trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath)
                return
            except IOError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            except ArchiveReadError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            finally:
                imp_unlock()
        # sys.path does not contain filename of executable with bundled zip archive.
        # Raise import error.
        raise ImportError("Can't load frozen modules.")


    def __call__(self, path):
        """
        PEP-302 sys.path_hook processor. is_py2: This is only needed for Python
        2; see comments at `path_hook installation`_.

        sys.path_hook is a list of callables, which will be checked in
        sequence to determine if they can handle a given path item.
        """

        if path.startswith(SYS_PREFIX):
            fullname = path[SYS_PREFIXLEN+1:].replace(pyi_os_path.os_sep, '.')
            loader = self.find_module(fullname)
            if loader is not None:
                return loader
        raise ImportError(path)


    def find_module(self, fullname, path=None):
        # Deprecated in Python 3.4, see PEP-451
        """
        PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__
                     for submodules or subpackages.

        Return a loader object if the module was found, or None if it wasn't.
        If find_module() raises an exception, it will be propagated to the
        caller, aborting the import.
        """

        # Acquire the interpreter's import lock for the current thread. This
        # lock should be used by import hooks to ensure thread-safety when
        # importing modules.

        imp_lock()
        module_loader = None  # None means - no module found in this importer.

        if fullname in self.toc:
            # Tell the import machinery to use self.load_module() to load the module.
            module_loader = self
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.split('.')[-1]

            for p in path:
                p = p[SYS_PREFIXLEN+1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts: continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = ".".join(parts)
                if entry_name in self.toc:
                    module_loader = FrozenPackageImporter(self, entry_name)
                    trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                          entry_name, fullname, p)
                    break
        # Release the interpreter's import lock.
        imp_unlock()
        if module_loader is None:
            trace("# %s not found in PYZ", fullname)
        return module_loader

    def load_module(self, fullname, entry_name=None):
        # Deprecated in Python 3.4, see PEP-451
        """
        PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.

        Return the loaded module (instance of imp_new_module()) or raises
        an exception, preferably ImportError if an existing exception
        is not being propagated.

        When called from FrozenPackageImporter, `entry_name` is the name of the
        module as it is stored in the archive. This module will be loaded and installed
        into sys.modules using `fullname` as its name
        """
        # Acquire the interpreter's import lock.
        imp_lock()
        module = None
        if entry_name is None:
            entry_name = fullname
        try:
            # PEP302 If there is an existing module object named 'fullname'
            # in sys.modules, the loader must use that existing module.
            module = sys.modules.get(fullname)

            # Module not in sys.modules - load it and it to sys.modules.
            if module is None:
                # Load code object from the bundled ZIP archive.
                is_pkg, bytecode = self._pyz_archive.extract(entry_name)
                # Create new empty 'module' object.
                module = imp_new_module(fullname)

                # TODO Replace bytecode.co_filename by something more meaningful:
                # e.g. /absolute/path/frozen_executable/path/to/module/module_name.pyc
                # Paths from developer machine are masked.

                # Set __file__ attribute of a module relative to the
                # executable so that data files can be found.
                module.__file__ = self.get_filename(entry_name)

                ### Set __path__  if 'fullname' is a package.
                # Python has modules and packages. A Python package is container
                # for several modules or packages.
                if is_pkg:

                    # If a module has a __path__ attribute, the import mechanism
                    # will treat it as a package.
                    #
                    # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the
                    # correct path where PyInstaller should find bundled dynamic
                    # libraries. In one-file mode it points to the tmp directory where
                    # bundled files are extracted at execution time.
                    #
                    # __path__ cannot be empty list because 'wx' module prepends something to it.
                    # It cannot contain value 'sys.prefix' because 'xml.etree.cElementTree' fails
                    # Otherwise.
                    #
                    # Set __path__ to point to 'sys.prefix/package/subpackage'.
                    module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]

                ### Set __loader__
                # The attribute __loader__ improves support for module 'pkg_resources' and
                # with the frozen apps the following functions are working:
                # pkg_resources.resource_string(), pkg_resources.resource_stream().
                module.__loader__ = self

                ### Set __package__
                # Accoring to PEP302 this attribute must be set.
                # When it is present, relative imports will be based on this
                # attribute rather than the module __name__ attribute.
                # More details can be found in PEP366.
                # For ordinary modules this is set like:
                #     'aa.bb.cc.dd'  ->  'aa.bb.cc'
                if is_pkg:
                    module.__package__ = fullname
                else:
                    module.__package__ = fullname.rsplit('.', 1)[0]

                ### Set __spec__ for Python 3.4+
                # In Python 3.4 was introduced module attribute __spec__ to
                # consolidate all module attributes.
                if sys.version_info[0:2] > (3, 3):
                    module.__spec__ = _frozen_importlib.ModuleSpec(
                        entry_name, self, is_package=is_pkg)

                ### Add module object to sys.modules dictionary.
                # Module object must be in sys.modules before the loader
                # executes the module code. This is crucial because the module
                # code may (directly or indirectly) import itself; adding it
                # to sys.modules beforehand prevents unbounded recursion in the
                # worst case and multiple loading in the best.
                sys.modules[fullname] = module

                # Run the module code.
                exec(bytecode, module.__dict__)
                # Reread the module from sys.modules in case it's changed itself
                module = sys.modules[fullname]

        except Exception:
            # Remove 'fullname' from sys.modules if it was appended there.
            if fullname in sys.modules:
                sys.modules.pop(fullname)
            # TODO Do we need to raise different types of Exceptions for better debugging?
            # PEP302 requires to raise ImportError exception.
            #raise ImportError("Can't load frozen module: %s" % fullname)

            raise

        finally:
            # Release the interpreter's import lock.
            imp_unlock()


        # Module returned only in case of no exception.
        return module

    ### Optional Extensions to the PEP-302 Importer Protocol

    def is_package(self, fullname):
        if fullname in self.toc:
            try:
                return self._pyz_archive.is_package(fullname)
            except Exception:
                raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)

    def get_code(self, fullname):
        """
        Get the code object associated with the module.

        ImportError should be raised if module not found.
        """
        try:
            # extract() returns None if fullname not in the archive, thus the
            # next line will raise an execpion which will be catched just
            # below and raise the ImportError.
            return self._pyz_archive.extract(fullname)[1]
        except:
            raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)

    def get_source(self, fullname):
        """
        Method should return the source code for the module as a string.
        But frozen modules does not contain source code.

        Return None.
        """
        if fullname in self.toc:
            return None
        else:
            # ImportError should be raised if module not found.
            raise ImportError('No module named ' + fullname)

    def get_data(self, path):
        """
        This returns the data as a string, or raise IOError if the "file"
        wasn't found. The data is always returned as if "binary" mode was used.

        This method is useful getting resources with 'pkg_resources' that are
        bundled with Python modules in the PYZ archive.

        The 'path' argument is a path that can be constructed by munging
        module.__file__ (or pkg.__path__ items)
        """
        assert path.startswith(SYS_PREFIX + pyi_os_path.os_sep)
        fullname = path[SYS_PREFIXLEN+1:]
        if fullname in self.toc:
            # If the file is in the archive, return this
            return self._pyz_archive.extract(fullname)[1]
        else:
            # Otherwise try to fetch it from the filesystem. Since
            # __file__ attribute works properly just try to open and
            # read it.
            with open(path, 'rb') as fp:
                return fp.read()

    def get_filename(self, fullname):
        """
        This method should return the value that __file__ would be set to
        if the named module was loaded. If the module is not found, then
        ImportError should be raised.
        """
        # The absolute absolute path to the executable is taken from
        # sys.prefix. In onefile mode it points to the temp directory where
        # files are unpacked by PyInstaller. Then, append the appropriate
        # suffix (__init__.pyc for a package, or just .pyc for a module).
        # Method is_package() will raise ImportError if module not found.
        if self.is_package(fullname):
            filename = pyi_os_path.os_path_join(pyi_os_path.os_path_join(SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep)), '__init__.pyc')
        else:
            filename = pyi_os_path.os_path_join(SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
        return filename

    def find_spec(self, fullname, path=None, target=None):
        """
        PEP-451 finder.find_spec() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__ for
                     submodules or subpackages.
        target       unused by this Finder

        Finders are still responsible for identifying, and typically creating,
        the loader that should be used to load a module. That loader will now
        be stored in the module spec returned by find_spec() rather than
        returned directly. As is currently the case without the PEP-452, if a
        loader would be costly to create, that loader can be designed to defer
        the cost until later.

        Finders must return ModuleSpec objects when find_spec() is called.
        This new method replaces find_module() and find_loader() (in the
        PathEntryFinder case). If a loader does not have find_spec(),
        find_module() and find_loader() are used instead, for
        backward-compatibility.
        """
        entry_name = None  # None means - no module found in this importer.

        if fullname in self.toc:
            entry_name = fullname
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.rsplit('.')[-1]

            for p in path:
                p = p[SYS_PREFIXLEN+1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts: continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = ".".join(parts)
                if entry_name in self.toc:
                    trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                          entry_name, fullname, p)
                    break
            else:
                entry_name = None

        if entry_name is None:
            trace("# %s not found in PYZ", fullname)
            return None

        # origin has to be the filename
        origin = self.get_filename(entry_name)
        is_pkg = self.is_package(entry_name)

        spec =  _frozen_importlib.ModuleSpec(
            fullname, self,
            is_package=is_pkg, origin=origin,
            # Provide the entry_name for the loader to use during loading
            loader_state = entry_name)

        # Make the import machinery set __file__.
        # PEP 451 says: "has_location" is true if the module is locatable. In
        # that case the spec's origin is used as the location and __file__ is
        # set to spec.origin. If additional location information is required
        # (e.g. zipimport), that information may be stored in
        # spec.loader_state.
        spec.has_location = True
        return spec

    def create_module(self, spec):
        """
        PEP-451 loader.create_module() method for the ``sys.meta_path`` hook.

        Loaders may also implement create_module() that will return a new
        module to exec. It may return None to indicate that the default module
        creation code should be used. One use case, though atypical, for
        create_module() is to provide a module that is a subclass of the
        builtin module type. Most loaders will not need to implement
        create_module(),

        create_module() should properly handle the case where it is called
        more than once for the same spec/module. This may include returning
        None or raising ImportError.
        """
        # Opposed to what is defined in PEP-451, this method is not optional.
        # We want the default results, so we simply return None (which is
        # handled for su my the import machinery). See
        # https://bugs.python.org/issue23014 for more information.
        return None

    def exec_module(self, module):
        """
        PEP-451 loader.exec_module() method for the ``sys.meta_path`` hook.

        Loaders will have a new method, exec_module(). Its only job is to
        "exec" the module and consequently populate the module's namespace. It
        is not responsible for creating or preparing the module object, nor
        for any cleanup afterward. It has no return value. exec_module() will
        be used during both loading and reloading.

        exec_module() should properly handle the case where it is called more
        than once. For some kinds of modules this may mean raising ImportError
        every time after the first time the method is called. This is
        particularly relevant for reloading, where some kinds of modules do
        not support in-place reloading.
        """
        spec = module.__spec__
        bytecode = self.get_code(spec.loader_state)

        # Set by the import machinery
        assert hasattr(module, '__file__')

        # If `submodule_search_locations` is not None, this is a package;
        # set __path__.
        if spec.submodule_search_locations is not None:
            # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to
            # the correct path where PyInstaller should find bundled dynamic
            # libraries. In one-file mode it points to the tmp directory where
            # bundled files are extracted at execution time.
            #
            # __path__ cannot be empty list because 'wx' module prepends
            # something to it. It cannot contain value 'sys.prefix' because
            # 'xml.etree.cElementTree' fails otherwise.
            #
            # Set __path__ to point to 'sys.prefix/package/subpackage'.
            module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]

        exec(bytecode, module.__dict__)
示例#4
0
class FrozenImporter(object):
    """
    Load bytecode of Python modules from the executable created by PyInstaller.

    Python bytecode is zipped and appended to the executable.

    NOTE: PYZ format cannot be replaced by zipimport module.

    The problem is that we have no control over zipimport; for instance,
    it doesn't work if the zip file is embedded into a PKG appended
    to an executable, like we create in one-file.

    This is PEP-302 finder and loader class for the ``sys.meta_path`` hook.
    A PEP-302 finder requires method find_module() to return loader
    class with method load_module(). Both these methods are implemented
    in one class.


    To use this class just call

        FrozenImporter.install()
    """
    def __init__(self):
        """
        Load, unzip and initialize the Zip archive bundled with the executable.
        """
        # Examine all items in sys.path and the one like /path/executable_name?117568
        # is the correct executable with bundled zip archive. Use this value
        # for the ZlibArchiveReader class and remove this item from sys.path.
        # It was needed only for FrozenImporter class. Wrong path from sys.path
        # Raises ArchiveReadError exception.
        for pyz_filepath in sys.path:
            # We need to acquire the interpreter's import lock here
            # because ZlibArchiveReader() seeks through and reads from the
            # zip archive.
            imp_lock()
            try:
                # Unzip zip archive bundled with the executable.
                self._pyz_archive = ZlibArchiveReader(pyz_filepath)
                # Verify the integrity of the zip archive with Python modules.
                # This is already done when creating the ZlibArchiveReader instance.
                #self._pyz_archive.checkmagic()

                # End this method since no Exception was raised we can assume
                # ZlibArchiveReader was successfully loaded. Let's remove 'pyz_filepath'
                # from sys.path.
                sys.path.remove(pyz_filepath)
                # Some runtime hook might need access to the list of available
                # frozen module. Let's make them accessible as a set().
                self.toc = set(self._pyz_archive.toc.keys())
                # Return - no error was raised.
                trace("# PyInstaller: FrozenImporter(%s)", pyz_filepath)
                return
            except IOError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            except ArchiveReadError:
                # Item from sys.path is not ZlibArchiveReader let's try next.
                continue
            finally:
                imp_unlock()
        # sys.path does not contain filename of executable with bundled zip archive.
        # Raise import error.
        raise ImportError("Can't load frozen modules.")


    def __call__(self, path):
        """
        PEP-302 sys.path_hook processor.

        sys.path_hook is a list of callables, which will be checked in
        sequence to determine if they can handle a given path item.
        """

        if path.startswith(SYS_PREFIX):
            fullname = path[SYS_PREFIXLEN+1:].replace(pyi_os_path.os_sep, '.')
            loader = self.find_module(fullname)
            if loader is not None:
                return loader
        raise ImportError(path)


    def find_module(self, fullname, path=None):
        """
        PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.

        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__ for submodules or subpackages.

        Return a loader object if the module was found, or None if it wasn't. If find_module() raises
        an exception, it will be propagated to the caller, aborting the import.
        """
        # Acquire the interpreter's import lock for the current thread. This
        # lock should be used by import hooks to ensure thread-safety when
        # importing modules.

        imp_lock()
        module_loader = None  # None means - no module found in this importer.

        if fullname in self.toc:
            # Tell the import machinery to use self.load_module() to load the module.
            module_loader = self
            trace("import %s # PyInstaller PYZ", fullname)
        elif path is not None:
            # Try to handle module.__path__ modifications by the modules themselves
            # Reverse the fake __path__ we added to the package module to a
            # dotted module name and add the tail module from fullname onto that
            # to synthesize a new fullname
            modname = fullname.split('.')[-1]

            for p in path:
                p = p.replace(SYS_PREFIX, "")
                parts = p.split(pyi_os_path.os_sep)
                if not len(parts): continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                real_fullname = ".".join(parts)
                if real_fullname in self.toc:
                    module_loader = FrozenPackageImporter(self, real_fullname)
                    trace("import %s as %s # PyInstaller PYZ (__path__ override: %s)",
                          real_fullname, fullname, p)
                    break
        # Release the interpreter's import lock.
        imp_unlock()
        if module_loader is None:
            trace("# %s not found in PYZ", fullname)
        return module_loader

    def load_module(self, fullname, real_fullname=None):
        """
        PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.

        Return the loaded module (instance of imp_new_module()) or raises
        an exception, preferably ImportError if an existing exception
        is not being propagated.

        When called from FrozenPackageImporter, `real_fullname` is the name of the
        module as it is stored in the archive. This module will be loaded and installed
        into sys.modules using `fullname` as its name
        """
        # Acquire the interpreter's import lock.
        imp_lock()
        module = None
        if real_fullname is None:
            real_fullname=fullname
        try:
            # PEP302 If there is an existing module object named 'fullname'
            # in sys.modules, the loader must use that existing module.
            module = sys.modules.get(fullname)

            # Module not in sys.modules - load it and it to sys.modules.
            if module is None:
                # Load code object from the bundled ZIP archive.
                is_pkg, bytecode = self._pyz_archive.extract(real_fullname)
                # Create new empty 'module' object.
                module = imp_new_module(fullname)

                # TODO Replace bytecode.co_filename by something more meaningful:
                # e.g. /absolute/path/frozen_executable/path/to/module/module_name.pyc
                # Paths from developer machine are masked.

                ### Set __file__ attribute of a module relative to the executable
                # so that data files can be found. The absolute absolute path
                # to the executable is taken from sys.prefix. In onefile mode it
                # points to the temp directory where files are unpacked by PyInstaller.
                # Then, append the appropriate suffix (__init__.pyc for a package, or just .pyc for a module).
                if is_pkg:
                    module.__file__ = pyi_os_path.os_path_join(pyi_os_path.os_path_join(SYS_PREFIX,
                        fullname.replace('.', pyi_os_path.os_sep)), '__init__.pyc')
                else:
                    module.__file__ = pyi_os_path.os_path_join(SYS_PREFIX,
                        fullname.replace('.', pyi_os_path.os_sep) + '.pyc')

                ### Set __path__  if 'fullname' is a package.
                # Python has modules and packages. A Python package is container
                # for several modules or packages.
                if is_pkg:

                    # If a module has a __path__ attribute, the import mechanism
                    # will treat it as a package.
                    #
                    # Since PYTHONHOME is set in bootloader, 'sys.prefix' points to the
                    # correct path where PyInstaller should find bundled dynamic
                    # libraries. In one-file mode it points to the tmp directory where
                    # bundled files are extracted at execution time.
                    #
                    # __path__ cannot be empty list because 'wx' module prepends something to it.
                    # It cannot contain value 'sys.prefix' because 'xml.etree.cElementTree' fails
                    # Otherwise.
                    #
                    # Set __path__ to point to 'sys.prefix/package/subpackage'.
                    module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]

                ### Set __loader__
                # The attribute __loader__ improves support for module 'pkg_resources' and
                # with the frozen apps the following functions are working:
                # pkg_resources.resource_string(), pkg_resources.resource_stream().
                module.__loader__ = self

                ### Set __package__
                # Accoring to PEP302 this attribute must be set.
                # When it is present, relative imports will be based on this
                # attribute rather than the module __name__ attribute.
                # More details can be found in PEP366.
                # For ordinary modules this is set like:
                #     'aa.bb.cc.dd'  ->  'aa.bb.cc'
                if is_pkg:
                    module.__package__ = fullname
                else:
                    module.__package__ = fullname.rsplit('.', 1)[0]

                ### Set __spec__ for Python 3.4+
                # In Python 3.4 was introduced module attribute __spec__ to
                # consolidate all module attributes.
                if sys.version_info[0:2] > (3, 3):
                    module.__spec__ = _frozen_importlib.ModuleSpec(
                        real_fullname, self, is_package=is_pkg)

                ### Add module object to sys.modules dictionary.
                # Module object must be in sys.modules before the loader
                # executes the module code. This is crucial because the module
                # code may (directly or indirectly) import itself; adding it
                # to sys.modules beforehand prevents unbounded recursion in the
                # worst case and multiple loading in the best.
                sys.modules[fullname] = module

                # Run the module code.
                exec(bytecode, module.__dict__)
                # Reread the module from sys.modules in case it's changed itself
                module = sys.modules[fullname]

        except Exception:
            # Remove 'fullname' from sys.modules if it was appended there.
            if fullname in sys.modules:
                sys.modules.pop(fullname)
            # TODO Do we need to raise different types of Exceptions for better debugging?
            # PEP302 requires to raise ImportError exception.
            #raise ImportError("Can't load frozen module: %s" % fullname)

            raise

        finally:
            # Release the interpreter's import lock.
            imp_unlock()


        # Module returned only in case of no exception.
        return module

    ### Optional Extensions to the PEP-302 Importer Protocol

    def is_package(self, fullname):
        """
        Return always False since built-in modules are never packages.
        """
        if fullname in self.toc:
            try:
                is_pkg, bytecode = self._pyz_archive.extract(fullname)
                return bool(is_pkg)
            except Exception:
                raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)

    def get_code(self, fullname):
        """
        Get the code object associated with the module.

        ImportError should be raised if module not found.
        """
        if fullname in self.toc:
            try:
                is_pkg, bytecode = self._pyz_archive.extract(fullname)
                return bytecode
            except Exception:
                raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)
        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' + fullname)

    def get_source(self, fullname):
        """
        Method should return the source code for the module as a string.
        But frozen modules does not contain source code.

        Return None.
        """
        if fullname in self.toc:
            return None
        else:
            # ImportError should be raised if module not found.
            raise ImportError('No module named ' + fullname)

    def get_data(self, path):
        """
        This returns the data as a string, or raise IOError if the "file"
        wasn't found. The data is always returned as if "binary" mode was used.

        This method is useful getting resources with 'pkg_resources' that are
        bundled with Python modules in the PYZ archive.

        The 'path' argument is a path that can be constructed by munging
        module.__file__ (or pkg.__path__ items)
        """
        assert path.startswith(SYS_PREFIX + pyi_os_path.os_sep)
        fullname = path[len(SYS_PREFIX)+1:]
        if fullname in self.toc:
            # If the file is in the archive, return this
            return self._pyz_archive.extract(fullname)[1]
        else:
            # Otherwise try to fetch it from the filesystem. Since
            # __file__ attribute works properly just try to open and
            # read it.
            with open(path, 'rb') as fp:
                return fp.read()

    # TODO Do we really need to implement this method?
    def get_filename(self, fullname):
        """
        This method should return the value that __file__ would be set to
        if the named module was loaded. If the module is not found, then
        ImportError should be raised.
        """
        # Then, append the appropriate suffix (__init__.pyc for a package, or just .pyc for a module).
        # Method is_package() will raise ImportError if module not found.
        if self.is_package(fullname):
            filename = pyi_os_path.os_path_join(pyi_os_path.os_path_join(SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep)), '__init__.pyc')
        else:
            filename = pyi_os_path.os_path_join(SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
        return filename
示例#5
0
class FrozenImporter(object):
    """
    Load bytecode of Python modules from the executable created by PyInstaller.
    
    Python bytecode is zipped and appended to the executable.
    
    NOTE: PYZ format cannot be replaced by zipimport module.
    
    The problem is that we have no control over zipimport; for instance,
    it doesn't work if the zip file is embedded into a PKG appended
    to an executable, like we create in one-file.
    
    This is PEP-302 finder and loader class for the ``sys.meta_path`` hook.
    A PEP-302 finder requires method find_module() to return loader
    class with method load_module(). Both these methods are implemented
    in one class.
    
    This is also a PEP-451 finder and loader class for the ModuleSpec type
    import system. A PEP-451 finder requires method find_spec(), a PEP-451
    loader requires methods exec_module(), load_module(9 and (optionally)
    create_module(). All these methods are implemented in this one class.
    
    To use this class just call
    
        FrozenImporter.install()
    """
    def __init__(self):
        """
        Load, unzip and initialize the Zip archive bundled with the executable.
        """
        for pyz_filepath in sys.path:
            imp_lock()
            try:
                try:
                    self._pyz_archive = ZlibArchiveReader(pyz_filepath)
                    sys.path.remove(pyz_filepath)
                    self.toc = set(self._pyz_archive.toc.keys())
                    trace('# PyInstaller: FrozenImporter(%s)', pyz_filepath)
                    return
                except IOError:
                    continue
                except ArchiveReadError:
                    continue

            finally:
                imp_unlock()

        raise ImportError("Can't load frozen modules.")

    def __call__(self, path):
        """
        PEP-302 sys.path_hook processor. is_py2: This is only needed for Python
        2; see comments at `path_hook installation`_.
        
        sys.path_hook is a list of callables, which will be checked in
        sequence to determine if they can handle a given path item.
        """
        if path.startswith(SYS_PREFIX):
            fullname = path[SYS_PREFIXLEN + 1:].replace(
                pyi_os_path.os_sep, '.')
            loader = self.find_module(fullname)
            if loader is not None:
                return loader
        raise ImportError(path)
        return

    def find_module(self, fullname, path=None):
        """
        PEP-302 finder.find_module() method for the ``sys.meta_path`` hook.
        
        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__
                     for submodules or subpackages.
        
        Return a loader object if the module was found, or None if it wasn't.
        If find_module() raises an exception, it will be propagated to the
        caller, aborting the import.
        """
        imp_lock()
        module_loader = None
        if fullname in self.toc:
            module_loader = self
            trace('import %s # PyInstaller PYZ', fullname)
        elif path is not None:
            modname = fullname.split('.')[-1]
            for p in path:
                p = p[SYS_PREFIXLEN + 1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts:
                    continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = '.'.join(parts)
                if entry_name in self.toc:
                    module_loader = FrozenPackageImporter(self, entry_name)
                    trace(
                        'import %s as %s # PyInstaller PYZ (__path__ override: %s)',
                        entry_name, fullname, p)
                    break

        imp_unlock()
        if module_loader is None:
            trace('# %s not found in PYZ', fullname)
        return module_loader

    def load_module(self, fullname, entry_name=None):
        """
        PEP-302 loader.load_module() method for the ``sys.meta_path`` hook.
        
        Return the loaded module (instance of imp_new_module()) or raises
        an exception, preferably ImportError if an existing exception
        is not being propagated.
        
        When called from FrozenPackageImporter, `entry_name` is the name of the
        module as it is stored in the archive. This module will be loaded and installed
        into sys.modules using `fullname` as its name
        """
        imp_lock()
        module = None
        if entry_name is None:
            entry_name = fullname
        try:
            try:
                module = sys.modules.get(fullname)
                if module is None:
                    is_pkg, bytecode = self._pyz_archive.extract(entry_name)
                    module = imp_new_module(fullname)
                    module.__file__ = self.get_filename(entry_name)
                    if is_pkg:
                        module.__path__ = [
                            pyi_os_path.os_path_dirname(module.__file__)
                        ]
                    module.__loader__ = self
                    if is_pkg:
                        module.__package__ = fullname
                    else:
                        module.__package__ = fullname.rsplit('.', 1)[0]
                    if sys.version_info[0:2] > (3, 3):
                        module.__spec__ = _frozen_importlib.ModuleSpec(
                            entry_name, self, is_package=is_pkg)
                    sys.modules[fullname] = module
                    exec bytecode in module.__dict__
                    module = sys.modules[fullname]
            except Exception:
                if fullname in sys.modules:
                    sys.modules.pop(fullname)
                raise

        finally:
            imp_unlock()

        return module

    def is_package(self, fullname):
        if fullname in self.toc:
            try:
                return self._pyz_archive.is_package(fullname)
            except Exception:
                raise ImportError(
                    'Loader FrozenImporter cannot handle module ' + fullname)

        else:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_code(self, fullname):
        """
        Get the code object associated with the module.
        
        ImportError should be raised if module not found.
        """
        try:
            return self._pyz_archive.extract(fullname)[1]
        except:
            raise ImportError('Loader FrozenImporter cannot handle module ' +
                              fullname)

    def get_source(self, fullname):
        """
        Method should return the source code for the module as a string.
        But frozen modules does not contain source code.
        
        Return None.
        """
        if fullname in self.toc:
            return None
        else:
            raise ImportError('No module named ' + fullname)
            return None

    def get_data(self, path):
        """
        This returns the data as a string, or raise IOError if the "file"
        wasn't found. The data is always returned as if "binary" mode was used.
        
        This method is useful getting resources with 'pkg_resources' that are
        bundled with Python modules in the PYZ archive.
        
        The 'path' argument is a path that can be constructed by munging
        module.__file__ (or pkg.__path__ items)
        """
        assert path.startswith(SYS_PREFIX + pyi_os_path.os_sep)
        fullname = path[SYS_PREFIXLEN + 1:]
        if fullname in self.toc:
            return self._pyz_archive.extract(fullname)[1]
        with open(path, 'rb') as fp:
            return fp.read()

    def get_filename(self, fullname):
        """
        This method should return the value that __file__ would be set to
        if the named module was loaded. If the module is not found, then
        ImportError should be raised.
        """
        if self.is_package(fullname):
            filename = pyi_os_path.os_path_join(
                pyi_os_path.os_path_join(
                    SYS_PREFIX, fullname.replace('.', pyi_os_path.os_sep)),
                '__init__.pyc')
        else:
            filename = pyi_os_path.os_path_join(
                SYS_PREFIX,
                fullname.replace('.', pyi_os_path.os_sep) + '.pyc')
        return filename

    def find_spec(self, fullname, path=None, target=None):
        """
        PEP-451 finder.find_spec() method for the ``sys.meta_path`` hook.
        
        fullname     fully qualified name of the module
        path         None for a top-level module, or package.__path__ for
                     submodules or subpackages.
        target       unused by this Finder
        
        Finders are still responsible for identifying, and typically creating,
        the loader that should be used to load a module. That loader will now
        be stored in the module spec returned by find_spec() rather than
        returned directly. As is currently the case without the PEP-452, if a
        loader would be costly to create, that loader can be designed to defer
        the cost until later.
        
        Finders must return ModuleSpec objects when find_spec() is called.
        This new method replaces find_module() and find_loader() (in the
        PathEntryFinder case). If a loader does not have find_spec(),
        find_module() and find_loader() are used instead, for
        backward-compatibility.
        """
        entry_name = None
        if fullname in self.toc:
            entry_name = fullname
            trace('import %s # PyInstaller PYZ', fullname)
        elif path is not None:
            modname = fullname.rsplit('.')[-1]
            for p in path:
                p = p[SYS_PREFIXLEN + 1:]
                parts = p.split(pyi_os_path.os_sep)
                if not parts:
                    continue
                if not parts[0]:
                    parts = parts[1:]
                parts.append(modname)
                entry_name = '.'.join(parts)
                if entry_name in self.toc:
                    trace(
                        'import %s as %s # PyInstaller PYZ (__path__ override: %s)',
                        entry_name, fullname, p)
                    break
            else:
                entry_name = None

        if entry_name is None:
            trace('# %s not found in PYZ', fullname)
            return
        else:
            origin = self.get_filename(entry_name)
            is_pkg = self.is_package(entry_name)
            spec = _frozen_importlib.ModuleSpec(fullname,
                                                self,
                                                is_package=is_pkg,
                                                origin=origin,
                                                loader_state=entry_name)
            spec.has_location = True
            return spec

    def create_module(self, spec):
        """
        PEP-451 loader.create_module() method for the ``sys.meta_path`` hook.
        
        Loaders may also implement create_module() that will return a new
        module to exec. It may return None to indicate that the default module
        creation code should be used. One use case, though atypical, for
        create_module() is to provide a module that is a subclass of the
        builtin module type. Most loaders will not need to implement
        create_module(),
        
        create_module() should properly handle the case where it is called
        more than once for the same spec/module. This may include returning
        None or raising ImportError.
        """
        return None

    def exec_module(self, module):
        """
        PEP-451 loader.exec_module() method for the ``sys.meta_path`` hook.
        
        Loaders will have a new method, exec_module(). Its only job is to
        "exec" the module and consequently populate the module's namespace. It
        is not responsible for creating or preparing the module object, nor
        for any cleanup afterward. It has no return value. exec_module() will
        be used during both loading and reloading.
        
        exec_module() should properly handle the case where it is called more
        than once. For some kinds of modules this may mean raising ImportError
        every time after the first time the method is called. This is
        particularly relevant for reloading, where some kinds of modules do
        not support in-place reloading.
        """
        spec = module.__spec__
        bytecode = self.get_code(spec.loader_state)
        assert hasattr(module, '__file__')
        if spec.submodule_search_locations is not None:
            module.__path__ = [pyi_os_path.os_path_dirname(module.__file__)]
        exec bytecode in module.__dict__
        return