Skip to content

Commit 9005f3c

Browse files
committed
env: override default scheme for Apple Python venv
1 parent 665918f commit 9005f3c

2 files changed

Lines changed: 29 additions & 16 deletions

File tree

src/build/env.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -246,20 +246,22 @@ def _find_executable_and_scripts(path): # type: (str) -> Tuple[str, str, str]
246246
"""
247247
config_vars = sysconfig.get_config_vars().copy() # globally cached, copy before altering it
248248
config_vars['base'] = path
249-
env_scripts = sysconfig.get_path('scripts', vars=config_vars)
250-
if not env_scripts:
251-
raise RuntimeError("Couldn't get environment scripts path")
252-
exe = 'pypy3' if platform.python_implementation() == 'PyPy' else 'python'
253-
if os.name == 'nt':
254-
exe = '{}.exe'.format(exe)
255-
executable = os.path.join(env_scripts, exe)
249+
# The Python that ships with the macOS developer tools varies the
250+
# default scheme depending on whether the ``sys.prefix`` is part of a framework.
251+
# The framework "osx_framework_library" scheme
252+
# can't be used to expand the paths in a venv, which
253+
# can happen if build itself is not installed in a venv.
254+
# If the Apple-custom "osx_framework_library" scheme is available
255+
# we enforce "posix_prefix", the venv scheme, for isolated envs.
256+
if 'osx_framework_library' in sysconfig.get_scheme_names():
257+
paths = sysconfig.get_paths(scheme='posix_prefix', vars=config_vars)
258+
else:
259+
paths = sysconfig.get_paths(vars=config_vars)
260+
executable = os.path.join(paths['scripts'], 'python.exe' if os.name == 'nt' else 'python')
256261
if not os.path.exists(executable):
257262
raise RuntimeError('Virtual environment creation failed, executable {} missing'.format(executable))
258263

259-
purelib = sysconfig.get_path('purelib', vars=config_vars)
260-
if not purelib:
261-
raise RuntimeError("Couldn't get environment purelib folder")
262-
return executable, env_scripts, purelib
264+
return executable, paths['scripts'], paths['purelib']
263265

264266

265267
__all__ = (

tests/test_env.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,32 @@ def test_isolated_environment_install(mocker):
5151
]
5252

5353

54+
@pytest.mark.skipif(IS_PY2, reason='venv module used on Python 3 only')
55+
@pytest.mark.skipif(IS_PYPY3, reason='PyPy3 uses get path to create and provision venv')
56+
@pytest.mark.skipif(sys.platform != 'darwin', reason='workaround for Apple Python')
57+
def test_can_get_venv_paths_with_conflicting_default_scheme(mocker):
58+
mocker.patch.object(build.env, 'virtualenv', None)
59+
get_scheme_names = mocker.patch('sysconfig.get_scheme_names', return_value=('osx_framework_library',))
60+
with build.env.IsolatedEnvBuilder():
61+
pass
62+
assert get_scheme_names.call_count == 1
63+
64+
5465
@pytest.mark.skipif(IS_PY2, reason='venv module used on Python 3 only')
5566
@pytest.mark.skipif(IS_PYPY3, reason='PyPy3 uses get path to create and provision venv')
5667
def test_executable_missing_post_creation(mocker):
5768
mocker.patch.object(build.env, 'virtualenv', None)
58-
original_get_path = sysconfig.get_path
69+
original_get_paths = sysconfig.get_paths
5970

60-
def _get_path(name, vars): # noqa
71+
def _get_paths(vars): # noqa
6172
shutil.rmtree(vars['base'])
62-
return original_get_path(name, vars=vars)
73+
return original_get_paths(vars=vars)
6374

64-
get_path = mocker.patch('sysconfig.get_path', side_effect=_get_path)
75+
get_paths = mocker.patch('sysconfig.get_paths', side_effect=_get_paths)
6576
with pytest.raises(RuntimeError, match='Virtual environment creation failed, executable .* missing'):
6677
with build.env.IsolatedEnvBuilder():
6778
pass
68-
assert get_path.call_count == 1
79+
assert get_paths.call_count == 1
6980

7081

7182
def test_isolated_env_abstract():

0 commit comments

Comments
 (0)