diff options
Diffstat (limited to 'baserockimport/exts/python.find_deps')
-rwxr-xr-x | baserockimport/exts/python.find_deps | 124 |
1 files changed, 86 insertions, 38 deletions
diff --git a/baserockimport/exts/python.find_deps b/baserockimport/exts/python.find_deps index 91a9e39..b173110 100755 --- a/baserockimport/exts/python.find_deps +++ b/baserockimport/exts/python.find_deps @@ -24,6 +24,7 @@ from __future__ import print_function +import contextlib import sys import subprocess import os @@ -32,6 +33,7 @@ import tempfile import logging import select import signal +import shutil import pkg_resources import xmlrpclib @@ -262,65 +264,109 @@ def find_build_deps(source, name, version=None): return build_deps + +@contextlib.contextmanager +def temporary_virtualenv(): + tempdir = tempfile.mkdtemp() + + logging.debug('Creating virtualenv in %s', tempdir) + p = subprocess.Popen(['virtualenv', tempdir], stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + + while True: + line = p.stdout.readline() + if line == '': + break + + logging.debug(line.rstrip('\n')) + + p.wait() # even with eof, wait for termination + + try: + yield tempdir + finally: + logging.debug('Removing virtualenv at %s', tempdir) + shutil.rmtree(tempdir) + + def find_runtime_deps(source, name, version=None, use_requirements_file=False): logging.debug('Finding runtime dependencies for %s%s at %s' % (name, ' %s' % version if version else '', source)) - # Run our patched pip to get a list of installed deps - # Run pip install . --list-dependencies=instdeps.txt with cwd=source - # Some temporary file needed for storing the requirements tmpfd, tmppath = tempfile.mkstemp() logging.debug('Writing install requirements to: %s', tmppath) - args = ['pip', 'install', '.', '--list-dependencies=%s' % tmppath] - if use_requirements_file: - args.insert(args.index('.') + 1, '-r') - args.insert(args.index('.') + 2, 'requirements.txt') + with temporary_virtualenv() as virtenv_path: + shutil.copytree(source, os.path.join(virtenv_path, 'source')) - logging.debug('Running pip, args: %s' % args) + pip_runner = os.path.join(os.path.dirname(os.path.abspath(__file__)), + 'python_run_pip') + logging.debug('pip_runner: %s', pip_runner) - p = subprocess.Popen(args, cwd=source, stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + subprocess_env = os.environ.copy() + subprocess_env['TMPDIR'] = os.path.join(virtenv_path, 'tmp') + logging.debug('Using %s as TMPDIR', subprocess_env['TMPDIR']) + p = subprocess.Popen(pip_runner, cwd=virtenv_path, env=subprocess_env, + stdout=subprocess.PIPE, stderr=subprocess.STDOUT) - output = [] - while True: - line = p.stdout.readline() - if line == '': - break + output = [] + while True: + line = p.stdout.readline() + if line == '': + break - logging.debug(line.rstrip('\n')) - output.append(line) + logging.debug(line.rstrip('\n')) + output.append(line) - p.wait() # even with eof, wait for termination + p.wait() # even with eof, wait for termination - logging.debug('pip exited with code: %d' % p.returncode) + logging.debug('pip exited with code: %d' % p.returncode) - if p.returncode != 0: - error('Failed to get runtime dependencies for %s%s at %s. Output from ' - 'Pip: %s' % (name, ' %s' % version if version else '', source, - ' '.join(output))) + if p.returncode != 0: + error('Failed to get runtime dependencies for %s%s at %s. Output ' + 'from Pip: %s' % (name, ' %s' % version if version else '', + source, ' '.join(output))) + + # Now run pip freeze + logging.debug('Running pip freeze') + + p = subprocess.Popen(['/bin/bash', '-c', + 'source bin/activate; ' + 'pip freeze --disable-pip-version-check'], + cwd=virtenv_path, + stdout=tmpfd, stderr=subprocess.PIPE) - with os.fdopen(tmpfd) as tmpfile: - ss = resolve_specs(pkg_resources.parse_requirements(tmpfile)) - logging.debug("Resolved specs for %s: %s" % (name, ss)) + _, err = p.communicate() + os.close(tmpfd) - logging.debug("Removing root package from specs") + if p.returncode != 0: + error('failed to get runtime dependencies for %s%s at %s: %s' + % (name, ' %s' % version if version else '', source, err)) - # filter out "root" package - # hyphens and underscores are treated as equivalents - # in distribution names - specsets = {k: v for (k, v) in ss.iteritems() - if k not in [name, name.replace('_', '-')]} + with open(tmppath) as f: + logging.debug(f.read()) - versions = resolve_versions(specsets) - logging.debug('Resolved versions: %s' % versions) + with open(tmppath) as tmpfile: + ss = resolve_specs(pkg_resources.parse_requirements(tmpfile)) + logging.debug("Resolved specs for %s: %s" % (name, ss)) - # Since any of the candidates in versions should satisfy - # all specs, we just pick the first version we see - runtime_deps = {name: vs[0] for (name, vs) in versions.iteritems()} + logging.debug("Removing root package from specs") - os.remove(tmppath) + # filter out "root" package + # hyphens and underscores are treated as equivalents + # in distribution names + specsets = {k: v for (k, v) in ss.iteritems() + if k not in [name, name.replace('_', '-')]} + + versions = resolve_versions(specsets) + logging.debug('Resolved versions: %s' % versions) + + # Since any of the candidates in versions should satisfy + # all specs, we just pick the first version we see + runtime_deps = {name: vs[0] for (name, vs) in versions.iteritems()} + + os.remove(tmppath) if (len(runtime_deps) == 0 and not use_requirements_file and os.path.isfile(os.path.join(source, 'requirements.txt'))): @@ -360,6 +406,8 @@ class PythonFindDepsExtension(ImportExtension): root = {'python': deps} + logging.debug('Returning %s', root) + print(json.dumps(root)) if __name__ == '__main__': |