Spaces:
Configuration error
Configuration error
| # -*- coding: utf-8 -*- | |
| 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' | |
| # resolve symlinks | |
| 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: | |
| # if location is explicitly specified, it must contains java | |
| # executable. | |
| for found in contains_python_in_bin(options['location']): | |
| if not version or found['version'].startswith(version): | |
| # Python found, no further discovery required. | |
| 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: | |
| # filter with 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) | |
| # location is not specified: try to discover a Python installation | |
| 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 | |
| # ensure executable publishes not-found marker | |
| expose_options(options, ['executable'], dict(), not_found=not_found, | |
| logger=logger) | |
| logger.warning('Python not found') | |
| return | |
| def install(self): | |
| return [] | |
| update = install | |