Ejemplo n.º 1
0
class IterationHandleBase(getMetaClassBase("IterationHandle")):
    """Base class for Iteration Handles."""

    @abstractmethod
    def getNextValueExpression(self):
        """Abstract method to get next iteration value."""

    @abstractmethod
    def getIterationValueWithIndex(self, value_index):
        # TODO: Have one doc string, and it applies to all derived methods.
        """Abstract method for random access of the expression."""

    def getNextValueTruth(self):
        """Returns truth value of the next expression or Stops the
        iteration handle if end is reached.
        """
        iteration_value = self.getNextValueExpression()
        if iteration_value is None:
            return StopIteration
        return iteration_value.getTruthValue()

    def getAllElementTruthValue(self):
        """Returns truth value for 'all' on 'lists'. It returns
        True: if all the elements of the list are True,
        False: if any element in the list is False,
        None: if number of elements in the list is greater than
        256 or any element is Unknown.
        """
        all_true = True
        count = 0
        while True:
            truth_value = self.getNextValueTruth()
            if truth_value is StopIteration:
                break

            if count > 256:
                return None

            if truth_value is False:
                return False

            if truth_value is None:
                all_true = None

            count += 1

        return all_true
Ejemplo n.º 2
0
class IterationHandleBase(getMetaClassBase("IterationHandle")):
    """Base class for Iteration Handles."""

    @abstractmethod
    def getNextValueExpression(self):
        """Abstract method to get next iteration value."""

    @abstractmethod
    def getIterationValueWithIndex(self, value_index):
        # TODO: Have one doc string, and it applies to all derived methods.
        """Abstract method for random access of the expression."""

    def getNextValueTruth(self):
        """Returns truth value of the next expression or Stops the
        iteration handle if end is reached.
        """
        iteration_value = self.getNextValueExpression()
        if iteration_value is None:
            return StopIteration
        return iteration_value.getTruthValue()
