Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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']:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume the changes to this block are related to PyPy support? Having a comment here explaining what's going on would be really useful (from my tests on Windows, get_python_lib returns absolute paths, so what's up with appending platlib to purelib?)

(I know this is basically code that was already present, so I'm happy if the answer is that you don't know enough of the detail to write a reasonable comment).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add a comment. As for appending, see comment above.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Realising it's os.pathsep, the comment's probably not needed...

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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use os.path.join here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's os.pathsep, not os.path.sep.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doh. I'm dense, sorry.

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