Repository: pypa/build (python)

Issue:
pipx run --spec build still broken
I'm still getting the same error as in #226:

```
Run pipx run --spec build==0.2.1 pyproject-build --sdist
Uninstalling setuptools-39.0.1:
  Successfully uninstalled setuptools-39.0.1
25l25h
Usage:   
  /tmp/build-env-5zipwlv4/bin/python -m pip install [options] <requirement specifier> [package-index-options] ...
  /tmp/build-env-5zipwlv4/bin/python -m pip install [options] -r <requirements file> [package-index-options] ...
  /tmp/build-env-5zipwlv4/bin/python -m pip install [options] [-e] <vcs project url> ...
  /tmp/build-env-5zipwlv4/bin/python -m pip install [options] [-e] <local project path> ...
  /tmp/build-env-5zipwlv4/bin/python -m pip install [options] <archive url/path> ...

no such option: --no-warn-script-location
Traceback (most recent call last):
  File "/opt/pipx/.cache/14c4e297c8a7e45/bin/pyproject-build", line 8, in <module>
    sys.exit(entrypoint())
  File "/opt/pipx/.cache/14c4e297c8a7e45/lib/python3.6/site-packages/build/__main__.py", line 210, in entrypoint
    main(sys.argv[1:])
  File "/opt/pipx/.cache/14c4e297c8a7e45/lib/python3.6/site-packages/build/__main__.py", line 206, in main
    build_package(args.srcdir, outdir, distributions, config_settings, not args.no_isolation, args.skip_dependencies)
  File "/opt/pipx/.cache/14c4e297c8a7e45/lib/python3.6/site-packages/build/__main__.py", line 94, in build_package
    _build_in_isolated_env(builder, outdir, distributions, config_settings)
  File "/opt/pipx/.cache/14c4e297c8a7e45/lib/python3.6/site-packages/build/__main__.py", line 56, in _build_in_isolated_env
    env.install(builder.build_dependencies)
  File "/opt/pipx/.cache/14c4e297c8a7e45/lib/python3.6/site-packages/build/env.py", line 143, in install
    subprocess.check_call(cmd)
  File "/usr/lib/python3.6/subprocess.py", line 311, in check_call
    raise CalledProcessError(retcode, cmd)
subprocess.CalledProcessError: Command '['/tmp/build-env-5zipwlv4/bin/python', '-Im', 'pip', 'install', '--no-warn-script-location', '-r', '/tmp/build-reqs-pgst_mml.txt']' returned non-zero exit status 2.
```


See here:
https://github.com/scikit-hep/boost-histogram/runs/1868826515?check_suite_focus=true


I can reproduce it locally with these four lines:

```bash
docker run --rm -it ubuntu:18.04
apt-get update && apt-get install -y python3-pip python3-venv
python3 -m pip install pipx
pipx run --spec build pyproject-build
```

Pull request description:
env: upgrade pip based on pi version (fixes Python 3.6.5-3.8 issues, including in GitHub Actions via pipx)
Fixes #228

- fix: Upgrade pip for all Python < 3.7
- fix: avoid breaking so badly if pip is too old


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 1c83b16..c6a2b67 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -3,6 +3,16 @@ Changelog
 +++++++++
 
 
+Unreleased
+==========
+
+- Upgrade pip based on venv pip version, avoids error from unrecognised pip flag on Debian Python 3.6.5-3.8 (`PR #229`_, Fixes `#228`_)
+
+.. _PR #229: https://github.com/pypa/build/pull/229
+.. _#228: https://github.com/pypa/build/issues/228
+
+
+
 0.2.1 (09-02-2021)
 ==================
 
diff --git a/src/build/env.py b/src/build/env.py
index c0bdc2c..8d79527 100644
--- a/src/build/env.py
+++ b/src/build/env.py
@@ -13,9 +13,16 @@ import tempfile
 from types import TracebackType
 from typing import Iterable, Optional, Tuple, Type
 
+import packaging.version
+
 from ._compat import abstractproperty, add_metaclass
 
 
+if sys.version_info < (3, 8):
+    import importlib_metadata as metadata
+else:
+    from importlib import metadata
+
 try:
     import virtualenv
 except ImportError:  # pragma: no cover
@@ -169,20 +176,28 @@ def _create_isolated_env_venv(path):  # type: (str) -> Tuple[str, str]
     import venv
 
     venv.EnvBuilder(with_pip=True).create(path)
