[Checkins] SVN: zc.buildout/branches/python-3-2/src/zc/buildout/ Restructure calling external interpreters.
Christian Theune
ct at gocept.com
Thu Apr 7 09:43:01 EDT 2011
Log message for revision 121332:
Restructure calling external interpreters.
* make IO handling consistent on Python 2 and 3
* simplify business code, extract into separate function
Changed:
A zc.buildout/branches/python-3-2/src/zc/buildout/compat23.py
U zc.buildout/branches/python-3-2/src/zc/buildout/easy_install.py
U zc.buildout/branches/python-3-2/src/zc/buildout/testing.py
-=-
Added: zc.buildout/branches/python-3-2/src/zc/buildout/compat23.py
===================================================================
--- zc.buildout/branches/python-3-2/src/zc/buildout/compat23.py (rev 0)
+++ zc.buildout/branches/python-3-2/src/zc/buildout/compat23.py 2011-04-07 13:42:59 UTC (rev 121332)
@@ -0,0 +1,65 @@
+#############################################################################
+#
+# Copyright (c) 2011 Zope Foundation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""Compatibility functions for handling Python 2->3 runtime issues."""
+
+import os
+import subprocess
+import sys
+import tempfile
+
+MUST_CLOSE_FDS = not sys.platform.startswith('win')
+
+
+class Result(object):
+ pass
+
+
+def call_external_python(cmd, suite_source, env=None):
+ """Quote a given code suite for consumption by `python -c '...'`
+ and ensure print output encoding in UTF-8."""
+ if isinstance(cmd, str):
+ cmd = [cmd]
+ code_file_path = tempfile.mktemp('.py')
+ cmd.append(code_file_path)
+ code_file = open(code_file_path, 'w')
+ try:
+ code_file.write(suite_source)
+ code_file.write("""
+import sys
+import os
+v = sys.version_info[0]
+if v == 2:
+ decode = True
+elif v == 3 and isinstance(result, bytes):
+ decode = True
+else:
+ decode = False
+if decode:
+ result = result.decode(sys.getfilesystemencoding())
+os.write(1, result.encode('utf-8'))
+""")
+ code_file.close()
+ process = subprocess.Popen(cmd,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ env=env,
+ close_fds=MUST_CLOSE_FDS)
+ out, err = process.communicate()
+ finally:
+ os.unlink(code_file_path)
+ result = Result()
+ result.out = out.decode('utf-8').strip()
+ result.err = err
+ result.returncode = process.returncode
+ return result
Modified: zc.buildout/branches/python-3-2/src/zc/buildout/easy_install.py
===================================================================
--- zc.buildout/branches/python-3-2/src/zc/buildout/easy_install.py 2011-04-07 13:38:13 UTC (rev 121331)
+++ zc.buildout/branches/python-3-2/src/zc/buildout/easy_install.py 2011-04-07 13:42:59 UTC (rev 121332)
@@ -37,6 +37,7 @@
import warnings
import zc.buildout
import zipimport
+from zc.buildout.compat23 import call_external_python
_oprp = getattr(os.path, 'realpath', lambda path: path)
def realpath(path):
@@ -1593,33 +1594,32 @@
- executable is a path to the desired Python executable.
- name is the name of the (pure, not C) Python module.
"""
- cmd = [executable, "-Sc",
- "import imp; "
- "fp, path, desc = imp.find_module(%r); "
- "fp.close(); "
- "print(path)" % (name,)]
env = os.environ.copy()
# We need to make sure that PYTHONPATH, which will often be set to
# include a custom buildout-generated site.py, is not set, or else
# we will not get an accurate value for the "real" site.py and
# sitecustomize.py.
env.pop('PYTHONPATH', None)
- _proc = subprocess.Popen(
- cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env)
- stdout, stderr = _proc.communicate();
- if _proc.returncode:
+ r = call_external_python([executable, "-S"],
+ """\
+import imp
+fp, path, desc = imp.find_module(%r)
+fp.close()
+result = path""" % (name,),
+ env)
+ if r.returncode:
if not silent:
logger.info(
- 'Could not find file for module %s:\n%s', name, stderr)
+ 'Could not find file for module %s:\n%s', name, r.err)
return None
# else: ...
- res = stdout.strip()
- if res.endswith('.pyc'.encode()) or res.endswith('.pyo'.encode()):
+ candidate = r.out
+ if candidate.endswith('.pyc') or candidate.endswith('.pyo'):
raise RuntimeError('Cannot find uncompiled version of %s' % (name,))
- if not os.path.exists(res):
+ if not os.path.exists(candidate):
raise RuntimeError(
- 'File does not exist for module %s:\n%s' % (name, res))
- return res
+ 'File does not exist for module %s:\n%s' % (name, candidate))
+ return candidate
def _generate_sitecustomize(dest, executable, initialization='',
exec_sitecustomize=False):
Modified: zc.buildout/branches/python-3-2/src/zc/buildout/testing.py
===================================================================
--- zc.buildout/branches/python-3-2/src/zc/buildout/testing.py 2011-04-07 13:38:13 UTC (rev 121331)
+++ zc.buildout/branches/python-3-2/src/zc/buildout/testing.py 2011-04-07 13:42:59 UTC (rev 121332)
@@ -41,6 +41,7 @@
import zc.buildout.easy_install
from zc.buildout.easy_install import setuptools_key
from zc.buildout.rmtree import rmtree
+from zc.buildout.compat23 import call_external_python
fsync = getattr(os, 'fsync', lambda fileno: None)
is_win32 = sys.platform == 'win32'
@@ -189,66 +190,39 @@
'--single-version-externally-managed')
def find_python(version):
+ """Return the path to a Python executable for a given version of Python.
+
+ The version is given as a 2-component string: '2.4', '3.2', ...
+
+ """
env_friendly_version = ''.join(version.split('.'))
- e = os.environ.get('PYTHON%s' % env_friendly_version)
- if e is not None:
- return e
+ candidates = []
+ candidates.append(os.environ.get('PYTHON%s' % env_friendly_version))
+
if is_win32:
- e = '\Python%s\python.exe' % env_friendly_version
- if os.path.exists(e):
- return e
+ candidates.append('\Python%s\python.exe' % env_friendly_version)
else:
- if version.startswith('2.'):
- cmd = ('python%s -c "import sys; print(sys.executable.decode('
- 'sys.getfilesystemencoding()).encode(\'utf-8\'))"' %
- version)
- else:
- cmd = ('python%s -c "import sys; print('
- 'sys.executable.encode(\'utf-8\'))"' % version)
- p = subprocess.Popen(cmd,
- shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- close_fds=MUST_CLOSE_FDS)
- i, o = (p.stdin, p.stdout)
- i.close()
- e = o.read().decode('utf-8').strip()
- o.close()
- if os.path.exists(e):
- return e
- cmd = 'python -c "import sys; print(\'%s.%s\' % sys.version_info[:2])"'
- p = subprocess.Popen(cmd,
- shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- close_fds=MUST_CLOSE_FDS)
- i, o = (p.stdin, p.stdout)
- i.close()
- e = o.read().decode('ascii').strip()
- o.close()
- if e == version:
- if version.startswith('2.'):
- cmd = ('python -c "import sys; print(sys.executable.decode('
- 'sys.getfilesystemencoding()).encode(\'utf-8\'))"')
- else:
- cmd = ('python -c "import sys; print('
- 'sys.executable.encode(\'utf-8\'))"')
- p = subprocess.Popen(cmd,
- shell=True,
- stdin=subprocess.PIPE,
- stdout=subprocess.PIPE,
- stderr=subprocess.STDOUT,
- close_fds=MUST_CLOSE_FDS)
- i, o = (p.stdin, p.stdout)
- i.close()
- e = o.read().decode('utf-8').strip()
- o.close()
- if os.path.exists(e):
- return e
+ GET_EXECUTABLE_PATH = 'import sys; result = sys.executable'
+ # Try Python executable with version encoded in filename
+ r = call_external_python(
+ 'python%s' % version, GET_EXECUTABLE_PATH)
+ candidates.append(r.out)
+ # Try Python executable without version encoded in filename
+ r = call_external_python(
+ 'python', 'import sys; result = "%s.%s\" % sys.version_info[:2]')
+
+ if r.out == version:
+ r = call_external_python('python', GET_EXECUTABLE_PATH)
+ candidates.append(r.out)
+
+ for candidate in candidates:
+ if not candidate:
+ continue
+ if os.path.exists(candidate):
+ return candidate
+
raise ValueError(
"Couldn't figure out the executable for Python %(version)s.\n"
"Set the environment variable PYTHON%(envversion)s to the location\n"
More information about the checkins
mailing list