Ejemplo n.º 3
0
class NuitkaPluginBase(getMetaClassBase("Plugin")):
    """Nuitka base class for all plug-ins.

    Derive your plugin from "NuitkaPluginBase" please.
    For instructions, see https://github.com/Nuitka/Nuitka/blob/orsiris/UserPlugin-Creation.rst

    Plugins allow to adapt Nuitka's behaviour in a number of ways as explained
    below at the individual methods.

    It is used to deal with special requirements some packages may have (e.g. PyQt
    and tkinter), data files to be included (e.g. certifi), inserting hidden
    code, coping with otherwise undetectable needs, or issuing messages in
    certain situations.

    A plugin in general must be enabled to be used by Nuitka. This happens by
    specifying "--plugin-enable" (standard plugins) or by "--user-plugin" (user
    plugins) in the Nuitka command line. However, some plugins are always enabled
    and invisible to the user.

    Nuitka comes with a number of "standard" plugins to be enabled as needed.
    What they are can be displayed using "nuitka --plugin-list file.py" (filename
    required but ignored).

    User plugins may be specified (and implicitly enabled) using their Python
    script pathname.
    """

    # Standard plugins must provide this as a unique string which Nuitka
    # then uses to identify them.
    #
    # User plugins are identified by their path and implicitly activated.
    # They however still need to specify some arbitrary non-blank string here,
    # which does not equal the name of an inactivated standard plugin.
    # For working with options, user plugins must set this variable to
    # the script's path (use __file__, __module__ or __name__).
    plugin_name = None

    @staticmethod
    def isAlwaysEnabled():
        """Request to be always enabled.

        Notes:
            Setting this to true is only applicable to standard plugins. In
            this case, the plugin will be enabled upon Nuitka start-up. Any
            plugin detector class will then be ignored. Method isRelevant() may
            also be present and can be used to fine-control enabling the
            plugin: A to-be-enabled, but irrelevant plugin will still not be
            activated.
        Returns:
            True or False
        """
        return False

    @classmethod
    def isRelevant(cls):
        """Consider if the plugin is relevant.

        Notes:
            A plugin may only be a needed on a certain OS, or with some options,
            but this is only a class method, so you will not have much run time
            information.

        Returns:
            True or False

        """
        return True

    @classmethod
    def addPluginCommandLineOptions(cls, group):
        # Call group.add_option() here.
        pass

    @classmethod
    def getPluginDefaultOptionValues(cls):
        """This method is used to get a values to use as defaults.

        Since the defaults are in the command line options, we call
        that and extract them.
        """

        from optparse import OptionGroup, OptionParser

        parser = OptionParser()
        group = OptionGroup(parser, "Pseudo Target")
        cls.addPluginCommandLineOptions(group)

        result = {}
        for option in group.option_list:
            result[option.dest] = option.default

        return result

    def isRequiredImplicitImport(self, module, full_name):
        """Indicate whether an implicitly imported module should be accepted.

        Notes:
            You may negate importing a module specified as "implicit import",
            although this is an unexpected event.

        Args:
            module: the module object
            full_name: of the implicitly import module
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return True

    def getImplicitImports(self, module):
        """Return the implicit imports for a given module (iterator).

        Args:
            module: the module object
        Yields:
            implicit imports for the module
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    # Provide fall-back for failed imports here.
    module_aliases = {}

    def considerFailedImportReferrals(self, module_name):
        """Provide a dictionary of fallback imports for modules that failed to import.

        Args:
            module_name: name of module
        Returns:
            dict
        """
        return self.module_aliases.get(module_name)

    def onModuleSourceCode(self, module_name, source_code):
        """Inspect or modify source code.

        Args:
            module_name: (str) name of module
            source_code: (str) its source code
        Returns:
            source_code (str)
        Notes:
            Default implementation forwards to `checkModuleSourceCode` which is
            going to allow simply checking the source code without the need to
            pass it back.
        """
        self.checkModuleSourceCode(module_name, source_code)

        return source_code

    def checkModuleSourceCode(self, module_name, source_code):
        """Inspect source code.

        Args:
            module_name: (str) name of module
            source_code: (str) its source code
        Returns:
            None
        """

    def onFrozenModuleSourceCode(self, module_name, is_package, source_code):
        """Inspect or modify frozen module source code.

        Args:
            module_name: (str) full name of module
            is_package: (bool) True indicates a package
            source_code: (str) its source code
        Returns:
            source_code (str)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return source_code

    def onFrozenModuleBytecode(self, module_name, is_package, bytecode):
        """Inspect or modify frozen module byte code.

        Args:
            module_name: (str) name of module
            is_package: (bool) True indicates a package
            bytecode: (bytes) byte code
        Returns:
            bytecode (bytes)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return bytecode

    @staticmethod
    def _createTriggerLoadedModule(module, trigger_name, code, flags):
        """Create a "trigger" for a module to be imported.

        Notes:
            The trigger will incorpaorate the code to be prepended / appended.
            Called by @onModuleDiscovered.

        Args:
            module: the module object (serves as dict key)
            trigger_name: string ("-preload"/"-postload")
            code: the code string

        Returns
            trigger_module
        """
        from nuitka.nodes.ModuleNodes import CompiledPythonModule
        from nuitka.tree.Building import createModuleTree

        from .Plugins import Plugins

        module_name = ModuleName(module.getFullName() + trigger_name)
        source_ref = fromFilename(module.getCompileTimeFilename() +
                                  trigger_name)

        mode = Plugins.decideCompilation(module_name, source_ref)

        trigger_module = CompiledPythonModule(
            module_name=module_name,
            is_top=False,
            mode=mode,
            future_spec=None,
            source_ref=source_ref,
        )

        createModuleTree(
            module=trigger_module,
            source_ref=module.getSourceReference(),
            source_code=code,
            is_main=False,
        )

        if mode == "bytecode":
            trigger_module.setSourceCode(code)

        # In debug mode, put the files in the build folder, so they can be looked up easily.
        if Options.is_debug and "HIDE_SOURCE" not in flags:
            source_path = os.path.join(
                OutputDirectories.getSourceDirectoryPath(),
                module_name + ".py")

            putTextFileContents(filename=source_path, contents=code)

        return trigger_module

    @staticmethod
    def createPreModuleLoadCode(module):
        """Create code to execute before importing a module.

        Notes:
            Called by @onModuleDiscovered.

        Args:
            module: the module object
        Returns:
            None (does not apply, default)
            tuple (code, documentary string)
            tuple (code, documentary string, flags)
        """
        # Virtual method, pylint: disable=unused-argument
        return None

    @staticmethod
    def createPostModuleLoadCode(module):
        """Create code to execute after loading to a module.

        Notes:
            Called by @onModuleDiscovered.

        Args:
            module: the module object

        Returns:
            None (does not apply, default)
            tuple (code, documentary string)
            tuple (code, documentary string, flags)
        """
        # Virtual method, pylint: disable=unused-argument
        return None

    def onModuleDiscovered(self, module):
        """Called with a module to be loaded.

        Notes:
            We may specify code to be prepended and/or appended to this module.
            This code is stored in the appropriate dict.
            For every imported module and each of these two options, only one plugin may do this.
            We check this condition here.

        Args:
            module: the module object
        Returns:
            None
        """

        full_name = module.getFullName()

        preload_desc = self.createPreModuleLoadCode(module)

        if preload_desc:
            if len(preload_desc) == 2:
                pre_code, reason = preload_desc
                flags = ()
            else:
                pre_code, reason, flags = preload_desc

            if pre_code:
                # Note: We could find a way to handle this if needed.
                if full_name in pre_modules:
                    plugins_logger.sysexit(
                        "Error, conflicting pre module code from plug-ins for %s"
                        % full_name)

                self.info("Injecting pre-module load code for module '%s':" %
                          full_name)
                for line in reason.split("\n"):
                    self.info("    " + line)

                pre_modules[full_name] = self._createTriggerLoadedModule(
                    module=module,
                    trigger_name="-preLoad",
                    code=pre_code,
                    flags=flags)

        post_desc = self.createPostModuleLoadCode(module)

        if post_desc:
            if len(post_desc) == 2:
                post_code, reason = post_desc
                flags = ()
            else:
                post_code, reason, flags = post_desc

            if post_code:
                # Note: We could find a way to handle this if needed.
                if full_name is post_modules:
                    plugins_logger.sysexit(
                        "Error, conflicting post module code from plug-ins for %s"
                        % full_name)

                self.info("Injecting post-module load code for module '%s':" %
                          full_name)
                for line in reason.split("\n"):
                    self.info("    " + line)

                post_modules[full_name] = self._createTriggerLoadedModule(
                    module=module,
                    trigger_name="-postLoad",
                    code=post_code,
                    flags=flags)

    def onModuleEncounter(self, module_filename, module_name, module_kind):
        """Help decide whether to include a module.

        Args:
            module_filename: filename
            module_name: full module name
            module_kind: one of "py", "shlib" (shared library)
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onModuleInitialSet(self):
        """Provide extra modules to the initial root module set.

        Args:
            None
        Returns:
            Iterable of modules, may yield.
        """
        # Virtual method, pylint: disable=no-self-use
        return ()

    @staticmethod
    def locateModule(importing, module_name):
        """Provide a filename / -path for a to-be-imported module.

        Args:
            importing: module object that asked for it (tracing only)
            module_name: (str or ModuleName) full name of module
            warn: (bool) True if required module
        Returns:
            filename for module
        """
        from nuitka.importing import Importing

        _module_package, module_filename, _finding = Importing.findModule(
            importing=importing,
            module_name=ModuleName(module_name),
            parent_package=None,
            level=-1,
            warn=False,
        )

        return module_filename

    def locateModules(self, importing, module_name):
        """Provide a filename / -path for a to-be-imported module.

        Args:
            importing: module object that asked for it (tracing only)
            module_name: (str or ModuleName) full name of module
            warn: (bool) True if required module
        Returns:
            list of ModuleName
        """
        module_path = self.locateModule(importing, module_name)

        result = []

        def _scanModules(path, prefix):
            for module_info in pkgutil.walk_packages((path, ),
                                                     prefix=prefix + "."):
                result.append(ModuleName(module_info[1]))

                if module_info[2]:
                    _scanModules(module_info[1], module_name + module_info[1])

        _scanModules(module_path, module_name)

        return result

    def considerExtraDlls(self, dist_dir, module):
        """Provide a tuple of names of binaries to be included.

        Args:
            dist_dir: the distribution folder
            module: the module object needing the binaries
        Returns:
            tuple
        """
        # TODO: This should no longer be here, as this API is obsolete, pylint: disable=unused-argument

        for included_entry_point in self.getExtraDlls(module):
            # Copy to the dist directory, which normally should not be our task, but is for now.
            makePath(os.path.dirname(included_entry_point.dest_path))

            shutil.copyfile(included_entry_point.source_path,
                            included_entry_point.dest_path)

            yield included_entry_point

    def getExtraDlls(self, module):
        """Provide IncludedEntryPoint named tuples describing extra needs of the module.

        Args:
            module: the module object needing the binaries
        Returns:
            yields IncludedEntryPoint objects

        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def removeDllDependencies(self, dll_filename, dll_filenames):
        """Yield any DLLs / shared libraries not to be included in distribution.

        Args:
            dll_filename: DLL name
            dll_filenames: list of DLLs
        Yields:
            yielded filenames to exclude
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def considerDataFiles(self, module):
        """Yield data file names (source|func, target) for inclusion (iterator).

        Args:
            module: module object that may need extra data files
        Yields:
            Data file description pairs, either (source, dest) or (func, dest)
            where the func will be called to create the content dynamically.

        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def onStandaloneDistributionFinished(self, dist_dir):
        """Called after successfully creating a standalone distribution.

        Note:
            It is up to the plugin to take subsequent action. Examples are:
            insert additional information (license, copyright, company or
            application description), create installation material, further
            folder clean-up, start downstream applications etc.

        Args:
            dist_dir: the created distribution folder

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onOnefileFinished(self, filename):
        """Called after successfully creating a onefile executable.

        Note:
            It is up to the plugin to take subsequent action. Examples are:
            insert additional information (license, copyright, company or
            application description), create installation material, further
            folder clean-up, start downstream applications etc.

        Args:
            filename: the created onefile executable

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onFinalResult(self, filename):
        """Called after successfully finishing a compilation.

        Note:
            Plugins normally don't need this, and what filename is will be
            heavily dependent on compilation modes. Actions can be take here,
            e.g. commercial plugins output generated keys near that executable
            path.
        Args:
            filename: the created binary (module, accelerated exe, dist exe, onefile exe)

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def suppressUnknownImportWarning(self, importing, module_name, source_ref):
        """Suppress import warnings for unknown modules.

        Args:
            importing: the module object
            module_name: name of module
            source_ref: ???
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return False

    def decideCompilation(self, module_name, source_ref):
        """Decide whether to compile a module (or just use its bytecode).

        Notes:
            The first plugin not returning None makes the decision. Thereafter,
            no other plugins will be checked. If all plugins return None, the
            module will be compiled.

        Args:
            module_name: name of module
            source_ref: ???

        Returns:
            "compiled" or "bytecode" or None (default)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def getPreprocessorSymbols(self):
        """Decide which C defines to be used in compilation.

        Notes:
            The plugins can each contribute, but are hopefully using
            a namespace for their defines.

        Returns:
            None for no defines, otherwise dictionary of key to be
            defined, and non-None values if any, i.e. no "-Dkey" only
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraCodeFiles(self):
        """Add extra code files to the compilation.

        Notes:
            This is generally a bad idea to use unless you absolutely
            know what you are doing.

        Returns:
            None for no extra codes, otherwise dictionary of key to be
            filename, and value to be source code.
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraLinkLibraries(self):
        """Decide which link library should be added.

        Notes:
            Names provided multiple times, e.g. by multiple plugins are
            only added once.

        Returns:
            None for no extra link library, otherwise the name as a **str**
            or an iterable of names of link libraries.
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def warnUnusedPlugin(self, message):
        """An inactive plugin may issue a warning if it believes this may be wrong.

        Returns:
            None
        """
        if self.plugin_name not in warned_unused_plugins:
            warned_unused_plugins.add(self.plugin_name)

            plugins_logger.warning("Use '--plugin-enable=%s' for: %s" %
                                   (self.plugin_name, message))

    def onDataComposerResult(self, blob_filename):
        """Internal use only.

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    _runtime_information_cache = {}

    def queryRuntimeInformationMultiple(self, info_name, setup_codes, values):
        if info_name in self._runtime_information_cache:
            return self._runtime_information_cache[info_name]

        keys = []
        query_codes = []

        for key, value_expression in values:
            keys.append(key)

            query_codes.append("print(repr(%s))" % value_expression)
            query_codes.append('print("-" * 27)')

        if type(setup_codes) is str:
            setup_codes = [setup_codes]

        cmd = r"""\