-    executable, script_dir = _find_executable_and_scripts(path)
-    # avoid the setuptools from ensurepip to break the isolation
-    if sys.version_info < (3, 6, 6):  # python 3.5 up to 3.6.5 come with pip 9 that's too old, for new standards
+    executable, script_dir, purelib = _find_executable_and_scripts(path)
+
+    # Get the version of pip in the environment
+    pip_distribution = next(iter(metadata.distributions(name='pip', path=[purelib])))
+    pip_version = packaging.version.Version(pip_distribution.version)
+
+    # Currently upgrade if Pip 19.1+ not available, since Pip 19 is the first
+    # one to officially support PEP 517, and 19.1 supports manylinux1.
+    if pip_version < packaging.version.Version('19.1'):
         subprocess.check_call([executable, '-m', 'pip', 'install', '-U', 'pip'])
+
+    # Avoid the setuptools from ensurepip to break the isolation
     subprocess.check_call([executable, '-m', 'pip', 'uninstall', 'setuptools', '-y'])
     return executable, script_dir
 
 
-def _find_executable_and_scripts(path):  # type: (str) -> Tuple[str, str]
+def _find_executable_and_scripts(path):  # type: (str) -> Tuple[str, str, str]
     """
     Detect the Python executable and script folder of a virtual environment.
 
     :param path: The location of the virtual environment
-    :return: The Python executable and script folder
+    :return: The Python executable, script folder, and purelib folder
     """
     config_vars = sysconfig.get_config_vars().copy()  # globally cached, copy before altering it
     config_vars['base'] = path
@@ -195,7 +210,11 @@ def _find_executable_and_scripts(path):  # type: (str) -> Tuple[str, str]
     executable = os.path.join(env_scripts, exe)
     if not os.path.exists(executable):
         raise RuntimeError('Virtual environment creation failed, executable {} missing'.format(executable))
-    return executable, env_scripts
+
+    purelib = sysconfig.get_path('purelib', vars=config_vars)
+    if not purelib:
+        raise RuntimeError("Couldn't get environment purelib folder")
+    return executable, env_scripts, purelib
 
 
 __all__ = (


Test patch:
diff --git a/tests/test_env.py b/tests/test_env.py
index f677659..04f18ab 100644
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -8,6 +8,8 @@ import sysconfig
 
 import pytest
 
+from packaging.version import Version
+
 import build.env
 
 
@@ -58,6 +60,25 @@ def test_fail_to_get_script_path(mocker):
     assert get_path.call_count == 1
 
 
+@pytest.mark.skipif(sys.version_info[0] == 2, reason='venv module used on Python 3 only')
+@pytest.mark.skipif(IS_PYPY3, reason='PyPy3 uses get path to create and provision venv')
+def test_fail_to_get_purepath(mocker):
+    sysconfig_get_path = sysconfig.get_path
+
+    def mock_sysconfig_get_path(path, *args, **kwargs):
+        if path == 'purelib':
+            return ''
+        else:
+            return sysconfig_get_path(path, *args, **kwargs)
+
+    mocker.patch('sysconfig.get_path', side_effect=mock_sysconfig_get_path)
+
+    with pytest.raises(RuntimeError, match="Couldn't get environment purelib folder"):
+        env = build.env.IsolatedEnvBuilder()
+        with env:
+            pass
+
+
 @pytest.mark.skipif(sys.version_info[0] == 2, reason='venv module used on Python 3 only')
 @pytest.mark.skipif(IS_PYPY3, reason='PyPy3 uses get path to create and provision venv')
 def test_executable_missing_post_creation(mocker):
@@ -100,7 +121,9 @@ def test_isolated_env_has_install_still_abstract():
 
 
 @pytest.mark.isolated
-@pytest.mark.skipif(sys.version_info > (3, 6, 5), reason='inapplicable')
-def test_default_pip_is_upgraded_on_python_3_6_5_and_below():
+def test_default_pip_is_never_too_old():
     with build.env.IsolatedEnvBuilder() as env:
-        assert not subprocess.check_output([env.executable, '-m', 'pip', '-V']).startswith(b'pip 9.0')
+        version = subprocess.check_output(
+            [env.executable, '-c', 'import pip; print(pip.__version__)'], universal_newlines=True
+        ).strip()
+        assert Version(version) >= Version('19.1')