Skip to content

Commit

Permalink
Merge pull request #1830 from benoit-pierre/pip_wheels
Browse files Browse the repository at this point in the history
drop easy_install script/command, re-implement `fetch_build_egg` to use pip
  • Loading branch information
jaraco authored Nov 16, 2019
2 parents a007982 + b8101f0 commit 402240c
Show file tree
Hide file tree
Showing 15 changed files with 364 additions and 1,258 deletions.
1,085 changes: 0 additions & 1,085 deletions docs/easy_install.txt

This file was deleted.

1 change: 0 additions & 1 deletion docs/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,4 @@ Documentation content:
python3
development
roadmap
Deprecated: Easy Install <easy_install>
history
17 changes: 9 additions & 8 deletions docs/setuptools.txt
Original file line number Diff line number Diff line change
Expand Up @@ -282,10 +282,11 @@ unless you need the associated ``setuptools`` feature.
``setup_requires``
A string or list of strings specifying what other distributions need to
be present in order for the *setup script* to run. ``setuptools`` will
attempt to obtain these before processing the rest of the setup script or
commands. This argument is needed if you are using distutils extensions as
part of your build process; for example, extensions that process setup()
arguments and turn them into EGG-INFO metadata files.
attempt to obtain these (using pip if available) before processing the
rest of the setup script or commands. This argument is needed if you
are using distutils extensions as part of your build process; for
example, extensions that process setup() arguments and turn them into
EGG-INFO metadata files.

