commands.py 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475
  1. import distlib.scripts
  2. import io
  3. import os.path as osp
  4. import shutil
  5. from zipfile import ZipFile
  6. SCRIPT_TEMPLATE = u"""# -*- coding: utf-8 -*-
  7. import sys, os
  8. import site
  9. installdir = os.path.dirname(os.path.dirname(sys.executable))
  10. pkgdir = os.path.join(installdir, 'pkgs')
  11. sys.path.insert(0, pkgdir)
  12. # Ensure .pth files in pkgdir are handled properly
  13. site.addsitedir(pkgdir)
  14. os.environ['PYTHONPATH'] = pkgdir + os.pathsep + os.environ.get('PYTHONPATH', '')
  15. # Allowing .dll files in Python directory to be found
  16. os.environ['PATH'] += ';' + os.path.dirname(sys.executable)
  17. {extra_preamble}
  18. if __name__ == '__main__':
  19. from {module} import {func}
  20. sys.exit({func}())
  21. """
  22. def find_exe(bitness=32, console=True):
  23. distlib_dir = osp.dirname(distlib.scripts.__file__)
  24. name = 't' if console else 'w'
  25. return osp.join(distlib_dir, '{name}{bitness}.exe'.format(name=name, bitness=bitness))
  26. def prepare_bin_directory(target, commands, bitness=32):
  27. for name, command in commands.items():
  28. exe_path = target / (name + '.exe')
  29. console = command.get('console', True)
  30. # 1. Get the base launcher exe from distlib
  31. with open(find_exe(bitness, console=console), 'rb') as f:
  32. launcher_b = f.read()
  33. # 2. Shebang: Python executable to run with
  34. # shebangs relative to launcher location, according to
  35. # https://bitbucket.org/vinay.sajip/simple_launcher/wiki/Launching%20an%20interpreter%20in%20a%20location%20relative%20to%20the%20launcher%20executable
  36. if console:
  37. shebang = b"#!<launcher_dir>\\..\\Python\\python.exe\r\n"
  38. else:
  39. shebang = b"#!<launcher_dir>\\..\\Python\\pythonw.exe\r\n"
  40. # 3. The script to run, inside a zip file
  41. specified_preamble = command.get('extra_preamble', None)
  42. if isinstance(specified_preamble, str):
  43. # Filename
  44. extra_preamble = io.open(specified_preamble, encoding='utf-8')
  45. elif specified_preamble is None:
  46. extra_preamble = io.StringIO() # Empty
  47. else:
  48. # Passed a StringIO or similar object
  49. extra_preamble = specified_preamble
  50. module, func = command['entry_point'].split(':')
  51. script = SCRIPT_TEMPLATE.format(
  52. module=module, func=func,
  53. extra_preamble=extra_preamble.read().rstrip(),
  54. )
  55. zip_bio = io.BytesIO()
  56. with ZipFile(zip_bio, 'w') as zf:
  57. zf.writestr('__main__.py', script.encode('utf-8'))
  58. # Put the pieces together
  59. with exe_path.open('wb') as f:
  60. f.write(launcher_b)
  61. f.write(shebang)
  62. f.write(zip_bio.getvalue())