Example #1
0
    def _load_hook_module(self):
        """
        Lazily load this hook script into an in-memory private module.

        This method (and, indeed, this class) preserves all attributes and functions defined by this hook script as
        is, ensuring sane behaviour in hook functions _not_ expecting unplanned external modification. Instead,
        this method copies public attributes defined by this hook script (e.g., `binaries`) into private attributes
        of this object, which the special `__getattr__()` and `__setattr__()` methods safely expose to external
        callers. For public attributes _not_ defined by this hook script, the corresponding private attributes will
        be assigned sane defaults. For some public attributes defined by this hook script, the corresponding private
        attributes will be transformed into objects more readily and safely consumed elsewhere by external callers.

        See Also
        ----------
        Class docstring for supported attributes.
        """

        # If this hook script module has already been loaded, or we are _shallow, noop.
        if self._hook_module is not None or self._shallow:
            if self._shallow:
                self._hook_module = True  # Not None
                # Inform the user
                logger.debug(
                    'Skipping module hook %r from %r because a hook for %s has already been loaded.',
                    *os.path.split(self.hook_filename)[::-1], self.module_name)
                # Set the default attributes to empty instances of the type.
                for attr_name, (attr_type,
                                _) in _MAGIC_MODULE_HOOK_ATTRS.items():
                    super().__setattr__(attr_name, attr_type())
            return

        # Load and execute the hook script. Even if mechanisms from the import machinery are used, this does not import
        # the hook as the module.
        head, tail = os.path.split(self.hook_filename)
        logger.info('Loading module hook %r from %r...', tail, head)
        try:
            self._hook_module = importlib_load_source(self.hook_module_name,
                                                      self.hook_filename)
        except ImportError:
            logger.debug("Hook failed with:", exc_info=True)
            raise ImportErrorWhenRunningHook(self.hook_module_name,
                                             self.hook_filename)

        # Copy hook script attributes into magic attributes exposed as instance variables of the current "ModuleHook"
        # instance.
        for attr_name, (default_type,
                        sanitizer_func) in _MAGIC_MODULE_HOOK_ATTRS.items():
            # Unsanitized value of this attribute.
            attr_value = getattr(self._hook_module, attr_name, None)

            # If this attribute is undefined, expose a sane default instead.
            if attr_value is None:
                attr_value = default_type()
            # Else if this attribute requires sanitization, do so.
            elif sanitizer_func is not None:
                attr_value = sanitizer_func(attr_value)
            # Else, expose the unsanitized value of this attribute.

            # Expose this attribute as an instance variable of the same name.
            setattr(self, attr_name, attr_value)
Example #2
0
    def _safe_import_module(self, module_basename, module_name,
                            parent_package):
        """
        Create a new graph node for the module with the passed name under the
        parent package signified by the passed graph node.

        This method wraps the superclass method with support for pre-import
        module hooks. If such a hook exists for this module (e.g., a script
        `PyInstaller.hooks.hook-{module_name}` containing a function
        `pre_safe_import_module()`), that hook will be run _before_ the
        superclass method is called.

        Pre-Safe-Import-Hooks are performed just *prior* to importing
        the module. When running the hook, the modules parent package
        has already been imported and ti's `__path__` is set up. But
        the module is just about to be imported.

        See the superclass method for description of parameters and
        return value.
        """
        # If this module has pre-safe import module hooks, run these first.
        if module_name in self._hooks_pre_safe_import_module:
            # For the absolute path of each such hook...
            for hook in self._hooks_pre_safe_import_module[module_name]:
                # Dynamically import this hook as a fabricated module.
                logger.info(
                    'Processing pre-safe import module hook %s '
                    'from %r.', module_name, hook.hook_filename)
                hook_module_name = 'PyInstaller_hooks_pre_safe_import_module_' + module_name.replace(
                    '.', '_')
                hook_module = importlib_load_source(hook_module_name,
                                                    hook.hook_filename)

                # Object communicating changes made by this hook back to us.
                hook_api = PreSafeImportModuleAPI(
                    module_graph=self,
                    module_basename=module_basename,
                    module_name=module_name,
                    parent_package=parent_package,
                )

                # Run this hook, passed this object.
                if not hasattr(hook_module, 'pre_safe_import_module'):
                    raise NameError(
                        'pre_safe_import_module() function not defined by '
                        'hook %r.' % hook_module)
                hook_module.pre_safe_import_module(hook_api)

                # Respect method call changes requested by this hook.
                module_basename = hook_api.module_basename
                module_name = hook_api.module_name

            # Prevent subsequent calls from rerunning these hooks.
            del self._hooks_pre_safe_import_module[module_name]

        # Call the superclass method.
        return super(PyiModuleGraph,
                     self)._safe_import_module(module_basename, module_name,
                                               parent_package)
