[pypy-commit] pypy py3.5: find and follow 'pyvenv.cfg' files, hopefully like CPython does

arigo pypy.commits at gmail.com
Wed Mar 8 11:11:50 EST 2017


Author: Armin Rigo <arigo at tunes.org>
Branch: py3.5
Changeset: r90598:d129aab5df7d
Date: 2017-03-08 17:11 +0100
http://bitbucket.org/pypy/pypy/changeset/d129aab5df7d/

Log:	find and follow 'pyvenv.cfg' files, hopefully like CPython does

diff --git a/lib-python/3/venv/__init__.py b/lib-python/3/venv/__init__.py
--- a/lib-python/3/venv/__init__.py
+++ b/lib-python/3/venv/__init__.py
@@ -119,6 +119,14 @@
             executable = os.environ['__PYVENV_LAUNCHER__']
         else:
             executable = sys.executable
+        #
+        # PyPy extension: resolve 'executable' if it is a symlink
+        try:
+            executable = os.path.join(os.path.dirname(executable),
+                                      os.readlink(executable))
+        except OSError:
+            pass
+        #
         dirname, exename = os.path.split(os.path.abspath(executable))
         context.executable = executable
         context.python_dir = dirname
@@ -217,6 +225,7 @@
                     copier(context.env_exe, path, relative_symlinks_ok=True)
                     if not os.path.islink(path):
                         os.chmod(path, 0o755)
+            #
             # PyPy extension: also copy the main library, not just the
             # small executable
             for libname in ['libpypy-c.so', 'libpypy-c.dylib']:
@@ -228,6 +237,7 @@
                     copier(src_library, dest_library)
                     if not os.path.islink(dest_library):
                         os.chmod(dest_library, 0o755)
+            #
         else:
             subdir = 'DLLs'
             include = self.include_binary
diff --git a/pypy/module/sys/initpath.py b/pypy/module/sys/initpath.py
--- a/pypy/module/sys/initpath.py
+++ b/pypy/module/sys/initpath.py
@@ -73,18 +73,57 @@
     return dirname
 
 
+def find_pyvenv_cfg(dirname):
+    try:
+        fd = os.open(os.path.join(dirname, 'pyvenv.cfg'), os.O_RDONLY)
+        try:
+            content = os.read(fd, 16384)
+        finally:
+            os.close(fd)
+    except OSError:
+        return ''
+    # painfully parse the file for a line 'home = PATH'
+    for line in content.splitlines():
+        line += '\n'
+        i = 0
+        while line[i] == ' ':
+            i += 1
+        if (line[i] == 'h' and
+            line[i+1] == 'o' and
+            line[i+2] == 'm' and
+            line[i+3] == 'e'):
+            i += 4
+            while line[i] == ' ':
+                i += 1
+            if line[i] == '=':
+                i += 1
+                return line[i:].strip()
+    return ''
+
+
 def find_stdlib(state, executable):
     """
     Find and compute the stdlib path, starting from the directory where
     ``executable`` is and going one level up until we find it.  Return a
     tuple (path, prefix), where ``prefix`` is the root directory which
     contains the stdlib.  If it cannot be found, return (None, None).
+
+    On PyPy3, it will also look for 'pyvenv.cfg' either in the same or
+    in the parent directory of 'executable', and search from the 'home'
+    entry instead of from the path to 'executable'.
     """
     search = 'pypy-c' if executable == '' else executable
+    search_pyvenv_cfg = 2
     while True:
         dirname = resolvedirof(search)
         if dirname == search:
             return None, None  # not found :-(
+        if search_pyvenv_cfg > 0:
+            search_pyvenv_cfg -= 1
+            home = find_pyvenv_cfg(dirname)
+            if home:
+                dirname = home
+                search_pyvenv_cfg = 0
         newpath = compute_stdlib_path_maybe(state, dirname)
         if newpath is not None:
             return newpath, dirname
diff --git a/pypy/module/sys/test/test_initpath.py b/pypy/module/sys/test/test_initpath.py
--- a/pypy/module/sys/test/test_initpath.py
+++ b/pypy/module/sys/test/test_initpath.py
@@ -2,7 +2,8 @@
 import os.path
 from pypy.module.sys.initpath import (compute_stdlib_path, find_executable,
                                       find_stdlib, resolvedirof,
-                                      pypy_init_home, pypy_init_free)
+                                      pypy_init_home, pypy_init_free,
+                                      find_pyvenv_cfg)
 from pypy.module.sys.version import PYPY_VERSION, CPYTHON_VERSION
 from rpython.rtyper.lltypesystem import rffi
 
@@ -114,3 +115,26 @@
         myfile2 = bar.join('myfile')
         myfile2.mksymlinkto(myfile)
         assert resolvedirof(str(myfile2)) == foo
+
+def test_find_pyvenv_cfg(tmpdir):
+    subdir = tmpdir.join('find_cfg').ensure(dir=True)
+    assert find_pyvenv_cfg(str(subdir)) == ''
+    subdir.join('pyvenv.cfg').write('foobar')
+    assert find_pyvenv_cfg(str(subdir)) == ''
+    subdir.join('pyvenv.cfg').write('foobar\nhome=xyz')
+    assert find_pyvenv_cfg(str(subdir)) == 'xyz'
+    subdir.join('pyvenv.cfg').write('foohome=xyz')
+    assert find_pyvenv_cfg(str(subdir)) == ''
+    subdir.join('pyvenv.cfg').write('home = xyx \nbar = baz\n')
+    assert find_pyvenv_cfg(str(subdir)) == 'xyx'
+
+def test_find_stdlib_follow_pyvenv_cfg(tmpdir):
+    mydir = tmpdir.join('follow_pyvenv_cfg').ensure(dir=True)
+    otherdir = tmpdir.join('otherdir').ensure(dir=True)
+    bin_dir = mydir.join('bin').ensure(dir=True)
+    pypy = bin_dir.join('pypy').ensure(file=True)
+    build_hierarchy(otherdir)
+    for homedir in [otherdir, otherdir.join('bin')]:
+        mydir.join('pyvenv.cfg').write('home = %s\n' % (homedir,))
+        _, prefix = find_stdlib(None, str(pypy))
+        assert prefix == otherdir


More information about the pypy-commit mailing list