Skip to content

Commit

Permalink
Merge pull request #5190 from benoit-pierre/fix_pep518_support
Browse files Browse the repository at this point in the history
fix PEP 518 support
  • Loading branch information
pfmoore authored Apr 9, 2018
2 parents 8e07440 + 6da1d9e commit 516adb4
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 119 deletions.
1 change: 1 addition & 0 deletions news/5188.bugfix
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix PEP 518 support.
12 changes: 8 additions & 4 deletions src/pip/_internal/build_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"""

import os
from distutils.sysconfig import get_python_lib
from sysconfig import get_paths

from pip._internal.utils.temp_dir import TempDirectory
Expand Down Expand Up @@ -38,11 +39,14 @@ def __enter__(self):
else:
os.environ['PATH'] = scripts + os.pathsep + os.defpath

if install_dirs['purelib'] == install_dirs['platlib']:
lib_dirs = install_dirs['purelib']
# Note: prefer distutils' sysconfig to get the
# library paths so PyPy is correctly supported.
purelib = get_python_lib(plat_specific=0, prefix=self.path)
platlib = get_python_lib(plat_specific=1, prefix=self.path)
if purelib == platlib:
lib_dirs = purelib
else:
lib_dirs = install_dirs['purelib'] + os.pathsep + \
install_dirs['platlib']
lib_dirs = purelib + os.pathsep + platlib
if self.save_pythonpath:
os.environ['PYTHONPATH'] = lib_dirs + os.pathsep + \
self.save_pythonpath
Expand Down
22 changes: 0 additions & 22 deletions src/pip/_internal/commands/wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
from pip._internal.operations.prepare import RequirementPreparer
from pip._internal.req import RequirementSet
from pip._internal.resolve import Resolver
from pip._internal.utils.misc import import_or_raise
from pip._internal.utils.temp_dir import TempDirectory
from pip._internal.wheel import WheelBuilder

Expand Down Expand Up @@ -102,28 +101,7 @@ def __init__(self, *args, **kw):
self.parser.insert_option_group(0, index_opts)
self.parser.insert_option_group(0, cmd_opts)

def check_required_packages(self):
import_or_raise(
'wheel.bdist_wheel',
CommandError,
"'pip wheel' requires the 'wheel' package. To fix this, run: "
"pip install wheel"
)

need_setuptools_message = (
"'pip wheel' requires setuptools >= 0.8 for dist-info support. "
"To fix this, run: pip install --upgrade setuptools>=0.8"
)
pkg_resources = import_or_raise(
'pkg_resources',
CommandError,
need_setuptools_message
)
if not hasattr(pkg_resources, 'DistInfoDistribution'):
raise CommandError(need_setuptools_message)

def run(self, options, args):
self.check_required_packages()
cmdoptions.check_install_build_global(options)

index_urls = [options.index_url] + options.extra_index_urls
Expand Down
28 changes: 17 additions & 11 deletions src/pip/_internal/operations/prepare.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,25 +126,31 @@ def prep_for_dist(self, finder, build_isolation):
build_requirements, isolate = self.req.get_pep_518_info()
should_isolate = build_isolation and isolate

if 'setuptools' not in build_requirements:
minimum_requirements = ('setuptools', 'wheel')
missing_requirements = set(minimum_requirements) - set(
pkg_resources.Requirement(r).key
for r in build_requirements
)
if missing_requirements:
def format_reqs(rs):
return ' and '.join(map(repr, sorted(rs)))
logger.warning(
"%s does not include 'setuptools' as a buildtime requirement "
"in its pyproject.toml.", self.req.name,
"Missing build time requirements in pyproject.toml for %s: "
"%s.", self.req, format_reqs(missing_requirements)
)
logger.warning(
"This version of pip does not implement PEP 517 so it cannot "
"build a wheel without setuptools."
"build a wheel without %s.", format_reqs(minimum_requirements)
)

if not should_isolate:
self.req.build_env = NoOpBuildEnvironment(no_clean=False)

with self.req.build_env as prefix:
if should_isolate:
if should_isolate:
with self.req.build_env as prefix:
_install_build_reqs(finder, prefix, build_requirements)
else:
self.req.build_env = NoOpBuildEnvironment(no_clean=False)

self.req.run_egg_info()
self.req.assert_source_matches_version()
self.req.run_egg_info()
self.req.assert_source_matches_version()


class Installed(DistAbstraction):
Expand Down
42 changes: 13 additions & 29 deletions src/pip/_internal/req/req_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -413,24 +413,6 @@ def setup_py_dir(self):
@property
def setup_py(self):
assert self.source_dir, "No source dir for %s" % self
cmd = [sys.executable, '-c', 'import setuptools']
output = call_subprocess(
cmd,
show_stdout=False,
command_desc='python -c "import setuptools"',
on_returncode='ignore',
)

if output:
if get_installed_version('setuptools') is None:
add_msg = "Please install setuptools."
else:
add_msg = output
# Setuptools is not available
raise InstallationError(
"Could not import setuptools which is required to "
"install from a source distribution.\n%s" % add_msg
)

setup_py = os.path.join(self.setup_py_dir, 'setup.py')

Expand Down Expand Up @@ -496,11 +478,12 @@ def run_egg_info(self):
egg_info_dir = os.path.join(self.setup_py_dir, 'pip-egg-info')
ensure_dir(egg_info_dir)
egg_base_option = ['--egg-base', 'pip-egg-info']
call_subprocess(
egg_info_cmd + egg_base_option,
cwd=self.setup_py_dir,
show_stdout=False,
command_desc='python setup.py egg_info')
with self.build_env:
call_subprocess(
egg_info_cmd + egg_base_option,
cwd=self.setup_py_dir,
show_stdout=False,
command_desc='python setup.py egg_info')

if not self.req:
if isinstance(parse_version(self.pkg_info()["Version"]), Version):
Expand Down Expand Up @@ -788,12 +771,13 @@ def install(self, install_options, global_options=None, root=None,
msg = 'Running setup.py install for %s' % (self.name,)
with open_spinner(msg) as spinner:
with indent_log():
call_subprocess(
install_args + install_options,
cwd=self.setup_py_dir,
show_stdout=False,
spinner=spinner,
)
with self.build_env:
call_subprocess(
install_args + install_options,
cwd=self.setup_py_dir,
show_stdout=False,
spinner=spinner,
)

if not os.path.exists(record_filename):
logger.debug('Record file %s not found', record_filename)
Expand Down
66 changes: 24 additions & 42 deletions tests/functional/test_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,48 +19,30 @@
from tests.lib.path import Path


def test_without_setuptools(script, data):
script.pip("uninstall", "setuptools", "-y")
result = script.run(
"python", "-c",
"import pip._internal; pip._internal.main(["
"'install', "
"'INITools==0.2', "
"'-f', '%s', "
"'--no-binary=:all:'])" % data.packages,
expect_error=True,
)
assert (
"Could not import setuptools which is required to install from a "
"source distribution."
in result.stderr
)
assert "Please install setuptools" in result.stderr


def test_with_setuptools_and_import_error(script, data):
# Make sure we get an ImportError while importing setuptools
setuptools_init_path = script.site_packages_path.join(
"setuptools", "__init__.py")
with open(setuptools_init_path, 'a') as f:
f.write('\nraise ImportError("toto")')

result = script.run(
"python", "-c",
"import pip._internal; pip._internal.main(["
"'install', "
"'INITools==0.2', "
"'-f', '%s', "
"'--no-binary=:all:'])" % data.packages,
expect_error=True,
)
assert (
"Could not import setuptools which is required to install from a "
"source distribution."
in result.stderr
)
assert "Traceback " in result.stderr
assert "ImportError: toto" in result.stderr
@pytest.mark.parametrize('original_setuptools', ('missing', 'bad'))
def test_pep518_uses_build_env(script, data, original_setuptools):
if original_setuptools == 'missing':
script.pip("uninstall", "-y", "setuptools")
elif original_setuptools == 'bad':
setuptools_init_path = script.site_packages_path.join(
"setuptools", "__init__.py")
with open(setuptools_init_path, 'a') as f:
f.write('\nraise ImportError("toto")')
else:
raise ValueError(original_setuptools)
to_install = data.src.join("pep518-3.0")
for command in ('install', 'wheel'):
kwargs = {}
if sys.version_info[:2] == (3, 3):
# Ignore Python 3.3 deprecation warning...
kwargs['expect_stderr'] = True
script.run(
"python", "-c",
"import pip._internal; pip._internal.main(["
"%r, " "'-f', %r, " "%r, "
"])" % (command, str(data.packages), str(to_install)),
**kwargs
)


@pytest.mark.network
Expand Down
11 changes: 0 additions & 11 deletions tests/functional/test_wheel.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,6 @@
from tests.lib import pyversion


def test_basic_pip_wheel_fails_without_wheel(script, data):
"""
Test 'pip wheel' fails without wheel
"""
result = script.pip(
'wheel', '--no-index', '-f', data.find_links, 'simple==3.0',
expect_error=True,
)
assert "'pip wheel' requires the 'wheel' package" in result.stderr


def test_wheel_exit_status_code_when_no_requirements(script, common_wheels):
"""
Test wheel exit status code when no requirements specified
Expand Down

0 comments on commit 516adb4

Please sign in to comment.