copymodules.py 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import importlib, importlib.abc, importlib.machinery
  2. import os
  3. import shutil
  4. import sys
  5. import zipfile, zipimport
  6. class ModuleCopier:
  7. """Finds and copies importable Python modules and packages.
  8. This uses importlib to locate modules.
  9. """
  10. def __init__(self, path=None):
  11. self.path = path if (path is not None) else ([''] + sys.path)
  12. def copy(self, modname, target):
  13. """Copy the importable module 'modname' to the directory 'target'.
  14. modname should be a top-level import, i.e. without any dots. Packages
  15. are always copied whole.
  16. This can currently copy regular filesystem files and directories, and
  17. extract modules and packages from appropriately structured zip files.
  18. """
  19. loader = importlib.find_loader(modname, self.path)
  20. if loader is None:
  21. raise ImportError('Could not find %s' % modname)
  22. pkg = loader.is_package(modname)
  23. if isinstance(loader, importlib.machinery.ExtensionFileLoader):
  24. shutil.copy2(loader.path, target)
  25. elif isinstance(loader, importlib.abc.FileLoader):
  26. file = loader.get_filename(modname)
  27. if pkg:
  28. pkgdir, basename = os.path.split(file)
  29. assert basename.startswith('__init__')
  30. dest = os.path.join(target, modname)
  31. shutil.copytree(pkgdir, dest, ignore=shutil.ignore_patterns('*.pyc'))
  32. else:
  33. shutil.copy2(file, target)
  34. elif isinstance(loader, zipimport.zipimporter):
  35. file = loader.get_filename(modname)
  36. prefix = loader.archive + '/' + loader.prefix
  37. assert file.startswith(prefix)
  38. path_in_zip = file[len(prefix):]
  39. zf = zipfile.ZipFile(loader.archive)
  40. if pkg:
  41. pkgdir, basename = path_in_zip.rsplit('/', 1)
  42. assert basename.startswith('__init__')
  43. pkgfiles = [f for f in zf.namelist() if f.startswith(pkgdir)]
  44. zf.extractall(target, pkgfiles)
  45. else:
  46. zf.extract(path_in_zip, target)
  47. def copy_modules(modnames, target, path=None):
  48. """Copy the specified importable modules to the target directory.
  49. By default, it finds modules in sys.path - this can be overridden by passing
  50. the path parameter.
  51. """
  52. mc = ModuleCopier(path)
  53. files_in_target_noext = [os.path.splitext(f)[0] for f in os.listdir(target)]
  54. for modname in modnames:
  55. if modname in files_in_target_noext:
  56. # Already there, no need to copy it.
  57. continue
  58. mc.copy(modname, target)
  59. if not modnames:
  60. # NSIS abhors an empty folder, so give it a file to find.
  61. with open(os.path.join(target, 'placeholder'), 'w') as f:
  62. f.write('This file only exists so NSIS finds something in this directory.')