1# Copyright 2021, Kay Hayen, mailto:kay.hayen@gmail.com 2# 3# Part of "Nuitka", an optimizing Python compiler that is compatible and 4# integrates with CPython, but also works on its own. 5# 6# Licensed under the Apache License, Version 2.0 (the "License"); 7# you may not use this file except in compliance with the License. 8# You may obtain a copy of the License at 9# 10# http://www.apache.org/licenses/LICENSE-2.0 11# 12# Unless required by applicable law or agreed to in writing, software 13# distributed under the License is distributed on an "AS IS" BASIS, 14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15# See the License for the specific language governing permissions and 16# limitations under the License. 17# 18""" Code to generate and interact with module loaders. 19 20This is for generating the look-up table for the modules included in a binary 21or distribution folder. 22 23Also this prepares tables for the freezer for bytecode compiled modules. Not 24real C compiled modules. 25 26This is including modules as bytecode and mostly intended for modules, where 27we know compiling it useless or does not make much sense, or for standalone 28mode to access modules during CPython library init that cannot be avoided. 29 30The level of compatibility for C compiled stuff is so high that this is not 31needed except for technical reasons. 32""" 33 34from nuitka import Options 35from nuitka.ModuleRegistry import ( 36 getDoneModules, 37 getUncompiledModules, 38 getUncompiledTechnicalModules, 39) 40from nuitka.plugins.Plugins import Plugins 41from nuitka.Tracing import inclusion_logger 42from nuitka.utils.CStrings import encodePythonStringToC 43 44from .Indentation import indented 45from .templates.CodeTemplatesLoader import ( 46 template_metapath_loader_body, 47 template_metapath_loader_bytecode_module_entry, 48 template_metapath_loader_compiled_module_entry, 49 template_metapath_loader_shlib_module_entry, 50) 51 52 53def getModuleMetapathLoaderEntryCode(module, bytecode_accessor): 54 module_c_name = encodePythonStringToC( 55 Plugins.encodeDataComposerName(module.getFullName().asString()) 56 ) 57 58 flags = ["NUITKA_TRANSLATED_FLAG"] 59 60 if module.isUncompiledPythonModule(): 61 code_data = module.getByteCode() 62 is_package = module.isUncompiledPythonPackage() 63 64 flags.append("NUITKA_BYTECODE_FLAG") 65 if is_package: 66 flags.append("NUITKA_PACKAGE_FLAG") 67 68 accessor_code = bytecode_accessor.getBlobDataCode(code_data) 69 70 return template_metapath_loader_bytecode_module_entry % { 71 "module_name": module_c_name, 72 "bytecode": accessor_code[accessor_code.find("[") + 1 : -1], 73 "size": len(code_data), 74 "flags": " | ".join(flags) or "0", 75 } 76 elif module.isPythonShlibModule(): 77 flags.append("NUITKA_SHLIB_FLAG") 78 79 return template_metapath_loader_shlib_module_entry % { 80 "module_name": module_c_name, 81 "flags": " | ".join(flags) or "0", 82 } 83 else: 84 if module.isCompiledPythonPackage(): 85 flags.append("NUITKA_PACKAGE_FLAG") 86 87 return template_metapath_loader_compiled_module_entry % { 88 "module_name": module_c_name, 89 "module_identifier": module.getCodeName(), 90 "flags": " | ".join(flags), 91 } 92 93 94def getMetapathLoaderBodyCode(bytecode_accessor): 95 metapath_loader_inittab = [] 96 metapath_module_decls = [] 97 98 uncompiled_modules = getUncompiledModules() 99 100 for other_module in getDoneModules(): 101 # Put those at the end. 102 if other_module in uncompiled_modules: 103 continue 104 105 metapath_loader_inittab.append( 106 getModuleMetapathLoaderEntryCode( 107 module=other_module, bytecode_accessor=bytecode_accessor 108 ) 109 ) 110 111 if other_module.isCompiledPythonModule(): 112 metapath_module_decls.append( 113 """\ 114extern PyObject *modulecode_%(module_identifier)s(PyObject *, struct Nuitka_MetaPathBasedLoaderEntry const *);""" 115 % {"module_identifier": other_module.getCodeName()} 116 ) 117 118 for uncompiled_module in uncompiled_modules: 119 metapath_loader_inittab.append( 120 getModuleMetapathLoaderEntryCode( 121 module=uncompiled_module, bytecode_accessor=bytecode_accessor 122 ) 123 ) 124 125 frozen_defs = [] 126 127 for uncompiled_module in getUncompiledTechnicalModules(): 128 module_name = uncompiled_module.getFullName() 129 code_data = uncompiled_module.getByteCode() 130 is_package = uncompiled_module.isUncompiledPythonPackage() 131 132 size = len(code_data) 133 134 # Packages are indicated with negative size. 135 if is_package: 136 size = -size 137 138 accessor_code = bytecode_accessor.getBlobDataCode(code_data) 139 140 frozen_defs.append( 141 """\ 142{{"{module_name}", {start}, {size}}},""".format( 143 module_name=module_name, 144 start=accessor_code[accessor_code.find("[") + 1 : -1], 145 size=size, 146 ) 147 ) 148 149 if Options.isShowInclusion(): 150 inclusion_logger.info("Embedded as frozen module '%s'." % module_name) 151 152 return template_metapath_loader_body % { 153 "metapath_module_decls": indented(metapath_module_decls, 0), 154 "metapath_loader_inittab": indented(metapath_loader_inittab), 155 "bytecode_count": bytecode_accessor.getConstantsCount(), 156 "frozen_modules": indented(frozen_defs), 157 } 158