(Note: projects listed in ``setup_requires`` will NOT be automatically
installed on the system where the setup script is being run. They are
Expand Down Expand Up @@ -332,10 +333,10 @@ unless you need the associated ``setuptools`` feature.
needed to install it, you can use this option to specify them. It should
be a string or list of strings specifying what other distributions need to
be present for the package's tests to run. When you run the ``test``
command, ``setuptools`` will attempt to obtain these. Note that these
required projects will *not* be installed on the system where the tests
are run, but only downloaded to the project's setup directory if they're
not already installed locally.
command, ``setuptools`` will attempt to obtain these (using pip if
available). Note that these required projects will *not* be installed on
the system where the tests are run, but only downloaded to the project's setup
directory if they're not already installed locally.

New in 41.5.0: Deprecated the test command.

Expand Down
5 changes: 0 additions & 5 deletions easy_install.py

This file was deleted.

1 change: 0 additions & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ classifiers =
[options]
zip_safe = True
python_requires = >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
py_modules = easy_install
packages = find:

[options.packages.find]
Expand Down
19 changes: 0 additions & 19 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,6 @@ def read_commands():
return command_ns['__all__']


def _gen_console_scripts():
yield "easy_install = setuptools.command.easy_install:main"

# Gentoo distributions manage the python-version-specific scripts
# themselves, so those platforms define an environment variable to
# suppress the creation of the version-specific scripts.
var_names = (
'SETUPTOOLS_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
'DISTRIBUTE_DISABLE_VERSIONED_EASY_INSTALL_SCRIPT',
)
if any(os.environ.get(var) not in (None, "", "0") for var in var_names):
return
tmpl = "easy_install-{shortver} = setuptools.command.easy_install:main"
yield tmpl.format(shortver='{}.{}'.format(*sys.version_info))


package_data = dict(
setuptools=['script (dev).tmpl', 'script.tmpl', 'site-patch.py'],
)
Expand Down Expand Up @@ -125,9 +109,6 @@ def pypi_link(pkg_filename):
"depends.txt = setuptools.command.egg_info:warn_depends_obsolete",
"dependency_links.txt = setuptools.command.egg_info:overwrite_arg",
],
"console_scripts": list(_gen_console_scripts()),
"setuptools.installation":
['eggsecutable = setuptools.command.easy_install:bootstrap'],
},
dependency_links=[
pypi_link(
Expand Down
63 changes: 8 additions & 55 deletions setuptools/command/easy_install.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@

__all__ = [
'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
'main', 'get_exe_prefixes',
'get_exe_prefixes',
]


Expand Down Expand Up @@ -410,7 +410,13 @@ def expand_dirs(self):
]
self._expand_attrs(dirs)

def run(self):
def run(self, show_deprecation=True):
if show_deprecation:
self.announce(
"WARNING: The easy_install command is deprecated "
"and will be removed in a future version."
, log.WARN,
)
if self.verbose != self.distribution.verbose:
log.set_verbosity(self.verbose)
try:
Expand Down Expand Up @@ -2283,59 +2289,6 @@ def current_umask():
return tmp


def bootstrap():
# This function is called when setuptools*.egg is run using /bin/sh
import setuptools

argv0 = os.path.dirname(setuptools.__path__[0])
sys.argv[0] = argv0
sys.argv.append(argv0)
main()


def main(argv=None, **kw):
from setuptools import setup
from setuptools.dist import Distribution

class DistributionWithoutHelpCommands(Distribution):
common_usage = ""

def _show_help(self, *args, **kw):
with _patch_usage():
Distribution._show_help(self, *args, **kw)

if argv is None:
argv = sys.argv[1:]

with _patch_usage():
setup(
script_args=['-q', 'easy_install', '-v'] + argv,
script_name=sys.argv[0] or 'easy_install',
distclass=DistributionWithoutHelpCommands,
**kw
)


@contextlib.contextmanager
def _patch_usage():
import distutils.core
USAGE = textwrap.dedent("""
usage: %(script)s [options] requirement_or_url ...
or: %(script)s --help
""").lstrip()

def gen_usage(script_name):
return USAGE % dict(
script=os.path.basename(script_name),
)

saved = distutils.core.gen_usage
distutils.core.gen_usage = gen_usage
try:
yield
finally:
distutils.core.gen_usage = saved

class EasyInstallDeprecationWarning(SetuptoolsDeprecationWarning):
"""Class for warning about deprecations in EasyInstall in SetupTools. Not ignored by default, unlike DeprecationWarning."""

2 changes: 1 addition & 1 deletion setuptools/command/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ def do_egg_install(self):
args.insert(0, setuptools.bootstrap_install_from)

cmd.args = args
cmd.run()
cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None


Expand Down
28 changes: 2 additions & 26 deletions setuptools/dist.py
Original file line number Diff line number Diff line change
Expand Up @@ -759,32 +759,8 @@ def get_egg_cache_dir(self):

def fetch_build_egg(self, req):
"""Fetch an egg needed for building"""
from setuptools.command.easy_install import easy_install
dist = self.__class__({'script_args': ['easy_install']})
opts = dist.get_option_dict('easy_install')
opts.clear()
opts.update(
(k, v)
for k, v in self.get_option_dict('easy_install').items()
if k in (
# don't use any other settings
'find_links', 'site_dirs', 'index_url',
'optimize', 'site_dirs', 'allow_hosts',
))
if self.dependency_links:
links = self.dependency_links[:]
if 'find_links' in opts:
links = opts['find_links'][1] + links
opts['find_links'] = ('setup', links)
install_dir = self.get_egg_cache_dir()
cmd = easy_install(
dist, args=["x"], install_dir=install_dir,
exclude_scripts=True,
always_copy=False, build_directory=None, editable=False,
upgrade=False, multi_version=True, no_report=True, user=False
)
cmd.ensure_finalized()
return cmd.easy_install(req)
from setuptools.installer import fetch_build_egg
return fetch_build_egg(self, req)

def _set_global_opts_from_features(self):
"""Add --with-X/--without-X options based on optional features"""
Expand Down
129 changes: 129 additions & 0 deletions setuptools/installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import glob
import os
import subprocess
import sys
from distutils import log
from distutils.errors import DistutilsError

import pkg_resources
from setuptools.command.easy_install import easy_install
from setuptools.wheel import Wheel

from .py31compat import TemporaryDirectory


def _legacy_fetch_build_egg(dist, req):
"""Fetch an egg needed for building.
Legacy path using EasyInstall.
"""
tmp_dist = dist.__class__({'script_args': ['easy_install']})
opts = tmp_dist.get_option_dict('easy_install')
opts.clear()
opts.update(
(k, v)
for k, v in dist.get_option_dict('easy_install').items()
if k in (
# don't use any other settings
'find_links', 'site_dirs', 'index_url',
'optimize', 'site_dirs', 'allow_hosts',
))
if dist.dependency_links:
links = dist.dependency_links[:]
if 'find_links' in opts:
links = opts['find_links'][1] + links
opts['find_links'] = ('setup', links)
install_dir = dist.get_egg_cache_dir()
cmd = easy_install(
tmp_dist, args=["x"], install_dir=install_dir,
exclude_scripts=True,
always_copy=False, build_directory=None, editable=False,
upgrade=False, multi_version=True, no_report=True, user=False
)
cmd.ensure_finalized()
return cmd.easy_install(req)


def fetch_build_egg(dist, req):
"""Fetch an egg needed for building.
Use pip/wheel to fetch/build a wheel."""
# Check pip is available.
try:
pkg_resources.get_distribution('pip')
except pkg_resources.DistributionNotFound:
dist.announce(
'WARNING: The pip package is not available, falling back '
'to EasyInstall for handling setup_requires/test_requires; '
'this is deprecated and will be removed in a future version.'
, log.WARN
)
return _legacy_fetch_build_egg(dist, req)
# Warn if wheel is not.
try:
pkg_resources.get_distribution('wheel')
except pkg_resources.DistributionNotFound:
dist.announce('WARNING: The wheel package is not available.', log.WARN)
if not isinstance(req, pkg_resources.Requirement):
req = pkg_resources.Requirement.parse(req)
# Take easy_install options into account, but do not override relevant
# pip environment variables (like PIP_INDEX_URL or PIP_QUIET); they'll
# take precedence.
opts = dist.get_option_dict('easy_install')
if 'allow_hosts' in opts:
raise DistutilsError('the `allow-hosts` option is not supported '
'when using pip to install requirements.')
if 'PIP_QUIET' in os.environ or 'PIP_VERBOSE' in os.environ:
quiet = False
else:
quiet = True
if 'PIP_INDEX_URL' in os.environ:
index_url = None
elif 'index_url' in opts:
index_url = opts['index_url'][1]
else:
index_url = None
if 'find_links' in opts:
find_links = opts['find_links'][1][:]
else:
find_links = []
if dist.dependency_links:
find_links.extend(dist.dependency_links)
eggs_dir = os.path.realpath(dist.get_egg_cache_dir())
environment = pkg_resources.Environment()
for egg_dist in pkg_resources.find_distributions(eggs_dir):
if egg_dist in req and environment.can_add(egg_dist):
return egg_dist
with TemporaryDirectory() as tmpdir:
cmd = [
sys.executable, '-m', 'pip',
'--disable-pip-version-check',
'wheel', '--no-deps',
'-w', tmpdir,
]
if quiet:
cmd.append('--quiet')
if index_url is not None:
cmd.extend(('--index-url', index_url))
if find_links is not None:
for link in find_links:
cmd.extend(('--find-links', link))
# If requirement is a PEP 508 direct URL, directly pass
# the URL to pip, as `req @ url` does not work on the
# command line.
if req.url:
cmd.append(req.url)
else:
cmd.append(str(req))
try:
subprocess.check_call(cmd)
except subprocess.CalledProcessError as e:
raise DistutilsError(str(e))
wheel = Wheel(glob.glob(os.path.join(tmpdir, '*.whl'))[0])
dist_location = os.path.join(eggs_dir, wheel.egg_name())
wheel.install_as_egg(dist_location)
dist_metadata = pkg_resources.PathMetadata(
dist_location, os.path.join(dist_location, 'EGG-INFO'))
dist = pkg_resources.Distribution.from_filename(
dist_location, metadata=dist_metadata)
return dist
19 changes: 18 additions & 1 deletion setuptools/tests/server.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
"""Basic http server for tests to simulate PyPI or custom indexes
"""

import os
import time
import threading

from setuptools.extern.six.moves import BaseHTTPServer, SimpleHTTPServer
from setuptools.extern.six.moves.urllib_parse import urljoin
from setuptools.extern.six.moves.urllib.request import pathname2url


class IndexServer(BaseHTTPServer.HTTPServer):
Expand Down Expand Up @@ -69,6 +72,20 @@ def __init__(
def run(self):
self.serve_forever()

@property
def netloc(self):
return 'localhost:%s' % self.server_port

@property
def url(self):
return 'http://localhost:%(server_port)s/' % vars(self)
return 'http://%s/' % self.netloc


def path_to_url(path, authority=None):
""" Convert a path to a file: URL. """
path = os.path.normpath(os.path.abspath(path))
base = 'file:'
if authority is not None:
base += '//' + authority
url = urljoin(base, pathname2url(path))
return url
Loading

0 comments on commit 402240c

Please sign in to comment.