from __future__ import print_function

%(setup_codes)s
%(query_codes)s
""" % {
            "setup_codes": "\n".join(setup_codes),
            "query_codes": "\n".join(query_codes),
        }

        feedback = check_output([sys.executable, "-c", cmd])

        if str is not bytes:  # We want to work with strings, that's hopefully OK.
            feedback = feedback.decode("utf8")

        # Ignore Windows newlines difference.
        feedback = [line.strip() for line in feedback.splitlines()]

        if feedback.count("-" * 27) != len(keys):
            self.sysexit(
                "Error, mismatch in output retrieving %r information." %
                info_name)

        feedback = [line for line in feedback if line != "-" * 27]

        NamedTupleResult = namedtuple(info_name, keys)

        # We are being lazy here, the code is trusted, pylint: disable=eval-used
        self._runtime_information_cache[info_name] = NamedTupleResult(
            *(eval(value) for value in feedback))

        return self._runtime_information_cache[info_name]

    def queryRuntimeInformationSingle(self, setup_codes, value):
        return self.queryRuntimeInformationMultiple(
            info_name="temp_info_for_" + self.plugin_name,
            setup_codes=setup_codes,
            values=(("key", value), ),
        ).key

    @classmethod
    def warning(cls, message):
        plugins_logger.warning(cls.plugin_name + ": " + message)

    @classmethod
    def info(cls, message):
        plugins_logger.info(cls.plugin_name + ": " + message)

    @classmethod
    def sysexit(cls, message):
        plugins_logger.sysexit(cls.plugin_name + ": " + message)
Ejemplo n.º 4
0
class NuitkaPluginBase(getMetaClassBase("Plugin")):
    """Nuitka base class for all plugins.

    Derive your plugin from "NuitkaPluginBase" please.
    For instructions, see https://github.com/Nuitka/Nuitka/blob/orsiris/UserPlugin-Creation.rst

    Plugins allow to adapt Nuitka's behaviour in a number of ways as explained
    below at the individual methods.

    It is used to deal with special requirements some packages may have (e.g. PyQt
    and tkinter), data files to be included (e.g. certifi), inserting hidden
    code, coping with otherwise undetectable needs, or issuing messages in
    certain situations.

    A plugin in general must be enabled to be used by Nuitka. This happens by
    specifying "--enable-plugin" (standard plugins) or by "--user-plugin" (user
    plugins) in the Nuitka command line. However, some plugins are always enabled
    and invisible to the user.

    Nuitka comes with a number of "standard" plugins to be enabled as needed.
    What they are can be displayed using "nuitka --plugin-list file.py" (filename
    required but ignored).

    User plugins may be specified (and implicitly enabled) using their Python
    script pathname.
    """

    # Standard plugins must provide this as a unique string which Nuitka
    # then uses to identify them.
    #
    # User plugins are identified by their path and implicitly activated.
    # They however still need to specify some arbitrary non-blank string here,
    # which does not equal the name of an inactivated standard plugin.
    # For working with options, user plugins must set this variable to
    # the script's path (use __file__, __module__ or __name__).
    plugin_name = None

    @staticmethod
    def isAlwaysEnabled():
        """Request to be always enabled.

        Notes:
            Setting this to true is only applicable to standard plugins. In
            this case, the plugin will be enabled upon Nuitka start-up. Any
            plugin detector class will then be ignored. Method isRelevant() may
            also be present and can be used to fine-control enabling the
            plugin: A to-be-enabled, but irrelevant plugin will still not be
            activated.
        Returns:
            True or False
        """
        return False

    @classmethod
    def isRelevant(cls):
        """Consider if the plugin is relevant.

        Notes:
            A plugin may only be a needed on a certain OS, or with some options,
            but this is only a class method, so you will not have much run time
            information.

        Returns:
            True or False

        """
        return True

    @classmethod
    def addPluginCommandLineOptions(cls, group):
        # Call group.add_option() here.
        pass

    @classmethod
    def getTagDataFileTagOptions(cls):
        # Return tag_name, description tuples
        return ()

    @classmethod
    def getPluginDefaultOptionValues(cls):
        """This method is used to get a values to use as defaults.

        Since the defaults are in the command line options, we call
        that and extract them.
        """

        from optparse import OptionGroup, OptionParser

        parser = OptionParser()
        group = OptionGroup(parser, "Pseudo Target")
        cls.addPluginCommandLineOptions(group)

        result = {}
        for option in group.option_list:
            result[option.dest] = option.default

        return result

    def isRequiredImplicitImport(self, module, full_name):
        """Indicate whether an implicitly imported module should be accepted.

        Notes:
            You may negate importing a module specified as "implicit import",
            although this is an unexpected event.

        Args:
            module: the module object
            full_name: of the implicitly import module
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return True

    def getImplicitImports(self, module):
        """Return the implicit imports for a given module (iterator).

        Args:
            module: the module object
        Yields:
            implicit imports for the module
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def onModuleSourceCode(self, module_name, source_code):
        """Inspect or modify source code.

        Args:
            module_name: (str) name of module
            source_code: (str) its source code
        Returns:
            source_code (str)
        Notes:
            Default implementation forwards to `checkModuleSourceCode` which is
            going to allow simply checking the source code without the need to
            pass it back.
        """
        self.checkModuleSourceCode(module_name, source_code)

        return source_code

    def checkModuleSourceCode(self, module_name, source_code):
        """Inspect source code.

        Args:
            module_name: (str) name of module
            source_code: (str) its source code
        Returns:
            None
        """

    def onFrozenModuleSourceCode(self, module_name, is_package, source_code):
        """Inspect or modify frozen module source code.

        Args:
            module_name: (str) full name of module
            is_package: (bool) True indicates a package
            source_code: (str) its source code
        Returns:
            source_code (str)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return source_code

    def onFrozenModuleBytecode(self, module_name, is_package, bytecode):
        """Inspect or modify frozen module byte code.

        Args:
            module_name: (str) name of module
            is_package: (bool) True indicates a package
            bytecode: (bytes) byte code
        Returns:
            bytecode (bytes)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return bytecode

    @staticmethod
    def createPreModuleLoadCode(module):
        """Create code to execute before importing a module.

        Notes:
            Called by @onModuleDiscovered.

        Args:
            module: the module object
        Returns:
            None (does not apply, default)
            tuple (code, documentary string)
            tuple (code, documentary string, flags)
        """
        # Virtual method, pylint: disable=unused-argument
        return None

    @staticmethod
    def createPostModuleLoadCode(module):
        """Create code to execute after loading to a module.

        Notes:
            Called by @onModuleDiscovered.

        Args:
            module: the module object

        Returns:
            None (does not apply, default)
            tuple (code, documentary string)
            tuple (code, documentary string, flags)
        """
        # Virtual method, pylint: disable=unused-argument
        return None

    @staticmethod
    def createFakeModuleDependency(module):
        """Create module to depend on.

        Notes:
            Called by @onModuleDiscovered.

        Args:
            module: the module object

        Returns:
            None (does not apply, default)
            tuple (code, reason)
            tuple (code, reason, flags)
        """
        # Virtual method, pylint: disable=unused-argument
        return None

    @staticmethod
    def hasPreModuleLoadCode(module_name):
        return (getModuleInclusionInfoByName(
            makeTriggerModuleName(module_name, preload_trigger_name))
                is not None)

    @staticmethod
    def hasPostModuleLoadCode(module_name):
        return (getModuleInclusionInfoByName(
            makeTriggerModuleName(module_name, postload_trigger_name))
                is not None)

    def onModuleDiscovered(self, module):
        """Called with a module to be loaded.

        Notes:
            We may specify code to be prepended and/or appended to this module.
            This code is stored in the appropriate dict.
            For every imported module and each of these two options, only one plugin may do this.
            We check this condition here.

        Args:
            module: the module object
        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onModuleEncounter(self, module_filename, module_name, module_kind):
        """Help decide whether to include a module.

        Args:
            module_filename: filename
            module_name: full module name
            module_kind: one of "py", "extension" (shared library)
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onModuleInitialSet(self):
        """Provide extra modules to the initial root module set.

        Args:
            None
        Returns:
            Iterable of modules, may yield.
        """
        # Virtual method, pylint: disable=no-self-use
        return ()

    def onModuleCompleteSet(self, module_set):
        """Provide extra modules to the initial root module set.

        Args:
            module_set - tuple of module objects
        Returns:
            None
        Notes:
            You must not change anything, this is purely for warning
            and error checking, and potentially for later stages to
            prepare.
        """

    @staticmethod
    def locateModule(module_name):
        """Provide a filename / -path for a to-be-imported module.

        Args:
            importing: module object that asked for it (tracing only)
            module_name: (str or ModuleName) full name of module
        Returns:
            filename for module
        """

        from nuitka.importing.Importing import locateModule

        _module_name, module_filename, _finding = locateModule(
            module_name=ModuleName(module_name), parent_package=None, level=0)

        return module_filename

    @staticmethod
    def locateModules(module_name):
        """Provide a filename / -path for a to-be-imported module.

        Args:
            module_name: (str or ModuleName) full name of module
        Returns:
            list of ModuleName
        """

        from nuitka.importing.Importing import locateModules

        return locateModules(module_name)

    @classmethod
    def locateDLL(cls, dll_name):
        """Locate a DLL by name."""
        return locateDLL(dll_name)

    @classmethod
    def locateDLLsInDirectory(cls, directory):
        """Locate all DLLs in a folder

        Returns:
            list of (filename, filename_relative, dll_extension)
        """
        return locateDLLsInDirectory(directory)

    @classmethod
    def makeDllEntryPoint(cls, source_path, dest_path, package_name):
        """Create an entry point, as expected to be provided by getExtraDlls."""
        return makeDllEntryPoint(source_path=source_path,
                                 dest_path=dest_path,
                                 package_name=package_name)

    def reportFileCount(self, module_name, count, section=None):
        if count:
            msg = "Found %d %s DLLs from %s%s installation." % (
                count,
                "file" if count < 2 else "files",
                "" if not section else (" '%s' " % section),
                module_name.asString(),
            )

            self.info(msg)

    def considerExtraDlls(self, dist_dir, module):
        """Provide a tuple of names of binaries to be included.

        Args:
            dist_dir: the distribution folder
            module: the module object needing the binaries
        Returns:
            tuple
        """
        # TODO: This should no longer be here, as this API is obsolete, pylint: disable=unused-argument
        for included_entry_point in self.getExtraDlls(module):
            # Copy to the dist directory, which normally should not be a plugin task, but is for now.
            makePath(os.path.dirname(included_entry_point.dest_path))

            copyFile(included_entry_point.source_path,
                     included_entry_point.dest_path)

            yield included_entry_point

    def getExtraDlls(self, module):
        """Provide IncludedEntryPoint named tuples describing extra needs of the module.

        Args:
            module: the module object needing the binaries
        Returns:
            yields IncludedEntryPoint objects

        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def onCopiedDLL(self, dll_filename):
        """Chance for a plugin to modify DLLs after copy, e.g. to compress it, remove attributes, etc.

        Args:
            dll_filename: the filename of the DLL

        Notes:
            Do not remove or add any files in this method, this will not work well, there
            is e.g. getExtraDLLs API to add things. This is only for post processing as
            described above.

        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def getModuleSpecificDllPaths(self, module_name):
        """Provide a list of directories, where DLLs should be searched for this package (or module).

        Args:
            module_name: name of a package or module, for which the DLL path addition applies.
        Returns:
            iterable of paths
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def removeDllDependencies(self, dll_filename, dll_filenames):
        """Yield any DLLs / shared libraries not to be included in distribution.

        Args:
            dll_filename: DLL name
            dll_filenames: list of DLLs
        Yields:
            yielded filenames to exclude
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def considerDataFiles(self, module):
        """Yield data file names (source|func, target) for inclusion (iterator).

        Args:
            module: module object that may need extra data files
        Yields:
            Data file description pairs, either (source, dest) or (func, dest)
            where the func will be called to create the content dynamically.

        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return ()

    def makeIncludedDataFile(self, source_path, dest_path, reason, tags=""):
        return makeIncludedDataFile(
            source_path=source_path,
            dest_path=dest_path,
            reason=reason,
            tracer=self,
            tags=tags,
        )

    def makeIncludedGeneratedDataFile(self, data, dest_path, reason, tags=""):
        return makeIncludedGeneratedDataFile(data=data,
                                             dest_path=dest_path,
                                             reason=reason,
                                             tracer=self,
                                             tags=tags)

    def makeIncludedDataDirectory(
            self,
            source_path,
            dest_path,
            reason,
            tags="",
            ignore_dirs=(),
            ignore_filenames=(),
            ignore_suffixes=(),
            only_suffixes=(),
            normalize=True,
    ):
        return makeIncludedDataDirectory(
            source_path=source_path,
            dest_path=dest_path,
            reason=reason,
            tracer=self,
            tags=tags,
            ignore_dirs=ignore_dirs,
            ignore_filenames=ignore_filenames,
            ignore_suffixes=ignore_suffixes,
            only_suffixes=only_suffixes,
            normalize=normalize,
        )

    def makeIncludedEmptyDirectories(self, source_path, dest_paths, reason,
                                     tags):
        return makeIncludedEmptyDirectories(
            source_path=source_path,
            dest_paths=dest_paths,
            reason=reason,
            tracer=self,
            tags=tags,
        )

    def updateDataFileTags(self, included_datafile):
        """Add or remove data file tags."""

    def onDataFileTags(self, included_datafile):
        """Action on data file tags."""

    def onBeforeCodeParsing(self):
        """Prepare for code parsing, normally not needed."""

    def onStandaloneDistributionFinished(self, dist_dir):
        """Called after successfully creating a standalone distribution.

        Note:
            It is up to the plugin to take subsequent action. Examples are:
            insert additional information (license, copyright, company or
            application description), create installation material, further
            folder clean-up, start downstream applications etc.

        Args:
            dist_dir: the created distribution folder

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onOnefileFinished(self, filename):
        """Called after successfully creating a onefile executable.

        Note:
            It is up to the plugin to take subsequent action. Examples are:
            insert additional information (license, copyright, company or
            application description), create installation material, further
            folder clean-up, start downstream applications etc.

        Args:
            filename: the created onefile executable

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onBootstrapBinary(self, filename):
        """Called after successfully creating a bootstrap binary, but without payload.

        Args:
            filename: the created bootstrap binary, will be modified later

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def onFinalResult(self, filename):
        """Called after successfully finishing a compilation.

        Note:
            Plugins normally don't need this, and what filename is will be
            heavily dependent on compilation modes. Actions can be take here,
            e.g. commercial plugins output generated keys near that executable
            path.
        Args:
            filename: the created binary (module, accelerated exe, dist exe, onefile exe)

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def suppressUnknownImportWarning(self, importing, module_name, source_ref):
        """Suppress import warnings for unknown modules.

        Args:
            importing: the module object
            module_name: name of module
            source_ref: ???
        Returns:
            True or False
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return False

    def decideCompilation(self, module_name):
        """Decide whether to compile a module (or just use its bytecode).

        Notes:
            The first plugin not returning None makes the decision. Thereafter,
            no other plugins will be checked. If all plugins return None, the
            module will be compiled.

        Args:
            module_name: name of module

        Returns:
            "compiled" or "bytecode" or None (no opinion, use by default)
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def getPreprocessorSymbols(self):
        """Decide which C defines to be used in compilation.

        Notes:
            The plugins can each contribute, but are hopefully using
            a namespace for their defines.

        Returns:
            None for no defines, otherwise dictionary of key to be
            defined, and non-None values if any, i.e. no "-Dkey" only
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraIncludeDirectories(self):
        """Decide which extra directories to use for C includes in compilation.

        Returns:
            List of directories or None by default
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraCodeFiles(self):
        """Add extra code files to the compilation.

        Notes:
            This is generally a bad idea to use unless you absolutely
            know what you are doing.

        Returns:
            None for no extra codes, otherwise dictionary of key to be
            filename, and value to be source code.
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraLinkLibraries(self):
        """Decide which link library should be added.

        Notes:
            Names provided multiple times, e.g. by multiple plugins are
            only added once.

        Returns:
            None for no extra link library, otherwise the name as a **str**
            or an iterable of names of link libraries.
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def getExtraLinkDirectories(self):
        """Decide which link directories should be added.

        Notes:
            Directories provided multiple times, e.g. by multiple plugins are
            only added once.

        Returns:
            None for no extra link directory, otherwise the name as a **str**
            or an iterable of names of link directories.
        """

        # Virtual method, pylint: disable=no-self-use
        return None

    def warnUnusedPlugin(self, message):
        """An inactive plugin may issue a warning if it believes this may be wrong.

        Returns:
            None
        """
        if self.plugin_name not in warned_unused_plugins:
            warned_unused_plugins.add(self.plugin_name)

            plugins_logger.warning("Use '--enable-plugin=%s' for: %s" %
                                   (self.plugin_name, message))

    def onDataComposerRun(self):
        """Internal use only.

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use
        return None

    def onDataComposerResult(self, blob_filename):
        """Internal use only.

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    def encodeDataComposerName(self, data_name):
        """Internal use only.

        Returns:
            None
        """
        # Virtual method, pylint: disable=no-self-use,unused-argument
        return None

    _runtime_information_cache = {}

    def queryRuntimeInformationMultiple(self, info_name, setup_codes, values):
        info_name = self.plugin_name.replace("-", "_") + "_" + info_name

        if info_name in self._runtime_information_cache:
            return self._runtime_information_cache[info_name]

        keys = []
        query_codes = []

        for key, value_expression in values:
            keys.append(key)

            query_codes.append("print(repr(%s))" % value_expression)
            query_codes.append('print("-" * 27)')

        if type(setup_codes) is str:
            setup_codes = setup_codes.split("\n")

        cmd = r"""\
from __future__ import print_function
from __future__ import absolute_import

try:
    %(setup_codes)s
except ImportError:
    import sys
    sys.exit(38)
%(query_codes)s
""" % {
            "setup_codes": "\n   ".join(setup_codes),
            "query_codes": "\n".join(query_codes),
        }

        try:
            feedback = check_output([sys.executable, "-c", cmd])
        except NuitkaCalledProcessError as e:
            if e.returncode == 38:
                return None
            raise

        if str is not bytes:  # We want to work with strings, that's hopefully OK.
            feedback = feedback.decode("utf8")

        # Ignore Windows newlines difference.
        feedback = [line.strip() for line in feedback.splitlines()]

        if feedback.count("-" * 27) != len(keys):
            self.sysexit(
                "Error, mismatch in output retrieving %r information." %
                info_name)

        feedback = [line for line in feedback if line != "-" * 27]

        NamedTupleResult = namedtuple(info_name, keys)

        # We are being lazy here, the code is trusted, pylint: disable=eval-used
        self._runtime_information_cache[info_name] = NamedTupleResult(
            *(eval(value) for value in feedback))

        return self._runtime_information_cache[info_name]

    def queryRuntimeInformationSingle(self, setup_codes, value):
        return self.queryRuntimeInformationMultiple(
            info_name="temp_info_for_" + self.plugin_name.replace("-", "_"),
            setup_codes=setup_codes,
            values=(("key", value), ),
        ).key

    @staticmethod
    def onFunctionBodyParsing(module_name, function_name, body):
        pass

    @classmethod
    def warning(cls, message):
        plugins_logger.warning(cls.plugin_name + ": " + message)

    @classmethod
    def info(cls, message):
        plugins_logger.info(cls.plugin_name + ": " + message)

    @classmethod
    def sysexit(cls, message):
        plugins_logger.sysexit(cls.plugin_name + ": " + message)
