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
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()
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)
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)
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
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
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)
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
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