copymodules.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. import os
  2. import shutil
  3. import sys
  4. import zipfile, zipimport
  5. PY2 = sys.version_info[0] == 2
  6. def copy_zipmodule(loader, modname, target):
  7. file = loader.get_filename(modname)
  8. prefix = loader.archive + '/' + loader.prefix
  9. assert file.startswith(prefix)
  10. path_in_zip = file[len(prefix):]
  11. zf = zipfile.ZipFile(loader.archive)
  12. if loader.is_package(modname):
  13. pkgdir, basename = path_in_zip.rsplit('/', 1)
  14. assert basename.startswith('__init__')
  15. pkgfiles = [f for f in zf.namelist() if f.startswith(pkgdir)]
  16. zf.extractall(target, pkgfiles)
  17. else:
  18. zf.extract(path_in_zip, target)
  19. if not PY2:
  20. import importlib
  21. import importlib.abc
  22. import importlib.machinery
  23. class ModuleCopier:
  24. """Finds and copies importable Python modules and packages.
  25. This is the Python >3.3 version and uses the `importlib` package to
  26. locate modules.
  27. """
  28. def __init__(self, path=None):
  29. self.path = path if (path is not None) else ([''] + sys.path)
  30. def copy(self, modname, target):
  31. """Copy the importable module 'modname' to the directory 'target'.
  32. modname should be a top-level import, i.e. without any dots.
  33. Packages are always copied whole.
  34. This can currently copy regular filesystem files and directories,
  35. and extract modules and packages from appropriately structured zip
  36. files.
  37. """
  38. loader = importlib.find_loader(modname, self.path)
  39. if loader is None:
  40. raise ImportError('Could not find %s' % modname)
  41. pkg = loader.is_package(modname)
  42. if isinstance(loader, importlib.machinery.ExtensionFileLoader):
  43. shutil.copy2(loader.path, target)
  44. elif isinstance(loader, importlib.abc.FileLoader):
  45. file = loader.get_filename(modname)
  46. if pkg:
  47. pkgdir, basename = os.path.split(file)
  48. assert basename.startswith('__init__')
  49. dest = os.path.join(target, modname)
  50. shutil.copytree(pkgdir, dest,
  51. ignore=shutil.ignore_patterns('*.pyc'))
  52. else:
  53. shutil.copy2(file, target)
  54. elif isinstance(loader, zipimport.zipimporter):
  55. copy_zipmodule(loader, modname, target)
  56. else:
  57. import imp
  58. class ModuleCopier:
  59. """Finds and copies importable Python modules and packages.
  60. This is the Python 2.7 version and uses the `imp` package to locate
  61. modules.
  62. """
  63. def __init__(self, path=None):
  64. self.path = path if (path is not None) else ([''] + sys.path)
  65. self.zip_paths = [p for p in self.path if zipfile.is_zipfile(p)]
  66. def copy(self, modname, target):
  67. """Copy the importable module 'modname' to the directory 'target'.
  68. modname should be a top-level import, i.e. without any dots.
  69. Packages are always copied whole.
  70. This can currently copy regular filesystem files and directories,
  71. and extract modules and packages from appropriately structured zip
  72. files.
  73. """
  74. info = imp.find_module(modname, self.path)
  75. path = info[1]
  76. modtype = info[2][2]
  77. if modtype in (imp.PY_SOURCE, imp.C_EXTENSION):
  78. shutil.copy2(path, target)
  79. elif modtype == imp.PKG_DIRECTORY:
  80. dest = os.path.join(target, modname)
  81. shutil.copytree(path, dest,
  82. ignore=shutil.ignore_patterns('*.pyc'))
  83. else:
  84. # Search all ZIP files in self.path for the module name
  85. # NOTE: `imp.find_module(...)` will *not* find modules in ZIP
  86. # files, so we have to check each file for ourselves
  87. for zpath in self.zip_path:
  88. loader = zipimport.zipimporter(fpath)
  89. if loader.find_module(modname) is None:
  90. continue
  91. copy_zipmodule(loader, modname, target)
  92. def copy_modules(modnames, target, path=None):
  93. """Copy the specified importable modules to the target directory.
  94. By default, it finds modules in sys.path - this can be overridden by passing
  95. the path parameter.
  96. """
  97. mc = ModuleCopier(path)
  98. files_in_target_noext = [os.path.splitext(f)[0] for f in os.listdir(target)]
  99. for modname in modnames:
  100. if modname in files_in_target_noext:
  101. # Already there, no need to copy it.
  102. continue
  103. mc.copy(modname, target)
  104. if not modnames:
  105. # NSIS abhors an empty folder, so give it a file to find.
  106. with open(os.path.join(target, 'placeholder'), 'w') as f:
  107. f.write('This file only exists so NSIS finds something in this directory.')