Ejemplo n.º 5
0
class PythonContextBase(getMetaClassBase("Context")):
    @counted_init
    def __init__(self):
        self.source_ref = None

        self.current_source_ref = None

    if isCountingInstances():
        __del__ = counted_del()

    def getCurrentSourceCodeReference(self):
        return self.current_source_ref

    def setCurrentSourceCodeReference(self, value):
        result = self.current_source_ref
        self.current_source_ref = value

        return result

    @contextmanager
    def withCurrentSourceCodeReference(self, value):
        old_source_ref = self.setCurrentSourceCodeReference(value)

        yield old_source_ref

        self.setCurrentSourceCodeReference(value)

    def getInplaceLeftName(self):
        return self.allocateTempName("inplace_orig", "PyObject *", True)

    @abstractmethod
    def getConstantCode(self, constant, deep_check=False):
        pass

    @abstractmethod
    def getModuleCodeName(self):
        pass

    @abstractmethod
    def getModuleName(self):
        pass

    @abstractmethod
    def addHelperCode(self, key, code):
        pass

    @abstractmethod
    def hasHelperCode(self, key):
        pass

    @abstractmethod
    def addDeclaration(self, key, code):
        pass

    @abstractmethod
    def pushFrameVariables(self, frame_variables):
        pass

    @abstractmethod
    def popFrameVariables(self):
        pass

    @abstractmethod
    def getFrameVariableTypeDescriptions(self):
        pass

    @abstractmethod
    def getFrameVariableTypeDescription(self):
        pass

    @abstractmethod
    def getFrameTypeDescriptionDeclaration(self):
        pass

    @abstractmethod
    def getFrameVariableCodeNames(self):
        pass

    @abstractmethod
    def allocateTempName(self, base_name, type_name="PyObject *", unique=False):
        pass

    @abstractmethod
    def skipTempName(self, base_name):
        pass

    @abstractmethod
    def getIntResName(self):
        pass

    @abstractmethod
    def getBoolResName(self):
        pass

    @abstractmethod
    def hasTempName(self, base_name):
        pass

    @abstractmethod
    def getExceptionEscape(self):
        pass

    @abstractmethod
    def setExceptionEscape(self, label):
        pass

    @abstractmethod
    def getLoopBreakTarget(self):
        pass

    @abstractmethod
    def setLoopBreakTarget(self, label):
        pass

    @abstractmethod
    def getLoopContinueTarget(self):
        pass

    @abstractmethod
    def setLoopContinueTarget(self, label):
        pass

    @abstractmethod
    def allocateLabel(self, label):
        pass

    @abstractmethod
    def allocateExceptionKeeperVariables(self):
        pass

    @abstractmethod
    def getExceptionKeeperVariables(self):
        pass

    @abstractmethod
    def setExceptionKeeperVariables(self, keeper_vars):
        pass

    @abstractmethod
    def addExceptionPreserverVariables(self, count):
        pass

    @abstractmethod
    def getTrueBranchTarget(self):
        pass

    @abstractmethod
    def getFalseBranchTarget(self):
        pass

    @abstractmethod
    def setTrueBranchTarget(self, label):
        pass

    @abstractmethod
    def setFalseBranchTarget(self, label):
        pass

    @abstractmethod
    def getCleanupTempnames(self):
        pass

    @abstractmethod
    def addCleanupTempName(self, tmp_name):
        pass

    @abstractmethod
    def removeCleanupTempName(self, tmp_name):
        pass

    @abstractmethod
    def needsCleanup(self, tmp_name):
        pass

    @abstractmethod
    def pushCleanupScope(self):
        pass

    @abstractmethod
    def popCleanupScope(self):
        pass
