2010-12-06 3 views
5

複数のファイルとモジュールを1つのスクリプトに使用するPythonプロジェクトをパッケージ化するためのツールがあるかどうかは知りませんか?Pythonファイルを単一の.pyスクリプトにパックする

+1

タイトルで述べたように、単一の.pyスクリプトに梱包の目的は何ですか?インストール目的などのために(python自体+第三者のモジュール+コード)を組み合わせる必要がある場合は、 'pyinstaller'を見てください。 – ChristopheD

+0

1つのファイルしかコピーせずにインポートまたは実行できる必要があります。プロジェクトツリー全体をコピーするのではなく、単一のファイルをコピーするほうがはるかに便利です。 –

+0

おそらく可能です(たとえば、クラスを名前空間として悪用し、スコープ/名前空間/インポート内部に関してコードがあまりにも巧妙で/ハッキリではないが、開発には有害であり、展開には不要です)。だから私は、誰もそのようなツールを作ることに迷惑をかけることはないと思います。 – delnan

答えて

3

保存は、この:

#!/bin/env/python 
# -*- coding: ascii -*- 
import os 
import sys 
import imp 
import tarfile 
import tempfile 


RUN_MODULE = "__run__" 
SENTINEL = 'RzlBTXhya3ljIzl6PFFkQiRKLntEdHF+c2hvWid0IX5NVlxWd' \ 
      'FxcJ0NWQ2xKVUI0TVEuNl0rWUtnKiRr'.decode('base64') 


class FileOffset(object): 
    def __init__(self, fileobj, offset=0): 
     self._fileobj = fileobj 
     self._offset = offset 
     self._fileobj.seek(offset) 

    def tell(self): 
     return self._fileobj.tell() - self._offset 

    def seek(self, position, whence=os.SEEK_SET): 
     if whence == os.SEEK_SET: 
      if position < 0: raise IOErrror("Negative seek") 
      self._fileobj.seek(position + self._offset) 
     else: 
      oldposition = self._fileobj.tell() 
      self._fileobj.seek(position, whence) 
      if self._fileobj.tell() < self._offset: 
       self._fileobj.seek(oldposition, os.SEEK_SET) 
       raise IOError("Negative seek") 

    def __getattr__(self, attrname): 
     return getattr(self._fileobj, attrname) 

    def __enter__(self, *args): 
     return self._fileobj.__enter__(*args) 

    def __exit__(self, *args): 
     return self._fileobj.__exit__(*args) 


class TarImport(object): 
    def __init__(self, tarobj, tarname=None): 
     if tarname is None: 
      tarname = '<tarfile>' 
     self._tarname = tarname 
     self._tarobj = tarobj 

    def find_module(self, name, path=None): 
     module_path = os.path.join(*name.split('.')) 
     package_path = os.path.join(module_path, '__init__') 

     for path in [module_path, package_path]: 
      for suffix, mode, module_type in imp.get_suffixes(): 
       if module_type != imp.PY_SOURCE: 
        continue 
       member = os.path.join(path) + suffix 
       try: 
        modulefileobj = self._tarobj.extractfile(member) 
       except KeyError: 
        pass 
       else: 
        return Loader(name, modulefileobj, 
            "%s/%s" % (self._tarname, member), 
            (suffix, mode, module_type)) 


class Loader(object): 
    def __init__(self, name, fileobj, filename, description): 
     self._name = name 
     self._fileobj = fileobj 
     self._filename = filename 
     self._description = description 

    def load_module(self, name): 
     imp.acquire_lock() 
     try: 
      module = sys.modules.get(name) 
      if module is None: 
       module = imp.new_module(name) 

      module_script = self._fileobj.read() 
      module.__file__ = self._filename 
      module.__path__ = [] 
      sys.modules[name] = module 
      exec(module_script, module.__dict__, module.__dict__) 
     finally: 
      imp.release_lock() 

     return module 


def find_offset(fileobj, sentinel): 
    read_bytes = 0 
    for line in fileobj: 
     try: 
      offset = line.index(sentinel) 
     except ValueError: 
      read_bytes += len(line) 
     else: 
      return read_bytes + offset + len(sentinel) 
    raise ValueError("sentinel not found in %r" % (fileobj,)) 


if __name__ == "__main__": 
    sys.argv[:] = sys.argv[1:] 
    archive_path = os.path.abspath(sys.argv[0]) 
    archive_offset = find_offset(open(archive_path), SENTINEL) 

    archive = FileOffset(open(archive_path), archive_offset) 

    tarobj = tarfile.TarFile(fileobj=archive) 
    importer = TarImport(tarobj, archive_path) 

    sys.meta_path.insert(0, importer) 

    importer.find_module(RUN_MODULE).load_module(RUN_MODULE) 

保存このsh_header.shとして:create_tarred_program.pyとして

#!/bin/sh 

head -n @@[email protected]@ "$0" | tail -n [email protected]@[email protected]@ | python - "$0" 

exit $? 

保存この:

#!/usr/bin/env python 
# -*- coding: latin-1 -*- 

import sys 
import imp 
import shutil 

sh_filename, runner_filename, tar_archive, dst_filename = sys.argv[1:] 

runner = imp.load_module("tarfile_runner", 
         open(runner_filename, 'U'), 
         runner_filename, 
         ('.py', 'U', imp.PY_SOURCE)) 



sh_lines = open(sh_filename, 'r').readlines() 
runner_lines = open(runner_filename, 'r').readlines() 

sh_block = ''.join(sh_lines) 
runner_block = ''.join(runner_lines) 

if runner.SENTINEL in runner_block or runner.SENTINEL in sh_block: 
    raise ValueError("Can't have the sentinel inside the runner module") 
if not runner_block.endswith('\n') or not sh_block.endswith('\n'): 
    raise ValueError("Trailing newline required in both headers") 

to_pos = len(sh_lines) + len(runner_lines) 
from_pos = len(sh_lines) + 1 

sh_block = sh_block.replace("@@[email protected]@", str(to_pos)) 
sh_block = sh_block.replace("@@[email protected]@", str(from_pos)) 


dst = open(dst_filename, 'wb') 

dst.write(sh_block) 
dst.write(runner_block) 
dst.write(runner.SENTINEL) 

shutil.copyfileobj(open(tar_archive, 'rb'), dst) 

dst.flush() 
dst.close()  

タールAを作成します。パッケージ名はpackages.tarです。メインモジュールは__run__.pyとし、__main__は絶対にインポートしないでください。実行:

create_tarred_program.py sh_header.sh python_header.py packages.tar program.sh 

Distrubute program.sh

/bin/shへの依存を避けることは可能ですが、* nix以外ではまだ動作しませんので、意味はありません。

+0

あなたはここで多くの良い仕事をしたようです。このコードはPyPIのような別の場所で利用できますか? –

5

.eggファイルを作成してインストールするかpythonpathに置くと問題が解決する可能性があります。 python_header.pyとして
similar fellow