| | |
| |
|
| | import logging |
| | import os |
| | import os.path |
| | import sys |
| | import re |
| |
|
| |
|
| | logger = logging.getLogger(__name__) |
| |
|
| |
|
| | EXPOSE_NAMES = ('executable', 'version', 'prefix', 'exec_prefix', 'through') |
| |
|
| |
|
| | def wellknown_locations(): |
| | if sys.platform == 'win32': |
| | base = 'c:\\' |
| | for name in os.listdir(base): |
| | name = name.lower() |
| | if name.startswith('python'): |
| | shortversion = name[len('python'):] |
| | m = re.match('[23][0-9]', shortversion) |
| | if m: |
| | yield base + name |
| | elif 'PYTHONZ_ROOT' in os.environ: |
| | pythonz_root = os.environ['PYTHONZ_ROOT'] |
| | pythons = os.path.join(pythonz_root, 'pythons') |
| | for item in os.listdir(pythons): |
| | yield os.path.join(pythons, item) |
| |
|
| |
|
| | def discover_python(in_wellknown=True, in_path=True): |
| |
|
| | if in_wellknown: |
| | for found in search_in_wellknown_locations(): |
| | yield found |
| |
|
| | if in_path: |
| | for found in search_in_path(): |
| | yield found |
| |
|
| |
|
| | def search_in_wellknown_locations(): |
| | for location in wellknown_locations(): |
| | if sys.platform == 'win32': |
| | founds = contains_python(location) |
| | else: |
| | founds = contains_python_in_bin(location) |
| |
|
| | for found in founds: |
| | found['through'] = 'WELLKNOWN_LOCATION' |
| | yield found |
| |
|
| |
|
| | def search_in_path(): |
| | if 'PATH' in os.environ: |
| | path = os.environ['PATH'] |
| | path = path.split(os.pathsep) |
| | for dir in path: |
| | for found in contains_python(dir): |
| | found['through'] = 'PATH' |
| |
|
| | |
| | resolved = os.path.realpath(found['executable']) |
| | found['executable'] = resolved |
| | yield found |
| |
|
| |
|
| | def contains_python_in_bin(dir): |
| | bindir = os.path.join(dir, 'bin') |
| | return contains_python(bindir) |
| |
|
| |
|
| | def contains_python(dir): |
| | vers = { |
| | 2: [3, 4, 5, 6, 7], |
| | 3: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], |
| | } |
| | names = (('python%d.%d' % (major, minor)) |
| | for major in reversed(sorted(vers)) |
| | for minor in reversed(sorted(vers[major]))) |
| | names = list(names) + ['python'] |
| | for name in names: |
| | executable = executable_in_dir(name, dir) |
| | if executable: |
| | found = executable_is_python(executable) |
| | if found: |
| | yield found |
| |
|
| |
|
| | def executable_in_dir(name, dir): |
| | assert name == os.path.basename(name) |
| | if sys.platform == 'win32': |
| | name += '.exe' |
| | path = os.path.join(dir, name) |
| | if not os.path.exists(path): |
| | return |
| | return path |
| |
|
| |
|
| | def executable_is_python(executable): |
| | from subprocess import Popen |
| | from subprocess import PIPE |
| | cmd = ''' |
| | import os, sys |
| | print(sys.hexversion) |
| | print(os.pathsep.join([sys.prefix, sys.exec_prefix])) |
| | '''.strip().replace('\n', ';') |
| | args = [executable, '-c', cmd] |
| | env = dict(os.environ) |
| | for k in ('PYTHONPATH', 'PYTHONHOME'): |
| | if k in env: |
| | del env[k] |
| | try: |
| | p = Popen(args, stdout=PIPE, env=env) |
| | lines = p.stdout.read().split('\n') |
| | p.wait() |
| | ver = int(lines[0]) |
| | ver_major = str(ver >> 24 & 0xff) |
| | ver_minor = str(ver >> 16 & 0xff) |
| | ver_patch = str(ver >> 8 & 0xff) |
| | ver = ver_major, ver_minor, ver_patch |
| | version = '.'.join(ver) |
| | prefix, exec_prefix = lines[1].split(os.pathsep) |
| | return dict(executable=executable, version=version, |
| | prefix=prefix, exec_prefix=exec_prefix) |
| | except Exception, e: |
| | logger.error('popen failed: %s', args) |
| | logger.exception(e) |
| |
|
| |
|
| | def log_discovered(qualify, found, names, log=logger.info): |
| | for item in found: |
| | msg = qualify + ':' |
| | for name in names: |
| | if name in item: |
| | msg += ' %s=%s' % (name, item[name]) |
| | log(msg) |
| | yield item |
| |
|
| |
|
| | def expose_options(options, names, found, not_found, logger=logger): |
| | for name in names: |
| | if name in found: |
| | value = found[name] |
| | if name in options: |
| | if value != options[name]: |
| | logger.info('(updating) %s = %s', name, value) |
| | options[name] = value |
| | else: |
| | logger.info('(preserving) %s = %s', name, value) |
| | else: |
| | logger.info('(exposing) %s = %s', name, value) |
| | options[name] = value |
| | else: |
| | if name not in options: |
| | options[name] = value = not_found |
| | logger.info('(exposing) %s = %s', name, value) |
| |
|
| |
|
| | class Discover(object): |
| | ''' Discover Python and provide its location. |
| | ''' |
| |
|
| | def __init__(self, buildout, name, options): |
| | from zc.buildout import UserError |
| | self.__logger = logger = logging.getLogger(name) |
| | for k, v in options.items(): |
| | logger.info('%s: %r', k, v) |
| |
|
| | self.__recipe = options['recipe'] |
| |
|
| | not_found = options.get('not-found', 'not-found') |
| | version = options.get('version', '').strip() |
| |
|
| | if 'location' in options: |
| | |
| | |
| | for found in contains_python_in_bin(options['location']): |
| | if not version or found['version'].startswith(version): |
| | |
| | options['executable'] = found['executable'] |
| | return |
| | raise UserError('Python not found at %s' % options['location']) |
| |
|
| | in_wellknown = options.get('search-in-wellknown-places', |
| | 'true').lower().strip() |
| | in_wellknown = in_wellknown in ('true', 'yes', '1') |
| | in_path = options.get('search-in-path', 'true').lower().strip() |
| | in_path = in_path in ('true', 'yes', '1') |
| |
|
| | founds = discover_python(in_wellknown=in_wellknown, |
| | in_path=in_path) |
| | founds = log_discovered('candidates', founds, EXPOSE_NAMES, |
| | log=logger.debug) |
| | if version: |
| | |
| | founds = (found for found in founds |
| | if found['version'].startswith(version)) |
| | founds = log_discovered('matching', founds, EXPOSE_NAMES, |
| | log=logger.info) |
| | founds = list(founds) |
| |
|
| | |
| | if founds: |
| | found = founds[0] |
| | logger.info('the first-matching one will be used:') |
| | expose_options(options, EXPOSE_NAMES, found, |
| | not_found=not_found, logger=logger) |
| | return |
| |
|
| | |
| | expose_options(options, ['executable'], dict(), not_found=not_found, |
| | logger=logger) |
| | logger.warning('Python not found') |
| | return |
| |
|
| | def install(self): |
| | return [] |
| |
|
| | update = install |
| |
|