Ejemplo n.º 6
0
class Variable(getMetaClassBase("Variable")):

    # We will need all of these attributes, since we track the global
    # state and cache some decisions as attributes. TODO: But in some
    # cases, part of the these might be moved to the outside.
    __slots__ = (
        "variable_name",
        "owner",
        "version_number",
        "shared_users",
        "traces",
        "users",
        "writers",
    )

    @InstanceCounters.counted_init
    def __init__(self, owner, variable_name):
        assert type(variable_name) is str, variable_name
        assert type(owner) not in (tuple, list), owner

        self.variable_name = variable_name
        self.owner = owner

        self.version_number = 0

        self.shared_users = False

        self.traces = set()

        # Derived from all traces.
        self.users = None
        self.writers = None

    __del__ = InstanceCounters.counted_del()

    def finalize(self):
        del self.users
        del self.writers
        del self.traces
        del self.owner

    def __repr__(self):
        return "<%s '%s' of '%s'>" % (
            self.__class__.__name__,
            self.variable_name,
            self.owner.getName(),
        )

    @abstractmethod
    def getVariableType(self):
        pass

    def getDescription(self):
        return "variable '%s'" % self.variable_name

    def getName(self):
        return self.variable_name

    def getOwner(self):
        return self.owner

    def getEntryPoint(self):
        return self.owner.getEntryPoint()

    def getCodeName(self):
        var_name = self.variable_name
        var_name = var_name.replace(".", "$")
        var_name = Utils.encodeNonAscii(var_name)

        return var_name

    def allocateTargetNumber(self):
        self.version_number += 1

        return self.version_number

    @staticmethod
    def isLocalVariable():
        return False

    @staticmethod
    def isParameterVariable():
        return False

    @staticmethod
    def isModuleVariable():
        return False

    @staticmethod
    def isIncompleteModuleVariable():
        return False

    @staticmethod
    def isTempVariable():
        return False

    @staticmethod
    def isTempVariableBool():
        return False

    @staticmethod
    def isLocalsDictVariable():
        return False

    def addVariableUser(self, user):
        # Update the shared scopes flag.
        if user is not self.owner:
            self.shared_users = True

            # These are not really scopes, just shared uses.
            if (user.isExpressionGeneratorObjectBody()
                    or user.isExpressionCoroutineObjectBody()
                    or user.isExpressionAsyncgenObjectBody()):
                if self.owner is user.getParentVariableProvider():
                    return

            _variables_in_shared_scopes.add(self)

    def isSharedTechnically(self):
        if not self.shared_users:
            return False

        if not self.users:
            return False

        owner = self.owner.getEntryPoint()

        for user in self.users:
            user = user.getEntryPoint()

            while user is not owner and (
                (user.isExpressionFunctionBody() and not user.needsCreation())
                    or user.isExpressionClassBody()):
                user = user.getParentVariableProvider()

            if user is not owner:
                return True

        return False

    def addTrace(self, variable_trace):
        self.traces.add(variable_trace)

    def removeTrace(self, variable_trace):
        self.traces.remove(variable_trace)

    def updateUsageState(self):
        writers = set()
        users = set()

        for trace in self.traces:
            owner = trace.owner
            users.add(owner)

            if trace.isAssignTrace():
                writers.add(owner)
            elif trace.isDeletedTrace() and owner is not self.owner:
                writers.add(owner)

        self.writers = writers
        self.users = users

    def hasAccessesOutsideOf(self, provider):
        if not self.owner.locals_scope.complete:
            return None
        elif self.users is None:
            return False
        elif provider in self.users:
            return len(self.users) > 1
        else:
            return bool(self.users)

    def getMatchingAssignTrace(self, assign_node):
        for trace in self.traces:
            if trace.isAssignTrace() and trace.getAssignNode() is assign_node:
                return trace

        return None

    def getMatchingDelTrace(self, del_node):
        for trace in self.traces:
            if trace.isDeletedTrace() and trace.getDelNode() is del_node:
                return trace

        return None

    def getTypeShapes(self):
        result = set()

        for trace in self.traces:
            if trace.isAssignTrace():
                result.add(trace.getAssignNode().getTypeShape())
            elif trace.isUnknownTrace():
                result.add(tshape_unknown)
            elif trace.isInitTrace():
                result.add(tshape_unknown)
            elif trace.isUnassignedTrace():
                pass
            elif trace.isMergeTrace():
                pass
            # TODO: Remove this and be not unknown.
            elif trace.isLoopTrace():
                trace.getTypeShape().emitAlternatives(result.add)
            else:
                assert False, trace

        return result
