123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169 |
- import os
- import shutil
- import sys
- import zipfile, zipimport
- PY2 = sys.version_info[0] == 2
- running_python = '.'.join(str(x) for x in sys.version_info[:2])
- class ExtensionModuleMismatch(ImportError):
- pass
- extensionmod_errmsg = """Found an extension module that will not be usable on %s:
- %s
- Put Windows packages in pynsist_pkgs/ to avoid this."""
- def check_ext_mod(path, target_python):
- """If path is an extension module, check that it matches target platform.
-
- It should be for Windows and we should be running on the same version
- of Python that we're targeting. Raises ExtensionModuleMismatch if not.
-
- Does nothing if path is not an extension module.
- """
- if path.endswith('.so'):
- raise ExtensionModuleMismatch(extensionmod_errmsg % ('Windows', path))
- elif path.endswith('.pyd') and not target_python.startswith(running_python):
- # TODO: From Python 3.2, extension modules can restrict themselves
- # to a stable ABI. Can we detect this?
- raise ExtensionModuleMismatch(extensionmod_errmsg % ('Python '+target_python, path))
- def check_package_for_ext_mods(path, target_python):
- """Walk the directory path, calling :func:`check_ext_mod` on each file.
- """
- for dirpath, dirnames, filenames in os.walk(path):
- for filename in filenames:
- check_ext_mod(os.path.join(path, dirpath, filename), target_python)
- def copy_zipmodule(loader, modname, target):
- """Copy a module or package out of a zip file to the target directory."""
- file = loader.get_filename(modname)
- prefix = loader.archive + '/' + loader.prefix
- assert file.startswith(prefix)
- path_in_zip = file[len(prefix):]
- zf = zipfile.ZipFile(loader.archive)
- if loader.is_package(modname):
- pkgdir, basename = path_in_zip.rsplit('/', 1)
- assert basename.startswith('__init__')
- pkgfiles = [f for f in zf.namelist() if f.startswith(pkgdir)]
- zf.extractall(target, pkgfiles)
- else:
- zf.extract(path_in_zip, target)
- if not PY2:
- import importlib
- import importlib.abc
- import importlib.machinery
- class ModuleCopier:
- """Finds and copies importable Python modules and packages.
- This is the Python >3.3 version and uses the `importlib` package to
- locate modules.
- """
- def __init__(self, py_version, path=None):
- self.py_version = py_version
- self.path = path if (path is not None) else ([''] + sys.path)
- def copy(self, modname, target):
- """Copy the importable module 'modname' to the directory 'target'.
- modname should be a top-level import, i.e. without any dots.
- Packages are always copied whole.
- This can currently copy regular filesystem files and directories,
- and extract modules and packages from appropriately structured zip
- files.
- """
- loader = importlib.find_loader(modname, self.path)
- if loader is None:
- raise ImportError('Could not find %s' % modname)
- pkg = loader.is_package(modname)
- if isinstance(loader, importlib.machinery.ExtensionFileLoader):
- check_ext_mod(loader.path, self.py_version)
- shutil.copy2(loader.path, target)
- elif isinstance(loader, importlib.abc.FileLoader):
- file = loader.get_filename(modname)
- if pkg:
- pkgdir, basename = os.path.split(file)
- assert basename.startswith('__init__')
- check_package_for_ext_mods(pkgdir, self.py_version)
- dest = os.path.join(target, modname)
- shutil.copytree(pkgdir, dest,
- ignore=shutil.ignore_patterns('*.pyc'))
- else:
- shutil.copy2(file, target)
- elif isinstance(loader, zipimport.zipimporter):
- copy_zipmodule(loader, modname, target)
- else:
- import imp
- class ModuleCopier:
- """Finds and copies importable Python modules and packages.
- This is the Python 2.7 version and uses the `imp` package to locate
- modules.
- """
- def __init__(self, py_version, path=None):
- self.py_version = py_version
- self.path = path if (path is not None) else ([''] + sys.path)
- self.zip_paths = [p for p in self.path if zipfile.is_zipfile(p)]
- def copy(self, modname, target):
- """Copy the importable module 'modname' to the directory 'target'.
- modname should be a top-level import, i.e. without any dots.
- Packages are always copied whole.
- This can currently copy regular filesystem files and directories,
- and extract modules and packages from appropriately structured zip
- files.
- """
- info = imp.find_module(modname, self.path)
- path = info[1]
- modtype = info[2][2]
- if modtype == imp.C_EXTENSION:
- check_ext_mod(path, self.py_version)
- if modtype in (imp.PY_SOURCE, imp.C_EXTENSION):
- shutil.copy2(path, target)
- elif modtype == imp.PKG_DIRECTORY:
- check_package_for_ext_mods(path, self.py_version)
- dest = os.path.join(target, modname)
- shutil.copytree(path, dest,
- ignore=shutil.ignore_patterns('*.pyc'))
- else:
- # Search all ZIP files in self.path for the module name
- # NOTE: `imp.find_module(...)` will *not* find modules in ZIP
- # files, so we have to check each file for ourselves
- for zpath in self.zip_path:
- loader = zipimport.zipimporter(zpath)
- if loader.find_module(modname) is None:
- continue
- copy_zipmodule(loader, modname, target)
- def copy_modules(modnames, target, py_version, path=None):
- """Copy the specified importable modules to the target directory.
-
- By default, it finds modules in sys.path - this can be overridden by passing
- the path parameter.
- """
- mc = ModuleCopier(py_version, path)
- files_in_target_noext = [os.path.splitext(f)[0] for f in os.listdir(target)]
-
- for modname in modnames:
- if modname in files_in_target_noext:
- # Already there, no need to copy it.
- continue
- mc.copy(modname, target)
-
- if not modnames:
- # NSIS abhors an empty folder, so give it a file to find.
- with open(os.path.join(target, 'placeholder'), 'w') as f:
- f.write('This file only exists so NSIS finds something in this directory.')
|