import textwrap import os import subprocess import sys import signal import glob import itertools class PathReader: @staticmethod def _read_file(filename): root = os.path.dirname(filename) return ( os.path.join(root, path.rstrip()) for path in open(filename) if path.strip() and not path.startswith('#') and not path.startswith('import ') ) @classmethod def _read(cls, target): """ As .pth files aren't honored except in site dirs, read the paths indicated by them. """ pth_files = glob.glob(os.path.join(target, '*.pth')) file_items = map(cls._read_file, pth_files) return itertools.chain.from_iterable(file_items) def _inject_sitecustomize(target): """ Create a sitecustomize file in the target that will install the target as a sitedir. """ hook = ( textwrap.dedent( """ import site site.addsitedir({target!r}) """ ) .lstrip() .format(**locals()) ) sc_fn = os.path.join(target, 'sitecustomize.py') with open(sc_fn, 'w') as strm: strm.write(hook) def _pythonpath(): return 'JYTHONPATH' if sys.platform.startswith('java') else 'PYTHONPATH' def _build_env(target): """ Prepend target and .pth references in target to PYTHONPATH """ key = _pythonpath() env = dict(os.environ) suffix = env.get(key) prefix = (target,) items = itertools.chain( prefix, PathReader._read(target), (suffix,) if suffix else () ) joined = os.pathsep.join(items) env[key] = joined return env def _setup_env(target): _inject_sitecustomize(target) return _build_env(target) def with_path(target, params): """ Launch Python with target on the path and params """ def null_handler(signum, frame): pass signal.signal(signal.SIGINT, null_handler) cmd = [sys.executable] + params return subprocess.Popen(cmd, env=_setup_env(target)).wait() def with_path_overlay(target, params): """ Overlay Python with target on the path and params """ cmd = [sys.executable] + params os.execve(sys.executable, cmd, _setup_env(target))