Ejemplo n.º 7
0
class ShapeBase(getMetaClassBase("Shape")):
    def __repr__(self):
        return "<%s %s %s>" % (
            self.__class__.__name__,
            self.getTypeName(),
            self.helper_code,
        )

    @staticmethod
    def getTypeName():
        return None

    helper_code = "OBJECT"

    @staticmethod
    def getCType():
        return CTypePyObjectPtr

    @staticmethod
    def getShapeIter():
        return tshape_unknown

    @staticmethod
    def hasShapeModule():
        return None

    @staticmethod
    def hasShapeSlotBytes():
        return None

    @staticmethod
    def hasShapeSlotComplex():
        return None

    @staticmethod
    def hasShapeSlotBool():
        return None

    @staticmethod
    def hasShapeSlotAbs():
        return None

    @staticmethod
    def hasShapeSlotLen():
        return None

    @staticmethod
    def hasShapeSlotInt():
        return None

    @staticmethod
    def hasShapeSlotLong():
        return None

    @staticmethod
    def hasShapeSlotFloat():
        return None

    @staticmethod
    def hasShapeSlotIter():
        return None

    @staticmethod
    def hasShapeSlotNext():
        return None

    @staticmethod
    def hasShapeSlotContains():
        return None

    @staticmethod
    def hasShapeSlotHash():
        return None

    add_shapes = {}

    def getOperationBinaryAddShape(self, right_shape):
        result = self.add_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryAddLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Add", self, right_shape)

            return operation_result_unknown

    # TODO: Change defaults to be "None" for easier catching of
    # non-overloaders
    iadd_shapes = {}

    def getOperationInplaceAddShape(self, right_shape):
        """Inplace add operation shape, for overload."""
        if self.iadd_shapes:
            result = self.iadd_shapes.get(right_shape)

            if result is not None:
                return result
            else:
                right_shape_type = type(right_shape)
                if right_shape_type is ShapeLoopCompleteAlternative:
                    return right_shape.getOperationBinaryAddLShape(self)

                if right_shape_type is ShapeLoopInitialAlternative:
                    return operation_result_unknown

                onMissingOperation("IAdd", self, right_shape)

                return operation_result_unknown
        else:
            # By default, inplace add is the same as plain add, the
            # only exception known right now is list, which extend
            # from all iterables, but don't add with them.
            return self.getOperationBinaryAddShape(right_shape)

    sub_shapes = {}

    def getOperationBinarySubShape(self, right_shape):
        result = self.sub_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinarySubLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Sub", self, right_shape)

            return operation_result_unknown

    mult_shapes = {}

    def getOperationBinaryMultShape(self, right_shape):
        result = self.mult_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryMultLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Mult", self, right_shape)

            return operation_result_unknown

    floordiv_shapes = {}

    def getOperationBinaryFloorDivShape(self, right_shape):
        result = self.floordiv_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryFloorDivLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("FloorDiv", self, right_shape)

            return operation_result_unknown

    olddiv_shapes = {}

    def getOperationBinaryOldDivShape(self, right_shape):
        result = self.olddiv_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryOldDivLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("OldDiv", self, right_shape)

            return operation_result_unknown

    truediv_shapes = {}

    def getOperationBinaryTrueDivShape(self, right_shape):
        result = self.truediv_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryTrueDivLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("TrueDiv", self, right_shape)

            return operation_result_unknown

    mod_shapes = {}

    def getOperationBinaryModShape(self, right_shape):
        result = self.mod_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryModLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Mod", self, right_shape)

            return operation_result_unknown

    divmod_shapes = {}

    def getOperationBinaryDivmodShape(self, right_shape):
        result = self.divmod_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryDivmodLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Divmod", self, right_shape)

            return operation_result_unknown

    pow_shapes = {}

    def getOperationBinaryPowShape(self, right_shape):
        result = self.pow_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryPowLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("Pow", self, right_shape)

            return operation_result_unknown

    lshift_shapes = {}

    def getOperationBinaryLShiftShape(self, right_shape):
        result = self.lshift_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryLShiftLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("LShift", self, right_shape)

            return operation_result_unknown

    rshift_shapes = {}

    def getOperationBinaryRShiftShape(self, right_shape):
        result = self.rshift_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryRShiftLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("RShift", self, right_shape)

            return operation_result_unknown

    bitor_shapes = {}

    def getOperationBinaryBitOrShape(self, right_shape):
        result = self.bitor_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryBitOrLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("BitOr", self, right_shape)

            return operation_result_unknown

    bitand_shapes = {}

    def getOperationBinaryBitAndShape(self, right_shape):
        result = self.bitand_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryBitAndLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("BitAnd", self, right_shape)

            return operation_result_unknown

    bitxor_shapes = {}

    def getOperationBinaryBitXorShape(self, right_shape):
        result = self.bitxor_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryBitXorLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("BitXor", self, right_shape)

            return operation_result_unknown

    ibitor_shapes = {}

    def getOperationInplaceBitOrShape(self, right_shape):
        """Inplace bitor operation shape, for overload."""
        if self.ibitor_shapes:
            result = self.ibitor_shapes.get(right_shape)

            if result is not None:
                return result
            else:
                right_shape_type = type(right_shape)
                if right_shape_type is ShapeLoopCompleteAlternative:
                    return right_shape.getOperationBinaryBitOrLShape(self)

                if right_shape_type is ShapeLoopInitialAlternative:
                    return operation_result_unknown

                onMissingOperation("IBitOr", self, right_shape)

                return operation_result_unknown
        else:
            # By default, inplace add is the same as plain add, the
            # only exception known right now is list, which extend
            # from all iterables, but don't add with them.
            return self.getOperationBinaryBitOrShape(right_shape)

    matmult_shapes = {}

    def getOperationBinaryMatMultShape(self, right_shape):
        result = self.matmult_shapes.get(right_shape)

        if result is not None:
            return result
        else:
            right_shape_type = type(right_shape)
            if right_shape_type is ShapeLoopCompleteAlternative:
                return right_shape.getOperationBinaryBitMatMultLShape(self)

            if right_shape_type is ShapeLoopInitialAlternative:
                return operation_result_unknown

            onMissingOperation("MatMult", self, right_shape)

            return operation_result_unknown

    def getComparisonLtShape(self, right_shape):
        onMissingOperation("Lt", self, right_shape)

        return operation_result_unknown

    def getComparisonLteShape(self, right_shape):
        return self.getComparisonLtShape(right_shape)

    def getComparisonGtShape(self, right_shape):
        return self.getComparisonLtShape(right_shape)

    def getComparisonGteShape(self, right_shape):
        return self.getComparisonLtShape(right_shape)

    def getComparisonEqShape(self, right_shape):
        return self.getComparisonLtShape(right_shape)

    def getComparisonNeqShape(self, right_shape):
        return self.getComparisonLtShape(right_shape)

    @abstractmethod
    def getOperationUnaryReprEscape(self):
        pass

    def emitAlternatives(self, emit):
        emit(self)