Example #3
0
    def _find_module_path(self, fullname, module_name, search_dirs):
        """
        Get a 3-tuple detailing the physical location of the module with the
        passed name if that module exists _or_ raise `ImportError` otherwise.

        This method wraps the superclass method with support for pre-find module
        path hooks. If such a hook exists for this module (e.g., a script
        `PyInstaller.hooks.hook-{module_name}` containing a function
        `pre_find_module_path()`), that hook will be run _before_ the
        superclass method is called.

        See superclass method for parameter and return value descriptions.
        """
        # If this module has pre-find module path hooks, run these first.
        if fullname in self._hooks_pre_find_module_path:
            # For the absolute path of each such hook...
            for hook in self._hooks_pre_find_module_path[fullname]:
                # Dynamically import this hook as a fabricated module.
                logger.info('Processing pre-find module path hook %s from %r.',
                            fullname, hook.hook_filename)
                hook_fullname = 'PyInstaller_hooks_pre_find_module_path_' + fullname.replace(
                    '.', '_')
                hook_module = importlib_load_source(hook_fullname,
                                                    hook.hook_filename)

                # Object communicating changes made by this hook back to us.
                hook_api = PreFindModulePathAPI(
                    module_graph=self,
                    module_name=fullname,
                    search_dirs=search_dirs,
                )

                # Run this hook, passed this object.
                if not hasattr(hook_module, 'pre_find_module_path'):
                    raise NameError(
                        'pre_find_module_path() function not defined by '
                        'hook %r.' % hook_module)
                hook_module.pre_find_module_path(hook_api)

                # Respect method call changes requested by this hook.
                search_dirs = hook_api.search_dirs

            # Prevent subsequent calls from rerunning these hooks.
            del self._hooks_pre_find_module_path[fullname]

        # Call the superclass method.
        return super(PyiModuleGraph,
                     self)._find_module_path(fullname, module_name,
                                             search_dirs)
Example #4
0
def _load_hook_module(self):

    logger = logging.getLogger(__name__)

    # If this hook script module has already been loaded,
    # or we are _shallow, noop.
    if self._hook_module is not None or self._shallow:

        if self._shallow:
            self._hook_module = True  # Not None

            # Inform the user

            logger.debug(
                'Skipping module hook %r from %r because a hook for %s has'
                ' already been loaded.',
                *os.path.split(self.hook_filename)[::-1], self.module_name)

            # Set the default attributes to empty instances of the type.
            for attr_name, (attr_type, _) in _MAGIC_MODULE_HOOK_ATTRS.items():
                super(ModuleHook, self).__setattr__(attr_name, attr_type())
        return

    # Load and execute the hook script. Even if mechanisms from the import
    # machinery are used, this does not import the hook as the module.
    head, tail = os.path.split(self.hook_filename)
    logger.info('Loading module module hook %r from %r...', tail, head)
    self._hook_module = importlib_load_source(self.hook_module_name,
                                              self.hook_filename)

    # Copy hook script attributes into magic attributes exposed as instance
    # variables of the current "ModuleHook" instance.
    for attr_name, (default_type,
                    sanitizer_func) in (_MAGIC_MODULE_HOOK_ATTRS.items()):
        # Unsanitized value of this attribute.
        attr_value = getattr(self._hook_module, attr_name, None)

        # If this attribute is undefined, expose a sane default instead.
        if attr_value is None:
            attr_value = default_type()
        # Else if this attribute requires sanitization, do so.
        elif sanitizer_func is not None:
            attr_value = sanitizer_func(attr_value)
        # Else, expose the unsanitized value of this attribute.

        # Expose this attribute as an instance variable of the same name.
        setattr(self, attr_name, attr_value)