def load_matplotlib(finder: cx_Freeze.finder.ModuleFinder, module: cx_Freeze.module.Module) -> None: """The matplotlib package requires mpl-data subdirectory.""" data_path = module.path[0] / "mpl-data" target_path = Path("lib", module.name, "mpl-data") # After matplotlib 3.4 mpl-data is guaranteed to be a subdirectory. if not data_path.is_dir(): data_path = __import__("matplotlib").get_data_path() need_patch = True else: need_patch = not module.in_file_system finder.IncludeFiles(data_path, target_path, copy_dependent_files=False) finder.IncludePackage("matplotlib") finder.ExcludeModule("matplotlib.tests") finder.ExcludeModule("matplotlib.testing") if not need_patch or module.code is None: return CODE_STR = f""" def _get_data_path(): return os.path.join(os.path.dirname(sys.executable), "{target_path!s}") """ for code_str in [CODE_STR, CODE_STR.replace("_get_data_", "get_data_")]: new_code = compile(code_str, str(module.file), "exec") co_func = new_code.co_consts[0] name = co_func.co_name code = module.code consts = list(code.co_consts) for i, c in enumerate(consts): if isinstance(c, type(code)) and c.co_name == name: consts[i] = co_func break module.code = code_object_replace(code, co_consts=consts)
def _replace_package_in_code(self, module: Module) -> CodeType: """ Replace the value of __package__ directly in the code, when the module is in a package and will be stored in library.zip. """ code = module.code # Check if module is in a package and will be stored in library.zip # and is not defined in the module, like 'six' do if ( module.parent is None or module.in_file_system or "__package__" in module.global_names or code is None ): return code # Only if the code references it. if "__package__" in code.co_names: consts = list(code.co_consts) pkg_const_index = len(consts) pkg_name_index = code.co_names.index("__package__") if pkg_const_index > 255 or pkg_name_index > 255: # Don't touch modules with many constants or names; # This is good for now. return code # Insert a bytecode to represent the code: # __package__ = module.parent.name codes = [LOAD_CONST, pkg_const_index, STORE_NAME, pkg_name_index] codestring = bytes(codes) + code.co_code consts.append(module.parent.name) code = code_object_replace( code, co_code=codestring, co_consts=consts ) return code
def _replace_paths_in_code( self, module: Module, code: Optional[CodeType] = None ) -> CodeType: """ Replace paths in the code as directed, returning a new code object with the modified paths in place. """ top_level_module = module # type: Module while top_level_module.parent is not None: top_level_module = top_level_module.parent if code is None: code = module.code # Prepare the new filename. new_filename = original_filename = os.path.normpath(code.co_filename) for search_value, replace_value in self.replace_paths: if search_value == "*": if top_level_module.file is None: continue search_value = os.path.dirname(top_level_module.file) if top_level_module.path: search_value = os.path.dirname(search_value) if search_value: search_value = search_value + os.path.sep if original_filename.startswith(search_value): new_filename = ( replace_value + original_filename[len(search_value) :] ) break # Run on subordinate code objects from function & class definitions. consts = list(code.co_consts) for i in range(len(consts)): if isinstance(consts[i], type(code)): consts[i] = self._replace_paths_in_code( top_level_module, consts[i] ) return code_object_replace( code, co_consts=consts, co_filename=new_filename )