Ejemplo n.º 8
0
class TypeDescBase(getMetaClassBase("Type")):
    # To be overloaded
    type_name = None
    type_desc = None
    type_decl = None

    python_requirement = None

    def __init__(self):
        assert self.type_name
        assert self.type_desc
        assert self.type_decl

    def __repr__(self):
        return "<%s %s %s>" % (self.__class__.__name__, self.type_name, self.type_desc)

    @classmethod
    def getHelperCodeName(cls):
        return cls.type_name.upper()

    @classmethod
    def getVariableDecl(cls, variable_name):
        if cls.type_decl.endswith("*"):
            return cls.type_decl + variable_name
        else:
            return cls.type_decl + " " + variable_name

    @classmethod
    def getCheckValueCode(cls, operand):
        return "CHECK_OBJECT(%s);" % operand

    @classmethod
    def getTypeValueExpression(cls, operand):
        return "Py_TYPE(%s)" % operand

    @abstractmethod
    def getNewStyleNumberTypeCheckExpression(self, operand):
        pass

    @staticmethod
    def needsIndexConversion():
        return True

    def canTypeCoerceObjects(self, left):
        if left is self and left is not object_desc:
            return "0"

        # TODO: Provide hook for float to say it can do int.
        return (
            "1"
            if self.getSlotValueCheckExpression("type2", "nb_coerce") != "false"
            else "0"
        )

    @classmethod
    def getIntCheckExpression(cls, operand):
        if cls.type_name == "int":
            return "1"
        elif cls.type_name == "object":
            return "PyInt_CheckExact(%s)" % operand
        else:
            return "0"

    def getIndexCheckExpression(self, operand):
        if self.hasSlot("nb_index"):
            return "1"
        elif self.type_name == "object":
            return "PyIndex_Check(%s)" % operand
        else:
            return "0"

    def getTypeIdenticalCheckExpression(self, other, operand1, operand2):
        if self is object_desc or other is object_desc:
            return "%s == %s" % (operand1, operand2)
        elif self is other:
            return "1"
        else:
            return "0"

    @staticmethod
    def getRealSubTypeCheckCode(right, operand2, operand1):
        if right is object_desc:
            return "PyType_IsSubtype(%s, %s)" % (operand2, operand1)
        else:
            return 0

    def getSlotComparisonEqualExpression(self, right, operand1, operand2):
        if right is object_desc or self is object_desc:
            return "%s == %s" % (operand1, operand2)
        else:
            return "0"

    @abstractmethod
    def hasSlot(self, slot):
        pass

    def _getSlotValueExpression(self, operand, slot):
        if slot.startswith("nb_"):
            return "(%s) ? %s : NULL" % (
                operand
                + "->tp_as_number != NULL && "
                + self.getNewStyleNumberTypeCheckExpression(operand),
                operand + "->tp_as_number->" + slot,
            )

        elif slot.startswith("sq_"):
            return "%s ? %s : NULL" % (
                operand + "->tp_as_sequence" + " != NULL",
                operand + "->tp_as_sequence->" + slot,
            )
        else:
            assert False, slot

    def getSlotValueExpression(self, operand, slot):
        if not self.hasSlot(slot):
            return "NULL"

        return self._getSlotValueExpression(operand, slot)

    def getSlotValueCheckExpression(self, operand, slot):
        # Virtual method, pylint: disable=unused-argument
        return "true" if self.hasSlot(slot) else "false"

    def getRaiseUnsupportedTypeError(self, operation, other, operand1, operand2):
        args = []

        if self is object_desc:
            args.append("%s->tp_name" % operand1)
        if other is object_desc:
            args.append("%s->tp_name" % operand2)

        if args:
            args = ", " + ", ".join(args)
        else:
            args = ""

        return """\
PyErr_Format(PyExc_TypeError, "unsupported operand type(s) for %s: '%s' and '%s'"%s);
return NULL;""" % (
            operation,
            "%s" if self is object_desc else self.type_name,
            "%s" if other is object_desc else other.type_name,
            args,
        )

    def getSameTypeSpecializationCode(
        self, other, nb_slot, sq_slot, operand1, operand2
    ):
        cand = self if self is not object_desc else other

        if cand is object_desc:
            return ""

        # Special case for sequence concats/repeats.
        if sq_slot is not None and not cand.hasSlot(nb_slot) and cand.hasSlot(sq_slot):
            slot = sq_slot
        else:
            slot = nb_slot

        if slot == "sq_repeat":
            if cand in (list_desc, tuple_desc, unicode_desc, str_desc, bytes_desc):
                return ""

        return "return SLOT_%s_%s_%s(%s, %s);" % (
            slot,
            cand.getHelperCodeName(),
            cand.getHelperCodeName(),
            operand1,
            operand2,
        )

    def getSimilarTypeSpecializationCode(self, other, nb_slot, operand1, operand2):
        return "return SLOT_%s_%s_%s(%s, %s);" % (
            nb_slot,
            self.getHelperCodeName(),
            other.getHelperCodeName(),
            operand1,
            operand2,
        )

    def getTypeSpecializationCode(self, other, nb_slot, sq_slot, operand1, operand2):
        if self is object_desc or other is object_desc:
            return ""

        if self is other:
            return self.getSameTypeSpecializationCode(
                other, nb_slot, sq_slot, operand1, operand2
            )

        if other in related_types.get(self, ()):
            return self.getSimilarTypeSpecializationCode(
                other, nb_slot, operand1, operand2
            )

        return ""

    @abstractmethod
    def getSqConcatSlotSpecializationCode(self, other, slot, operand1, operand2):
        pass
Ejemplo n.º 9
0
class TypeDescBase(getMetaClassBase("Type")):
    # To be overloaded
    type_name = None
    type_desc = None
    type_decl = None

    python_requirement = None

    def __init__(self):
        assert self.type_name
        assert self.type_desc
        assert self.type_decl

    def __repr__(self):
        return "<%s %s %s>" % (self.__class__.__name__, self.type_name, self.type_desc)

    @classmethod
    def getHelperCodeName(cls):
        return cls.type_name.upper()

    @classmethod
    def getTypeName2(cls):
        return cls.type_name

    @classmethod
    def getTypeName3(cls):
        return cls.type_name

    @classmethod
    def getVariableDecl(cls, variable_name):
        if cls.type_decl.endswith("*"):
            return cls.type_decl + variable_name
        else:
            return cls.type_decl + " " + variable_name

    @classmethod
    def getCheckValueCode(cls, operand):
        return "CHECK_OBJECT(%s);" % operand

    @classmethod
    def getTypeValueExpression(cls, operand):
        return "Py_TYPE(%s)" % operand

    @abstractmethod
    def getNewStyleNumberTypeCheckExpression(self, operand):
        pass

    @staticmethod
    def needsIndexConversion():
        return True

    def canTypeCoerceObjects(self, left):
        if left is self and left is not object_desc:
            return "0"

        # TODO: Provide hook for float to say it can do int.
        return (
            "1"
            if self.getSlotValueCheckExpression("type2", "nb_coerce") != "false"
            else "0"
        )

    @classmethod
    def getIntCheckExpression(cls, operand):
        if cls.type_name == "int":
            return "1"
        elif cls.type_name == "object":
            return "PyInt_CheckExact(%s)" % operand
        else:
            return "0"

    def getIndexCheckExpression(self, operand):
        if self.hasSlot("nb_index"):
            return "1"
        elif self.type_name == "object":
            return "PyIndex_Check(%s)" % operand
        else:
            return "0"

    def getTypeIdenticalCheckExpression(self, other, operand1, operand2):
        if self is object_desc or other is object_desc:
            return "%s == %s" % (operand1, operand2)
        elif self is other:
            return "1"
        else:
            return "0"

    @staticmethod
    def getRealSubTypeCheckCode(right, operand2, operand1):
        if right is object_desc:
            return "PyType_IsSubtype(%s, %s)" % (operand2, operand1)
        else:
            return 0

    def getSlotComparisonEqualExpression(self, right, operand1, operand2):
        if right is object_desc or self is object_desc:
            return "%s == %s" % (operand1, operand2)
        else:
            return "0"

    @abstractmethod
    def hasSlot(self, slot):
        pass

    def _getSlotValueExpression(self, operand, slot):
        if slot.startswith("nb_"):
            return "(%s) ? %s : NULL" % (
                operand
                + "->tp_as_number != NULL && "
                + self.getNewStyleNumberTypeCheckExpression(operand),
                operand + "->tp_as_number->" + slot,
            )
        elif slot.startswith("sq_"):
            return "%s ? %s : NULL" % (
                operand + "->tp_as_sequence" + " != NULL",
                operand + "->tp_as_sequence->" + slot,
            )
        else:
            assert False, slot

    @staticmethod
    def getSlotType(slot):
        if slot == "nb_power":
            return "ternaryfunc"
        else:
            return "binaryfunc"

    @staticmethod
    def getSlotCallExpression(nb_slot, slot_var, operand1, operand2):
        if nb_slot == "nb_power":
            return "%s(%s, %s, Py_None)" % (slot_var, operand1, operand2)
        else:
            return "%s(%s, %s)" % (slot_var, operand1, operand2)

    def getSlotValueExpression(self, operand, slot):
        if not self.hasSlot(slot):
            return "NULL"

        return self._getSlotValueExpression(operand, slot)

    def getSlotValueCheckExpression(self, operand, slot):
        # Virtual method, pylint: disable=unused-argument
        return "true" if self.hasSlot(slot) else "false"

    def getRaiseUnsupportedTypeError(self, operation, other, operand1, operand2):
        args = []

        if self is object_desc:
            args.append("%s->tp_name" % operand1)
        if other is object_desc:
            args.append("%s->tp_name" % operand2)

        if args:
            args = ", " + ", ".join(args)
        else:
            args = ""

        def formatOperation(operation):
            if operation == "%":
                return "%%"
            elif operation == "**":
                return "** or pow()"
            else:
                return operation

        if (
            self.getTypeName2() != self.getTypeName3()
            or other.getTypeName2() != other.getTypeName3()
        ):
            return """\
#if PYTHON_VERSION < 300
    PyErr_Format(PyExc_TypeError, "unsupported operand type(s) for %s: '%s' and '%s'"%s);
#else
    PyErr_Format(PyExc_TypeError, "unsupported operand type(s) for %s: '%s' and '%s'"%s);
#endif
return NULL;""" % (
                formatOperation(operation),
                "%s" if self is object_desc else self.getTypeName2(),
                "%s" if other is object_desc else other.getTypeName2(),
                args,
                formatOperation(operation),
                "%s" if self is object_desc else self.getTypeName3(),
                "%s" if other is object_desc else other.getTypeName3(),
                args,
            )
        else:
            return """\
    PyErr_Format(PyExc_TypeError, "unsupported operand type(s) for %s: '%s' and '%s'"%s);
    return NULL;""" % (
                formatOperation(operation),
                "%s" if self is object_desc else self.getTypeName2(),
                "%s" if other is object_desc else other.getTypeName2(),
                args,
            )

    def getSameTypeSpecializationCode(
        self, other, nb_slot, sq_slot, operand1, operand2
    ):
        # Many cases, pylint: disable=too-many-branches,too-many-return-statements

        cand = self if self is not object_desc else other

        if cand is object_desc:
            return ""

        # Special case for sequence concats/repeats.
        if sq_slot is not None and not cand.hasSlot(nb_slot) and cand.hasSlot(sq_slot):
            slot = sq_slot
        else:
            slot = nb_slot

        if slot == "sq_repeat":
            if cand in (
                list_desc,
                tuple_desc,
                set_desc,
                dict_desc,
                unicode_desc,
                str_desc,
                bytes_desc,
            ):
                # No repeat with themselves.
                return ""

        if slot == "nb_remainder":
            if cand in (list_desc, tuple_desc, set_desc, dict_desc):
                return ""

        if slot == "nb_multiply":
            if cand in (
                str_desc,
                bytes_desc,
                list_desc,
                tuple_desc,
                set_desc,
                dict_desc,
            ):
                return ""

        if slot == "nb_add":
            # Tuple and list, etc. use sq_concat.
            # TODO: What about unicode_desc
            if cand in (
                str_desc,
                bytes_desc,
                tuple_desc,
                list_desc,
                set_desc,
                dict_desc,
            ):
                return ""

        if slot in ("nb_and", "nb_or", "nb_xor"):
            if cand in (
                str_desc,
                bytes_desc,
                unicode_desc,
                list_desc,
                tuple_desc,
                dict_desc,
            ):
                return ""

        if slot in ("nb_lshift", "nb_rshift"):
            if cand in (
                str_desc,
                bytes_desc,
                unicode_desc,
                tuple_desc,
                list_desc,
                set_desc,
                dict_desc,
            ):
                return ""

        # Nobody has it.
        if slot == "nb_matrix_multiply":
            return ""

        # We sometimes fake these, e.g. for CLONG. Maybe we should make it more
        # distinct function names in those cases and use cand.hasSlot there.

        return "return SLOT_%s_%s_%s(%s, %s);" % (
            slot,
            cand.getHelperCodeName(),
            cand.getHelperCodeName(),
            operand1,
            operand2,
        )

    def getSimilarTypeSpecializationCode(self, other, nb_slot, operand1, operand2):
        return "return SLOT_%s_%s_%s(%s, %s);" % (
            nb_slot,
            self.getHelperCodeName(),
            other.getHelperCodeName(),
            operand1,
            operand2,
        )

    def getTypeSpecializationCode(self, other, nb_slot, sq_slot, operand1, operand2):
        if self is object_desc or other is object_desc:
            return ""

        if self is other:
            return self.getSameTypeSpecializationCode(
                other, nb_slot, sq_slot, operand1, operand2
            )

        if other in related_types.get(self, ()):
            return self.getSimilarTypeSpecializationCode(
                other, nb_slot, operand1, operand2
            )

        return ""

    @abstractmethod
    def getSqConcatSlotSpecializationCode(self, other, slot, operand1, operand2):
        pass