aboutsummaryrefslogtreecommitdiff
path: root/setuptools/command
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools/command')
-rw-r--r--setuptools/command/__init__.py10
-rw-r--r--[-rwxr-xr-x]setuptools/command/alias.py2
-rw-r--r--setuptools/command/bdist_egg.py71
-rw-r--r--[-rwxr-xr-x]setuptools/command/bdist_rpm.py23
-rwxr-xr-xsetuptools/command/bdist_wininst.py21
-rw-r--r--setuptools/command/build_clib.py61
-rw-r--r--setuptools/command/build_ext.py47
-rw-r--r--setuptools/command/build_py.py84
-rw-r--r--[-rwxr-xr-x]setuptools/command/develop.py65
-rw-r--r--[-rwxr-xr-x]setuptools/command/easy_install.py626
-rw-r--r--[-rwxr-xr-x]setuptools/command/egg_info.py293
-rw-r--r--setuptools/command/install.py9
-rw-r--r--[-rwxr-xr-x]setuptools/command/install_egg_info.py3
-rw-r--r--setuptools/command/install_lib.py7
-rw-r--r--[-rwxr-xr-x]setuptools/command/install_scripts.py11
-rw-r--r--setuptools/command/py36compat.py4
-rw-r--r--[-rwxr-xr-x]setuptools/command/register.py16
-rw-r--r--[-rwxr-xr-x]setuptools/command/rotate.py8
-rw-r--r--[-rwxr-xr-x]setuptools/command/saveopts.py0
-rw-r--r--[-rwxr-xr-x]setuptools/command/sdist.py84
-rw-r--r--[-rwxr-xr-x]setuptools/command/setopt.py4
-rw-r--r--setuptools/command/test.py95
-rw-r--r--setuptools/command/upload.py45
-rw-r--r--setuptools/command/upload_docs.py46
24 files changed, 755 insertions, 880 deletions
diff --git a/setuptools/command/__init__.py b/setuptools/command/__init__.py
index fe619e2..b966dce 100644
--- a/setuptools/command/__init__.py
+++ b/setuptools/command/__init__.py
@@ -1,16 +1,6 @@
-__all__ = [
- 'alias', 'bdist_egg', 'bdist_rpm', 'build_ext', 'build_py', 'develop',
- 'easy_install', 'egg_info', 'install', 'install_lib', 'rotate', 'saveopts',
- 'sdist', 'setopt', 'test', 'install_egg_info', 'install_scripts',
- 'register', 'bdist_wininst', 'upload_docs', 'upload', 'build_clib',
- 'dist_info',
-]
-
from distutils.command.bdist import bdist
import sys
-from setuptools.command import install_scripts
-
if 'egg' not in bdist.format_commands:
bdist.format_command['egg'] = ('bdist_egg', "Python .egg file")
bdist.format_commands.append('egg')
diff --git a/setuptools/command/alias.py b/setuptools/command/alias.py
index 4532b1c..452a924 100755..100644
--- a/setuptools/command/alias.py
+++ b/setuptools/command/alias.py
@@ -1,7 +1,5 @@
from distutils.errors import DistutilsOptionError
-from setuptools.extern.six.moves import map
-
from setuptools.command.setopt import edit_config, option_base, config_file
diff --git a/setuptools/command/bdist_egg.py b/setuptools/command/bdist_egg.py
index 423b818..11a1c6b 100644
--- a/setuptools/command/bdist_egg.py
+++ b/setuptools/command/bdist_egg.py
@@ -2,7 +2,6 @@
Build .egg distributions"""
-from distutils.errors import DistutilsSetupError
from distutils.dir_util import remove_tree, mkpath
from distutils import log
from types import CodeType
@@ -12,24 +11,16 @@ import re
import textwrap
import marshal
-from setuptools.extern import six
-
-from pkg_resources import get_build_platform, Distribution, ensure_directory
-from pkg_resources import EntryPoint
+from pkg_resources import get_build_platform, Distribution
from setuptools.extension import Library
from setuptools import Command
+from .._path import ensure_directory
-try:
- # Python 2.7 or >=3.2
- from sysconfig import get_path, get_python_version
+from sysconfig import get_path, get_python_version
- def _get_purelib():
- return get_path("purelib")
-except ImportError:
- from distutils.sysconfig import get_python_lib, get_python_version
- def _get_purelib():
- return get_python_lib(False)
+def _get_purelib():
+ return get_path("purelib")
def strip_module(filename):
@@ -54,10 +45,12 @@ def write_stub(resource, pyfile):
_stub_template = textwrap.dedent("""
def __bootstrap__():
global __bootstrap__, __loader__, __file__
- import sys, pkg_resources, imp
+ import sys, pkg_resources, importlib.util
__file__ = pkg_resources.resource_filename(__name__, %r)
__loader__ = None; del __bootstrap__, __loader__
- imp.load_dynamic(__name__,__file__)
+ spec = importlib.util.spec_from_file_location(__name__,__file__)
+ mod = importlib.util.module_from_spec(spec)
+ spec.loader.exec_module(mod)
__bootstrap__()
""").lstrip()
with open(pyfile, 'w') as f:
@@ -158,7 +151,7 @@ class bdist_egg(Command):
self.run_command(cmdname)
return cmd
- def run(self):
+ def run(self): # noqa: C901 # is too complex (14) # FIXME
# Generate metadata first
self.run_command("egg_info")
# We run install_lib before install_data, because some data hacks
@@ -273,43 +266,7 @@ class bdist_egg(Command):
return analyze_egg(self.bdist_dir, self.stubs)
def gen_header(self):
- epm = EntryPoint.parse_map(self.distribution.entry_points or '')
- ep = epm.get('setuptools.installation', {}).get('eggsecutable')
- if ep is None:
- return 'w' # not an eggsecutable, do it the usual way.
-
- if not ep.attrs or ep.extras:
- raise DistutilsSetupError(
- "eggsecutable entry point (%r) cannot have 'extras' "
- "or refer to a module" % (ep,)
- )
-
- pyver = sys.version[:3]
- pkg = ep.module_name
- full = '.'.join(ep.attrs)
- base = ep.attrs[0]
- basename = os.path.basename(self.egg_output)
-
- header = (
- "#!/bin/sh\n"
- 'if [ `basename $0` = "%(basename)s" ]\n'
- 'then exec python%(pyver)s -c "'
- "import sys, os; sys.path.insert(0, os.path.abspath('$0')); "
- "from %(pkg)s import %(base)s; sys.exit(%(full)s())"
- '" "$@"\n'
- 'else\n'
- ' echo $0 is not the correct name for this egg file.\n'
- ' echo Please rename it back to %(basename)s and try again.\n'
- ' exec false\n'
- 'fi\n'
- ) % locals()
-
- if not self.dry_run:
- mkpath(os.path.dirname(self.egg_output), dry_run=self.dry_run)
- f = open(self.egg_output, 'w')
- f.write(header)
- f.close()
- return 'a'
+ return 'w'
def copy_metadata_to(self, target_dir):
"Copy metadata (egg info) to the target_dir"
@@ -411,9 +368,7 @@ def scan_module(egg_dir, base, name, stubs):
return True # Extension module
pkg = base[len(egg_dir) + 1:].replace(os.sep, '.')
module = pkg + (pkg and '.' or '') + os.path.splitext(name)[0]
- if sys.version_info < (3, 3):
- skip = 8 # skip magic & date
- elif sys.version_info < (3, 7):
+ if sys.version_info < (3, 7):
skip = 12 # skip magic & date & file size
else:
skip = 16 # skip magic & reserved? & date & file size
@@ -444,7 +399,7 @@ def iter_symbols(code):
for name in code.co_names:
yield name
for const in code.co_consts:
- if isinstance(const, six.string_types):
+ if isinstance(const, str):
yield const
elif isinstance(const, CodeType):
for name in iter_symbols(const):
diff --git a/setuptools/command/bdist_rpm.py b/setuptools/command/bdist_rpm.py
index 7073092..98bf5de 100755..100644
--- a/setuptools/command/bdist_rpm.py
+++ b/setuptools/command/bdist_rpm.py
@@ -1,4 +1,7 @@
import distutils.command.bdist_rpm as orig
+import warnings
+
+from setuptools import SetuptoolsDeprecationWarning
class bdist_rpm(orig.bdist_rpm):
@@ -8,36 +11,30 @@ class bdist_rpm(orig.bdist_rpm):
1. Run egg_info to ensure the name and version are properly calculated.
2. Always run 'install' using --single-version-externally-managed to
disable eggs in RPM distributions.
- 3. Replace dash with underscore in the version numbers for better RPM
- compatibility.
"""
def run(self):
+ warnings.warn(
+ "bdist_rpm is deprecated and will be removed in a future "
+ "version. Use bdist_wheel (wheel packages) instead.",
+ SetuptoolsDeprecationWarning,
+ )
+
# ensure distro name is up-to-date
self.run_command('egg_info')
orig.bdist_rpm.run(self)
def _make_spec_file(self):
- version = self.distribution.get_version()
- rpmversion = version.replace('-', '_')
spec = orig.bdist_rpm._make_spec_file(self)
- line23 = '%define version ' + version
- line24 = '%define version ' + rpmversion
spec = [
line.replace(
- "Source0: %{name}-%{version}.tar",
- "Source0: %{name}-%{unmangled_version}.tar"
- ).replace(
"setup.py install ",
"setup.py install --single-version-externally-managed "
).replace(
"%setup",
"%setup -n %{name}-%{unmangled_version}"
- ).replace(line23, line24)
+ )
for line in spec
]
- insert_loc = spec.index(line24) + 1
- unmangled_version = "%define unmangled_version " + version
- spec.insert(insert_loc, unmangled_version)
return spec
diff --git a/setuptools/command/bdist_wininst.py b/setuptools/command/bdist_wininst.py
deleted file mode 100755
index 073de97..0000000
--- a/setuptools/command/bdist_wininst.py
+++ /dev/null
@@ -1,21 +0,0 @@
-import distutils.command.bdist_wininst as orig
-
-
-class bdist_wininst(orig.bdist_wininst):
- def reinitialize_command(self, command, reinit_subcommands=0):
- """
- Supplement reinitialize_command to work around
- http://bugs.python.org/issue20819
- """
- cmd = self.distribution.reinitialize_command(
- command, reinit_subcommands)
- if command in ('install', 'install_lib'):
- cmd.install_lib = None
- return cmd
-
- def run(self):
- self._is_running = True
- try:
- orig.bdist_wininst.run(self)
- finally:
- self._is_running = False
diff --git a/setuptools/command/build_clib.py b/setuptools/command/build_clib.py
index 09caff6..67ce244 100644
--- a/setuptools/command/build_clib.py
+++ b/setuptools/command/build_clib.py
@@ -25,9 +25,9 @@ class build_clib(orig.build_clib):
sources = build_info.get('sources')
if sources is None or not isinstance(sources, (list, tuple)):
raise DistutilsSetupError(
- "in 'libraries' option (library '%s'), "
- "'sources' must be present and must be "
- "a list of source filenames" % lib_name)
+ "in 'libraries' option (library '%s'), "
+ "'sources' must be present and must be "
+ "a list of source filenames" % lib_name)
sources = list(sources)
log.info("building '%s' library", lib_name)
@@ -38,9 +38,9 @@ class build_clib(orig.build_clib):
obj_deps = build_info.get('obj_deps', dict())
if not isinstance(obj_deps, dict):
raise DistutilsSetupError(
- "in 'libraries' option (library '%s'), "
- "'obj_deps' must be a dictionary of "
- "type 'source: list'" % lib_name)
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
dependencies = []
# Get the global dependencies that are specified by the '' key.
@@ -48,9 +48,9 @@ class build_clib(orig.build_clib):
global_deps = obj_deps.get('', list())
if not isinstance(global_deps, (list, tuple)):
raise DistutilsSetupError(
- "in 'libraries' option (library '%s'), "
- "'obj_deps' must be a dictionary of "
- "type 'source: list'" % lib_name)
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
# Build the list to be used by newer_pairwise_group
# each source will be auto-added to its dependencies.
@@ -60,39 +60,42 @@ class build_clib(orig.build_clib):
extra_deps = obj_deps.get(source, list())
if not isinstance(extra_deps, (list, tuple)):
raise DistutilsSetupError(
- "in 'libraries' option (library '%s'), "
- "'obj_deps' must be a dictionary of "
- "type 'source: list'" % lib_name)
+ "in 'libraries' option (library '%s'), "
+ "'obj_deps' must be a dictionary of "
+ "type 'source: list'" % lib_name)
src_deps.extend(extra_deps)
dependencies.append(src_deps)
expected_objects = self.compiler.object_filenames(
- sources,
- output_dir=self.build_temp
- )
+ sources,
+ output_dir=self.build_temp,
+ )
- if newer_pairwise_group(dependencies, expected_objects) != ([], []):
+ if (
+ newer_pairwise_group(dependencies, expected_objects)
+ != ([], [])
+ ):
# First, compile the source code to object files in the library
# directory. (This should probably change to putting object
# files in a temporary build directory.)
macros = build_info.get('macros')
include_dirs = build_info.get('include_dirs')
cflags = build_info.get('cflags')
- objects = self.compiler.compile(
- sources,
- output_dir=self.build_temp,
- macros=macros,
- include_dirs=include_dirs,
- extra_postargs=cflags,
- debug=self.debug
- )
+ self.compiler.compile(
+ sources,
+ output_dir=self.build_temp,
+ macros=macros,
+ include_dirs=include_dirs,
+ extra_postargs=cflags,
+ debug=self.debug
+ )
# Now "link" the object files together into a static library.
# (On Unix at least, this isn't really linking -- it just
# builds an archive. Whatever.)
self.compiler.create_static_lib(
- expected_objects,
- lib_name,
- output_dir=self.build_clib,
- debug=self.debug
- )
+ expected_objects,
+ lib_name,
+ output_dir=self.build_clib,
+ debug=self.debug
+ )
diff --git a/setuptools/command/build_ext.py b/setuptools/command/build_ext.py
index ea97b37..c59eff8 100644
--- a/setuptools/command/build_ext.py
+++ b/setuptools/command/build_ext.py
@@ -1,7 +1,7 @@
import os
import sys
import itertools
-import imp
+from importlib.machinery import EXTENSION_SUFFIXES
from distutils.command.build_ext import build_ext as _du_build_ext
from distutils.file_util import copy_file
from distutils.ccompiler import new_compiler
@@ -10,7 +10,6 @@ from distutils.errors import DistutilsError
from distutils import log
from setuptools.extension import Library
-from setuptools.extern import six
try:
# Attempt to use Cython for building extensions, if available
@@ -23,7 +22,7 @@ except ImportError:
# make sure _config_vars is initialized
get_config_var("LDSHARED")
-from distutils.sysconfig import _config_vars as _CONFIG_VARS
+from distutils.sysconfig import _config_vars as _CONFIG_VARS # noqa
def _customize_compiler_for_shlib(compiler):
@@ -59,12 +58,14 @@ elif os.name != 'nt':
except ImportError:
pass
-if_dl = lambda s: s if have_rtld else ''
+
+def if_dl(s):
+ return s if have_rtld else ''
def get_abi3_suffix():
"""Return the file extension for an abi3-compliant Extension()"""
- for suffix, _, _ in (s for s in imp.get_suffixes() if s[2] == imp.C_EXTENSION):
+ for suffix in EXTENSION_SUFFIXES:
if '.abi3' in suffix: # Unix
return suffix
elif suffix == '.pyd': # Windows
@@ -103,18 +104,20 @@ class build_ext(_build_ext):
self.write_stub(package_dir or os.curdir, ext, True)
def get_ext_filename(self, fullname):
- filename = _build_ext.get_ext_filename(self, fullname)
+ so_ext = os.getenv('SETUPTOOLS_EXT_SUFFIX')
+ if so_ext:
+ filename = os.path.join(*fullname.split('.')) + so_ext
+ else:
+ filename = _build_ext.get_ext_filename(self, fullname)
+ so_ext = get_config_var('EXT_SUFFIX')
+
if fullname in self.ext_map:
ext = self.ext_map[fullname]
- use_abi3 = (
- six.PY3
- and getattr(ext, 'py_limited_api')
- and get_abi3_suffix()
- )
+ use_abi3 = getattr(ext, 'py_limited_api') and get_abi3_suffix()
if use_abi3:
- so_ext = _get_config_var_837('EXT_SUFFIX')
filename = filename[:-len(so_ext)]
- filename = filename + get_abi3_suffix()
+ so_ext = get_abi3_suffix()
+ filename = filename + so_ext
if isinstance(ext, Library):
fn, ext = os.path.splitext(filename)
return self.shlib_compiler.library_filename(fn, libtype)
@@ -245,7 +248,8 @@ class build_ext(_build_ext):
'\n'.join([
"def __bootstrap__():",
" global __bootstrap__, __file__, __loader__",
- " import sys, os, pkg_resources, imp" + if_dl(", dl"),
+ " import sys, os, pkg_resources, importlib.util" +
+ if_dl(", dl"),
" __file__ = pkg_resources.resource_filename"
"(__name__,%r)"
% os.path.basename(ext._file_name),
@@ -257,7 +261,10 @@ class build_ext(_build_ext):
" try:",
" os.chdir(os.path.dirname(__file__))",
if_dl(" sys.setdlopenflags(dl.RTLD_NOW)"),
- " imp.load_dynamic(__name__,__file__)",
+ " spec = importlib.util.spec_from_file_location(",
+ " __name__, __file__)",
+ " mod = importlib.util.module_from_spec(spec)",
+ " spec.loader.exec_module(mod)",
" finally:",
if_dl(" sys.setdlopenflags(old_flags)"),
" os.chdir(old_dir)",
@@ -319,13 +326,3 @@ else:
self.create_static_lib(
objects, basename, output_dir, debug, target_lang
)
-
-
-def _get_config_var_837(name):
- """
- In https://github.com/pypa/setuptools/pull/837, we discovered
- Python 3.3.0 exposes the extension suffix under the name 'SO'.
- """
- if sys.version_info < (3, 3, 1):
- name = 'SO'
- return get_config_var(name)
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index b0314fd..c3fdc09 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -7,20 +7,15 @@ import textwrap
import io
import distutils.errors
import itertools
+import stat
+from setuptools.extern.more_itertools import unique_everseen
-from setuptools.extern import six
-from setuptools.extern.six.moves import map, filter, filterfalse
-try:
- from setuptools.lib2to3_ex import Mixin2to3
-except ImportError:
+def make_writable(target):
+ os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE)
- class Mixin2to3:
- def run_2to3(self, files, doctests=True):
- "do nothing"
-
-class build_py(orig.build_py, Mixin2to3):
+class build_py(orig.build_py):
"""Enhanced 'build_py' command that includes data files with packages
The data files are specified via a 'package_data' argument to 'setup()'.
@@ -33,12 +28,10 @@ class build_py(orig.build_py, Mixin2to3):
def finalize_options(self):
orig.build_py.finalize_options(self)
self.package_data = self.distribution.package_data
- self.exclude_package_data = (self.distribution.exclude_package_data or
- {})
+ self.exclude_package_data = self.distribution.exclude_package_data or {}
if 'data_files' in self.__dict__:
del self.__dict__['data_files']
self.__updated_files = []
- self.__doctests_2to3 = []
def run(self):
"""Build modules, packages, and copy data files to build directory"""
@@ -52,10 +45,6 @@ class build_py(orig.build_py, Mixin2to3):
self.build_packages()
self.build_package_data()
- self.run_2to3(self.__updated_files, False)
- self.run_2to3(self.__updated_files, True)
- self.run_2to3(self.__doctests_2to3, True)
-
# Only compile actual .py files, using our base class' idea of what our
# output files are.
self.byte_compile(orig.build_py.get_outputs(self, include_bytecode=0))
@@ -68,11 +57,7 @@ class build_py(orig.build_py, Mixin2to3):
return orig.build_py.__getattr__(self, attr)
def build_module(self, module, module_file, package):
- if six.PY2 and isinstance(package, six.string_types):
- # avoid errors on Python 2 when unicode is passed (#190)
- package = package.split('.')
- outfile, copied = orig.build_py.build_module(self, module, module_file,
- package)
+ outfile, copied = orig.build_py.build_module(self, module, module_file, package)
if copied:
self.__updated_files.append(outfile)
return outfile, copied
@@ -82,6 +67,16 @@ class build_py(orig.build_py, Mixin2to3):
self.analyze_manifest()
return list(map(self._get_pkg_data_files, self.packages or ()))
+ def get_data_files_without_manifest(self):
+ """
+ Generate list of ``(package,src_dir,build_dir,filenames)`` tuples,
+ but without triggering any attempt to analyze or build the manifest.
+ """
+ # Prevent eventual errors from unset `manifest_files`
+ # (that would otherwise be set by `analyze_manifest`)
+ self.__dict__.setdefault('manifest_files', {})
+ return list(map(self._get_pkg_data_files, self.packages or ()))
+
def _get_pkg_data_files(self, package):
# Locate package source directory
src_dir = self.get_package_dir(package)
@@ -121,10 +116,8 @@ class build_py(orig.build_py, Mixin2to3):
self.mkpath(os.path.dirname(target))
srcfile = os.path.join(src_dir, filename)
outf, copied = self.copy_file(srcfile, target)
+ make_writable(target)
srcfile = os.path.abspath(srcfile)
- if (copied and
- srcfile in self.distribution.convert_2to3_doctests):
- self.__doctests_2to3.append(outf)
def analyze_manifest(self):
self.manifest_files = mf = {}
@@ -201,20 +194,13 @@ class build_py(orig.build_py, Mixin2to3):
package,
src_dir,
)
- match_groups = (
- fnmatch.filter(files, pattern)
- for pattern in patterns
- )
+ match_groups = (fnmatch.filter(files, pattern) for pattern in patterns)
# flatten the groups of matches into an iterable of matches
matches = itertools.chain.from_iterable(match_groups)
bad = set(matches)
- keepers = (
- fn
- for fn in files
- if fn not in bad
- )
+ keepers = (fn for fn in files if fn not in bad)
# ditch dupes
- return list(_unique_everseen(keepers))
+ return list(unique_everseen(keepers))
@staticmethod
def _get_platform_patterns(spec, package, src_dir):
@@ -235,36 +221,22 @@ class build_py(orig.build_py, Mixin2to3):
)
-# from Python docs
-def _unique_everseen(iterable, key=None):
- "List unique elements, preserving order. Remember all elements ever seen."
- # unique_everseen('AAAABBBCCDAABBB') --> A B C D
- # unique_everseen('ABBCcAD', str.lower) --> A B C D
- seen = set()
- seen_add = seen.add
- if key is None:
- for element in filterfalse(seen.__contains__, iterable):
- seen_add(element)
- yield element
- else:
- for element in iterable:
- k = key(element)
- if k not in seen:
- seen_add(k)
- yield element
-
-
def assert_relative(path):
if not os.path.isabs(path):
return path
from distutils.errors import DistutilsSetupError
- msg = textwrap.dedent("""
+ msg = (
+ textwrap.dedent(
+ """
Error: setup script specifies an absolute path:
%s
setup() arguments must *always* be /-separated paths relative to the
setup.py directory, *never* absolute paths.
- """).lstrip() % path
+ """
+ ).lstrip()
+ % path
+ )
raise DistutilsSetupError(msg)
diff --git a/setuptools/command/develop.py b/setuptools/command/develop.py
index 959c932..24fb0a7 100755..100644
--- a/setuptools/command/develop.py
+++ b/setuptools/command/develop.py
@@ -5,9 +5,7 @@ import os
import glob
import io
-from setuptools.extern import six
-
-from pkg_resources import Distribution, PathMetadata, normalize_path
+import pkg_resources
from setuptools.command.easy_install import easy_install
from setuptools import namespaces
import setuptools
@@ -63,9 +61,10 @@ class develop(namespaces.DevelopInstaller, easy_install):
if self.egg_path is None:
self.egg_path = os.path.abspath(ei.egg_base)
- target = normalize_path(self.egg_base)
- egg_path = normalize_path(os.path.join(self.install_dir,
- self.egg_path))
+ target = pkg_resources.normalize_path(self.egg_base)
+ egg_path = pkg_resources.normalize_path(
+ os.path.join(self.install_dir, self.egg_path)
+ )
if egg_path != target:
raise DistutilsOptionError(
"--egg-path must be a relative path from the install"
@@ -73,10 +72,10 @@ class develop(namespaces.DevelopInstaller, easy_install):
)
# Make a distribution for the package's source
- self.dist = Distribution(
+ self.dist = pkg_resources.Distribution(
target,
- PathMetadata(target, os.path.abspath(ei.egg_info)),
- project_name=ei.egg_name
+ pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)),
+ project_name=ei.egg_name,
)
self.setup_path = self._resolve_setup_path(
@@ -95,47 +94,25 @@ class develop(namespaces.DevelopInstaller, easy_install):
path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
if path_to_setup != os.curdir:
path_to_setup = '../' * (path_to_setup.count('/') + 1)
- resolved = normalize_path(
+ resolved = pkg_resources.normalize_path(
os.path.join(install_dir, egg_path, path_to_setup)
)
- if resolved != normalize_path(os.curdir):
+ if resolved != pkg_resources.normalize_path(os.curdir):
raise DistutilsOptionError(
"Can't get a consistent path to setup script from"
- " installation directory", resolved, normalize_path(os.curdir))
+ " installation directory",
+ resolved,
+ pkg_resources.normalize_path(os.curdir),
+ )
return path_to_setup
def install_for_development(self):
- if six.PY3 and getattr(self.distribution, 'use_2to3', False):
- # If we run 2to3 we can not do this inplace:
-
- # Ensure metadata is up-to-date
- self.reinitialize_command('build_py', inplace=0)
- self.run_command('build_py')
- bpy_cmd = self.get_finalized_command("build_py")
- build_path = normalize_path(bpy_cmd.build_lib)
-
- # Build extensions
- self.reinitialize_command('egg_info', egg_base=build_path)
- self.run_command('egg_info')
-
- self.reinitialize_command('build_ext', inplace=0)
- self.run_command('build_ext')
-
- # Fixup egg-link and easy-install.pth
- ei_cmd = self.get_finalized_command("egg_info")
- self.egg_path = build_path
- self.dist.location = build_path
- # XXX
- self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info)
- else:
- # Without 2to3 inplace works fine:
- self.run_command('egg_info')
+ self.run_command('egg_info')
- # Build extensions in-place
- self.reinitialize_command('build_ext', inplace=1)
- self.run_command('build_ext')
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
- self.install_site_py() # ensure that target dir is site-safe
if setuptools.bootstrap_install_from:
self.easy_install(setuptools.bootstrap_install_from)
setuptools.bootstrap_install_from = None
@@ -157,8 +134,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
egg_link_file = open(self.egg_link)
contents = [line.rstrip() for line in egg_link_file]
egg_link_file.close()
- if contents not in ([self.egg_path],
- [self.egg_path, self.setup_path]):
+ if contents not in ([self.egg_path], [self.egg_path, self.setup_path]):
log.warn("Link points to %s: uninstall aborted", contents)
return
if not self.dry_run:
@@ -192,12 +168,13 @@ class develop(namespaces.DevelopInstaller, easy_install):
return easy_install.install_wrapper_scripts(self, dist)
-class VersionlessRequirement(object):
+class VersionlessRequirement:
"""
Adapt a pkg_resources.Distribution to simply return the project
name as the 'requirement' so that scripts will work across
multiple versions.
+ >>> from pkg_resources import Distribution
>>> dist = Distribution(project_name='foo', version='1.0')
>>> str(dist.as_requirement())
'foo==1.0'
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index 85ee40f..107850a 100755..100644
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -1,4 +1,3 @@
-#!/usr/bin/env python
"""
Easy Install
------------
@@ -7,7 +6,7 @@ A tool for doing automatic download/extract/build of distutils-based Python
packages. For detailed documentation, see the accompanying EasyInstall.txt
file, or visit the `EasyInstall home page`__.
-__ https://setuptools.readthedocs.io/en/latest/easy_install.html
+__ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
"""
@@ -18,10 +17,10 @@ from distutils.errors import (
DistutilsArgError, DistutilsOptionError,
DistutilsError, DistutilsPlatformError,
)
-from distutils.command.install import INSTALL_SCHEMES, SCHEME_KEYS
from distutils import log, dir_util
from distutils.command.build_scripts import first_line_re
from distutils.spawn import find_executable
+from distutils.command import install
import sys
import os
import zipimport
@@ -39,14 +38,16 @@ import contextlib
import subprocess
import shlex
import io
+import configparser
+import sysconfig
-from setuptools.extern import six
-from setuptools.extern.six.moves import configparser, map
+
+from sysconfig import get_path
+
+from setuptools import SetuptoolsDeprecationWarning
from setuptools import Command
from setuptools.sandbox import run_setup
-from setuptools.py31compat import get_path, get_config_vars
-from setuptools.py27compat import rmtree_safe
from setuptools.command import setopt
from setuptools.archive_util import unpack_archive
from setuptools.package_index import (
@@ -55,19 +56,22 @@ from setuptools.package_index import (
from setuptools.command import bdist_egg, egg_info
from setuptools.wheel import Wheel
from pkg_resources import (
- yield_lines, normalize_path, resource_string, ensure_directory,
+ normalize_path, resource_string,
get_distribution, find_distributions, Environment, Requirement,
Distribution, PathMetadata, EggMetadata, WorkingSet, DistributionNotFound,
VersionConflict, DEVELOP_DIST,
)
-import pkg_resources.py31compat
+import pkg_resources
+from .._path import ensure_directory
+from ..extern.jaraco.text import yield_lines
+
# Turn on PEP440Warnings
warnings.filterwarnings("default", category=pkg_resources.PEP440Warning)
__all__ = [
- 'samefile', 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
- 'main', 'get_exe_prefixes',
+ 'easy_install', 'PthDistributions', 'extract_wininst_cfg',
+ 'get_exe_prefixes',
]
@@ -75,47 +79,20 @@ def is_64bit():
return struct.calcsize("P") == 8
-def samefile(p1, p2):
- """
- Determine if two paths reference the same file.
-
- Augments os.path.samefile to work on Windows and
- suppresses errors if the path doesn't exist.
- """
- both_exist = os.path.exists(p1) and os.path.exists(p2)
- use_samefile = hasattr(os.path, 'samefile') and both_exist
- if use_samefile:
- return os.path.samefile(p1, p2)
- norm_p1 = os.path.normpath(os.path.normcase(p1))
- norm_p2 = os.path.normpath(os.path.normcase(p2))
- return norm_p1 == norm_p2
-
-
-if six.PY2:
-
- def _to_ascii(s):
- return s
+def _to_bytes(s):
+ return s.encode('utf8')
- def isascii(s):
- try:
- six.text_type(s, 'ascii')
- return True
- except UnicodeError:
- return False
-else:
- def _to_ascii(s):
- return s.encode('ascii')
-
- def isascii(s):
- try:
- s.encode('ascii')
- return True
- except UnicodeError:
- return False
+def isascii(s):
+ try:
+ s.encode('ascii')
+ return True
+ except UnicodeError:
+ return False
-_one_liner = lambda text: textwrap.dedent(text).strip().replace('\n', '; ')
+def _one_liner(text):
+ return textwrap.dedent(text).strip().replace('\n', '; ')
class easy_install(Command):
@@ -150,23 +127,26 @@ class easy_install(Command):
"allow building eggs from local checkouts"),
('version', None, "print version information and exit"),
('no-find-links', None,
- "Don't load find-links defined in packages being installed")
+ "Don't load find-links defined in packages being installed"),
+ ('user', None, "install in user site-package '%s'" % site.USER_SITE)
]
boolean_options = [
'zip-ok', 'multi-version', 'exclude-scripts', 'upgrade', 'always-copy',
'editable',
- 'no-deps', 'local-snapshots-ok', 'version'
+ 'no-deps', 'local-snapshots-ok', 'version',
+ 'user'
]
- if site.ENABLE_USER_SITE:
- help_msg = "install in user site-package '%s'" % site.USER_SITE
- user_options.append(('user', None, help_msg))
- boolean_options.append('user')
-
negative_opt = {'always-unzip': 'zip-ok'}
create_index = PackageIndex
def initialize_options(self):
+ warnings.warn(
+ "easy_install command is deprecated. "
+ "Use build and pip and other standards-based tools.",
+ EasyInstallDeprecationWarning,
+ )
+
# the --user option seems to be an opt-in one,
# so the default should be False.
self.user = 0
@@ -202,7 +182,6 @@ class easy_install(Command):
self.pth_file = self.always_copy_from = None
self.site_dirs = None
self.installed_projects = {}
- self.sitepy_installed = False
# Always read easy_install options, even if we are subclassed, or have
# an independent instance created. This ensures that defaults will
# always come from the standard configuration file(s)' "easy_install"
@@ -235,37 +214,52 @@ class easy_install(Command):
"""
Render the Setuptools version and installation details, then exit.
"""
- ver = sys.version[:3]
+ ver = '{}.{}'.format(*sys.version_info)
dist = get_distribution('setuptools')
tmpl = 'setuptools {dist.version} from {dist.location} (Python {ver})'
print(tmpl.format(**locals()))
raise SystemExit()
- def finalize_options(self):
+ def finalize_options(self): # noqa: C901 # is too complex (25) # FIXME
self.version and self._render_version()
py_version = sys.version.split()[0]
- prefix, exec_prefix = get_config_vars('prefix', 'exec_prefix')
- self.config_vars = {
+ self.config_vars = dict(sysconfig.get_config_vars())
+
+ self.config_vars.update({
'dist_name': self.distribution.get_name(),
'dist_version': self.distribution.get_version(),
'dist_fullname': self.distribution.get_fullname(),
'py_version': py_version,
- 'py_version_short': py_version[0:3],
- 'py_version_nodot': py_version[0] + py_version[2],
- 'sys_prefix': prefix,
- 'prefix': prefix,
- 'sys_exec_prefix': exec_prefix,
- 'exec_prefix': exec_prefix,
+ 'py_version_short': f'{sys.version_info.major}.{sys.version_info.minor}',
+ 'py_version_nodot': f'{sys.version_info.major}{sys.version_info.minor}',
+ 'sys_prefix': self.config_vars['prefix'],
+ 'sys_exec_prefix': self.config_vars['exec_prefix'],
# Only python 3.2+ has abiflags
'abiflags': getattr(sys, 'abiflags', ''),
- }
+ 'platlibdir': getattr(sys, 'platlibdir', 'lib'),
+ })
+ with contextlib.suppress(AttributeError):
+ # only for distutils outside stdlib
+ self.config_vars.update({
+ 'implementation_lower': install._get_implementation().lower(),
+ 'implementation': install._get_implementation(),
+ })
+
+ # pypa/distutils#113 Python 3.9 compat
+ self.config_vars.setdefault(
+ 'py_version_nodot_plat',
+ getattr(sys, 'windir', '').replace('.', ''),
+ )
if site.ENABLE_USER_SITE:
self.config_vars['userbase'] = self.install_userbase
self.config_vars['usersite'] = self.install_usersite
+ elif self.user:
+ log.warn("WARNING: The user site-packages directory is disabled.")
+
self._fix_install_dir_for_user_site()
self.expand_basedirs()
@@ -299,24 +293,9 @@ class easy_install(Command):
self.script_dir = self.install_scripts
# default --record from the install command
self.set_undefined_options('install', ('record', 'record'))
- # Should this be moved to the if statement below? It's not used
- # elsewhere
- normpath = map(normalize_path, sys.path)
self.all_site_dirs = get_site_dirs()
- if self.site_dirs is not None:
- site_dirs = [
- os.path.expanduser(s.strip()) for s in
- self.site_dirs.split(',')
- ]
- for d in site_dirs:
- if not os.path.isdir(d):
- log.warn("%s (in --site-dirs) does not exist", d)
- elif normalize_path(d) not in normpath:
- raise DistutilsOptionError(
- d + " (in --site-dirs) is not on sys.path"
- )
- else:
- self.all_site_dirs.append(normalize_path(d))
+ self.all_site_dirs.extend(self._process_site_dirs(self.site_dirs))
+
if not self.editable:
self.check_site_dir()
self.index_url = self.index_url or "https://pypi.org/simple/"
@@ -336,7 +315,7 @@ class easy_install(Command):
self.local_index = Environment(self.shadow_path + sys.path)
if self.find_links is not None:
- if isinstance(self.find_links, six.string_types):
+ if isinstance(self.find_links, str):
self.find_links = self.find_links.split()
else:
self.find_links = []
@@ -345,13 +324,7 @@ class easy_install(Command):
if not self.no_find_links:
self.package_index.add_find_links(self.find_links)
self.set_undefined_options('install_lib', ('optimize', 'optimize'))
- if not isinstance(self.optimize, int):
- try:
- self.optimize = int(self.optimize)
- if not (0 <= self.optimize <= 2):
- raise ValueError
- except ValueError:
- raise DistutilsOptionError("--optimize must be 0, 1, or 2")
+ self.optimize = self._validate_optimize(self.optimize)
if self.editable and not self.build_directory:
raise DistutilsArgError(
@@ -363,6 +336,39 @@ class easy_install(Command):
self.outputs = []
+ @staticmethod
+ def _process_site_dirs(site_dirs):
+ if site_dirs is None:
+ return
+
+ normpath = map(normalize_path, sys.path)
+ site_dirs = [
+ os.path.expanduser(s.strip()) for s in
+ site_dirs.split(',')
+ ]
+ for d in site_dirs:
+ if not os.path.isdir(d):
+ log.warn("%s (in --site-dirs) does not exist", d)
+ elif normalize_path(d) not in normpath:
+ raise DistutilsOptionError(
+ d + " (in --site-dirs) is not on sys.path"
+ )
+ else:
+ yield normalize_path(d)
+
+ @staticmethod
+ def _validate_optimize(value):
+ try:
+ value = int(value)
+ if value not in range(3):
+ raise ValueError
+ except ValueError as e:
+ raise DistutilsOptionError(
+ "--optimize must be 0, 1, or 2"
+ ) from e
+
+ return value
+
def _fix_install_dir_for_user_site(self):
"""
Fix the install_dir if "--user" was used.
@@ -375,7 +381,7 @@ class easy_install(Command):
msg = "User base directory is not specified"
raise DistutilsPlatformError(msg)
self.install_base = self.install_platbase = self.install_userbase
- scheme_name = os.name.replace('posix', 'unix') + '_user'
+ scheme_name = f'{os.name}_user'
self.select_scheme(scheme_name)
def _expand_attrs(self, attrs):
@@ -404,7 +410,13 @@ class easy_install(Command):
]
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:
@@ -441,12 +453,18 @@ class easy_install(Command):
def warn_deprecated_options(self):
pass
- def check_site_dir(self):
+ def check_site_dir(self): # noqa: C901 # is too complex (12) # FIXME
"""Verify that self.install_dir is .pth-capable dir, if needed"""
instdir = normalize_path(self.install_dir)
pth_file = os.path.join(instdir, 'easy-install.pth')
+ if not os.path.exists(instdir):
+ try:
+ os.makedirs(instdir)
+ except (OSError, IOError):
+ self.cant_write_to_target()
+
# Is it a configured, PYTHONPATH, implicit, or explicit site dir?
is_site_dir = instdir in self.all_site_dirs
@@ -466,8 +484,9 @@ class easy_install(Command):
self.cant_write_to_target()
if not is_site_dir and not self.multi_version:
- # Can't install non-multi to non-site dir
- raise DistutilsError(self.no_default_version_msg())
+ # Can't install non-multi to non-site dir with easy_install
+ pythonpath = os.environ.get('PYTHONPATH', '')
+ log.warn(self.__no_default_msg, self.install_dir, pythonpath)
if is_site_dir:
if self.pth_file is None:
@@ -475,12 +494,8 @@ class easy_install(Command):
else:
self.pth_file = None
- if instdir not in map(normalize_path, _pythonpath()):
- # only PYTHONPATH dirs need a site.py, so pretend it's there
- self.sitepy_installed = True
- elif self.multi_version and not os.path.exists(pth_file):
- self.sitepy_installed = True # don't need site.py in this case
- self.pth_file = None # and don't create a .pth file
+ if self.multi_version and not os.path.exists(pth_file):
+ self.pth_file = None # don't create a .pth file
self.install_dir = instdir
__cant_write_msg = textwrap.dedent("""
@@ -495,13 +510,13 @@ class easy_install(Command):
the distutils default setting) was:
%s
- """).lstrip()
+ """).lstrip() # noqa
__not_exists_id = textwrap.dedent("""
This directory does not currently exist. Please create it and try again, or
choose a different installation directory (using the -d or --install-dir
option).
- """).lstrip()
+ """).lstrip() # noqa
__access_msg = textwrap.dedent("""
Perhaps your account does not have write access to this directory? If the
@@ -514,10 +529,10 @@ class easy_install(Command):
For information on other options, you may wish to consult the
documentation at:
- https://setuptools.readthedocs.io/en/latest/easy_install.html
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html
Please make the appropriate changes for your system and try again.
- """).lstrip()
+ """).lstrip() # noqa
def cant_write_to_target(self):
msg = self.__cant_write_msg % (sys.exc_info()[1], self.install_dir,)
@@ -545,7 +560,7 @@ class easy_install(Command):
if ok_exists:
os.unlink(ok_file)
dirname = os.path.dirname(ok_file)
- pkg_resources.py31compat.makedirs(dirname, exist_ok=True)
+ os.makedirs(dirname, exist_ok=True)
f = open(pth_file, 'w')
except (OSError, IOError):
self.cant_write_to_target()
@@ -629,17 +644,14 @@ class easy_install(Command):
@contextlib.contextmanager
def _tmpdir(self):
- tmpdir = tempfile.mkdtemp(prefix=six.u("easy_install-"))
+ tmpdir = tempfile.mkdtemp(prefix=u"easy_install-")
try:
# cast to str as workaround for #709 and #710 and #712
yield str(tmpdir)
finally:
- os.path.exists(tmpdir) and rmtree(rmtree_safe(tmpdir))
+ os.path.exists(tmpdir) and rmtree(tmpdir)
def easy_install(self, spec, deps=False):
- if not self.editable:
- self.install_site_py()
-
with self._tmpdir() as tmpdir:
if not isinstance(spec, Requirement):
if URL_SCHEME(spec):
@@ -709,15 +721,16 @@ class easy_install(Command):
return dist
def select_scheme(self, name):
- """Sets the install directories by applying the install schemes."""
- # it's the caller's problem if they supply a bad name!
- scheme = INSTALL_SCHEMES[name]
- for key in SCHEME_KEYS:
- attrname = 'install_' + key
- if getattr(self, attrname) is None:
- setattr(self, attrname, scheme[key])
-
- def process_distribution(self, requirement, dist, deps=True, *info):
+ try:
+ install._select_scheme(self, name)
+ except AttributeError:
+ # stdlib distutils
+ install.install.select_scheme(self, name.replace('posix', 'unix'))
+
+ # FIXME: 'easy_install.process_distribution' is too complex (12)
+ def process_distribution( # noqa: C901
+ self, requirement, dist, deps=True, *info,
+ ):
self.update_pth(dist)
self.package_index.add(dist)
if dist in self.local_index[dist.key]:
@@ -746,9 +759,9 @@ class easy_install(Command):
[requirement], self.local_index, self.easy_install
)
except DistributionNotFound as e:
- raise DistutilsError(str(e))
+ raise DistutilsError(str(e)) from e
except VersionConflict as e:
- raise DistutilsError(e.report())
+ raise DistutilsError(e.report()) from e
if self.always_copy or self.always_copy_from:
# Force all the relevant distros to be copied or activated
for dist in distros:
@@ -802,7 +815,7 @@ class easy_install(Command):
if is_script:
body = self._load_template(dev_path) % locals()
script_text = ScriptWriter.get_header(script_text) + body
- self.write_script(script_name, _to_ascii(script_text), 'b')
+ self.write_script(script_name, _to_bytes(script_text), 'b')
@staticmethod
def _load_template(dev_path):
@@ -841,12 +854,19 @@ class easy_install(Command):
def install_eggs(self, spec, dist_filename, tmpdir):
# .egg dirs or files are already built, so just return them
- if dist_filename.lower().endswith('.egg'):
- return [self.install_egg(dist_filename, tmpdir)]
- elif dist_filename.lower().endswith('.exe'):
- return [self.install_exe(dist_filename, tmpdir)]
- elif dist_filename.lower().endswith('.whl'):
- return [self.install_wheel(dist_filename, tmpdir)]
+ installer_map = {
+ '.egg': self.install_egg,
+ '.exe': self.install_exe,
+ '.whl': self.install_wheel,
+ }
+ try:
+ install_dist = installer_map[
+ dist_filename.lower()[-4:]
+ ]
+ except KeyError:
+ pass
+ else:
+ return [install_dist(dist_filename, tmpdir)]
# Anything else, try to extract and build
setup_base = tmpdir
@@ -891,7 +911,8 @@ class easy_install(Command):
metadata = EggMetadata(zipimport.zipimporter(egg_path))
return Distribution.from_filename(egg_path, metadata=metadata)
- def install_egg(self, egg_path, tmpdir):
+ # FIXME: 'easy_install.install_egg' is too complex (11)
+ def install_egg(self, egg_path, tmpdir): # noqa: C901
destination = os.path.join(
self.install_dir,
os.path.basename(egg_path),
@@ -901,7 +922,9 @@ class easy_install(Command):
ensure_directory(destination)
dist = self.egg_distribution(egg_path)
- if not samefile(egg_path, destination):
+ if not (
+ os.path.exists(destination) and os.path.samefile(egg_path, destination)
+ ):
if os.path.isdir(destination) and not os.path.islink(destination):
dir_util.remove_tree(destination, dry_run=self.dry_run)
elif os.path.exists(destination):
@@ -990,7 +1013,8 @@ class easy_install(Command):
# install the .egg
return self.install_egg(egg_path, tmpdir)
- def exe_to_egg(self, dist_filename, egg_tmp):
+ # FIXME: 'easy_install.exe_to_egg' is too complex (12)
+ def exe_to_egg(self, dist_filename, egg_tmp): # noqa: C901
"""Extract a bdist_wininst to the directories an egg would use"""
# Check for .pth file and set up prefix translations
prefixes = get_exe_prefixes(dist_filename)
@@ -1081,13 +1105,13 @@ class easy_install(Command):
pkg_resources.require("%(name)s") # latest installed version
pkg_resources.require("%(name)s==%(version)s") # this exact version
pkg_resources.require("%(name)s>=%(version)s") # this version or higher
- """).lstrip()
+ """).lstrip() # noqa
__id_warning = textwrap.dedent("""
Note also that the installation directory must be on sys.path at runtime for
this to work. (e.g. by being the application's script directory, by being on
PYTHONPATH, or by being added to sys.path by your code.)
- """)
+ """) # noqa
def installation_report(self, req, dist, what="Installed"):
"""Helpful installation message for display to package users"""
@@ -1112,7 +1136,7 @@ class easy_install(Command):
%(python)s setup.py develop
See the setuptools documentation for the "develop" command for more info.
- """).lstrip()
+ """).lstrip() # noqa
def report_editable(self, spec, setup_script):
dirname = os.path.dirname(setup_script)
@@ -1137,7 +1161,9 @@ class easy_install(Command):
try:
run_setup(setup_script, args)
except SystemExit as v:
- raise DistutilsError("Setup script exited with %s" % (v.args[0],))
+ raise DistutilsError(
+ "Setup script exited with %s" % (v.args[0],)
+ ) from v
def build_and_install(self, setup_script, setup_base):
args = ['bdist_egg', '--dist-dir']
@@ -1174,29 +1200,30 @@ class easy_install(Command):
# to the setup.cfg file.
ei_opts = self.distribution.get_option_dict('easy_install').copy()
fetch_directives = (
- 'find_links', 'site_dirs', 'index_url', 'optimize',
- 'site_dirs', 'allow_hosts',
+ 'find_links', 'site_dirs', 'index_url', 'optimize', 'allow_hosts',
)
fetch_options = {}
for key, val in ei_opts.items():
if key not in fetch_directives:
continue
- fetch_options[key.replace('_', '-')] = val[1]
+ fetch_options[key] = val[1]
# create a settings dictionary suitable for `edit_config`
settings = dict(easy_install=fetch_options)
cfg_filename = os.path.join(base, 'setup.cfg')
setopt.edit_config(cfg_filename, settings)
- def update_pth(self, dist):
+ def update_pth(self, dist): # noqa: C901 # is too complex (11) # FIXME
if self.pth_file is None:
return
for d in self.pth_file[dist.key]: # drop old entries
- if self.multi_version or d.location != dist.location:
- log.info("Removing %s from easy-install.pth file", d)
- self.pth_file.remove(d)
- if d.location in self.shadow_path:
- self.shadow_path.remove(d.location)
+ if not self.multi_version and d.location == dist.location:
+ continue
+
+ log.info("Removing %s from easy-install.pth file", d)
+ self.pth_file.remove(d)
+ if d.location in self.shadow_path:
+ self.shadow_path.remove(d.location)
if not self.multi_version:
if dist.location in self.pth_file.paths:
@@ -1210,19 +1237,21 @@ class easy_install(Command):
if dist.location not in self.shadow_path:
self.shadow_path.append(dist.location)
- if not self.dry_run:
+ if self.dry_run:
+ return
- self.pth_file.save()
+ self.pth_file.save()
- if dist.key == 'setuptools':
- # Ensure that setuptools itself never becomes unavailable!
- # XXX should this check for latest version?
- filename = os.path.join(self.install_dir, 'setuptools.pth')
- if os.path.islink(filename):
- os.unlink(filename)
- f = open(filename, 'wt')
- f.write(self.pth_file.make_relative(dist.location) + '\n')
- f.close()
+ if dist.key != 'setuptools':
+ return
+
+ # Ensure that setuptools itself never becomes unavailable!
+ # XXX should this check for latest version?
+ filename = os.path.join(self.install_dir, 'setuptools.pth')
+ if os.path.islink(filename):
+ os.unlink(filename)
+ with open(filename, 'wt') as f:
+ f.write(self.pth_file.make_relative(dist.location) + '\n')
def unpack_progress(self, src, dst):
# Progress filter for unpacking
@@ -1293,53 +1322,18 @@ class easy_install(Command):
* You can set up the installation directory to support ".pth" files by
using one of the approaches described here:
- https://setuptools.readthedocs.io/en/latest/easy_install.html#custom-installation-locations
+ https://setuptools.pypa.io/en/latest/deprecated/easy_install.html#custom-installation-locations
- Please make the appropriate changes for your system and try again.""").lstrip()
-
- def no_default_version_msg(self):
- template = self.__no_default_msg
- return template % (self.install_dir, os.environ.get('PYTHONPATH', ''))
-
- def install_site_py(self):
- """Make sure there's a site.py in the target dir, if needed"""
-
- if self.sitepy_installed:
- return # already did it, or don't need to
-
- sitepy = os.path.join(self.install_dir, "site.py")
- source = resource_string("setuptools", "site-patch.py")
- source = source.decode('utf-8')
- current = ""
-
- if os.path.exists(sitepy):
- log.debug("Checking existing site.py in %s", self.install_dir)
- with io.open(sitepy) as strm:
- current = strm.read()
-
- if not current.startswith('def __boot():'):
- raise DistutilsError(
- "%s is not a setuptools-generated site.py; please"
- " remove it." % sitepy
- )
-
- if current != source:
- log.info("Creating %s", sitepy)
- if not self.dry_run:
- ensure_directory(sitepy)
- with io.open(sitepy, 'w', encoding='utf-8') as strm:
- strm.write(source)
- self.byte_compile([sitepy])
-
- self.sitepy_installed = True
+ Please make the appropriate changes for your system and try again.
+ """).strip()
def create_home_path(self):
"""Create directories under ~."""
if not self.user:
return
home = convert_path(os.path.expanduser("~"))
- for name, path in six.iteritems(self.config_vars):
+ for path in only_strs(self.config_vars.values()):
if path.startswith(home) and not os.path.isdir(path):
self.debug_print("os.makedirs('%s', 0o700)" % path)
os.makedirs(path, 0o700)
@@ -1361,7 +1355,7 @@ class easy_install(Command):
if self.prefix:
# Set default install_dir/scripts from --prefix
- config_vars = config_vars.copy()
+ config_vars = dict(config_vars)
config_vars['base'] = self.prefix
scheme = self.INSTALL_SCHEMES.get(os.name, self.DEFAULT_SCHEME)
for attr, val in scheme.items():
@@ -1398,58 +1392,63 @@ def get_site_dirs():
if sys.exec_prefix != sys.prefix:
prefixes.append(sys.exec_prefix)
for prefix in prefixes:
- if prefix:
- if sys.platform in ('os2emx', 'riscos'):
- sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))
- elif os.sep == '/':
- sitedirs.extend([
- os.path.join(
- prefix,
- "lib",
- "python" + sys.version[:3],
- "site-packages",
- ),
- os.path.join(prefix, "lib", "site-python"),
- ])
- else:
- sitedirs.extend([
+ if not prefix:
+ continue
+
+ if sys.platform in ('os2emx', 'riscos'):
+ sitedirs.append(os.path.join(prefix, "Lib", "site-packages"))
+ elif os.sep == '/':
+ sitedirs.extend([
+ os.path.join(
prefix,
- os.path.join(prefix, "lib", "site-packages"),
- ])
- if sys.platform == 'darwin':
- # for framework builds *only* we add the standard Apple
- # locations. Currently only per-user, but /Library and
- # /Network/Library could be added too
- if 'Python.framework' in prefix:
- home = os.environ.get('HOME')
- if home:
- home_sp = os.path.join(
- home,
- 'Library',
- 'Python',
- sys.version[:3],
- 'site-packages',
- )
- sitedirs.append(home_sp)
+ "lib",
+ "python{}.{}".format(*sys.version_info),
+ "site-packages",
+ ),
+ os.path.join(prefix, "lib", "site-python"),
+ ])
+ else:
+ sitedirs.extend([
+ prefix,
+ os.path.join(prefix, "lib", "site-packages"),
+ ])
+ if sys.platform != 'darwin':
+ continue
+
+ # for framework builds *only* we add the standard Apple
+ # locations. Currently only per-user, but /Library and
+ # /Network/Library could be added too
+ if 'Python.framework' not in prefix:
+ continue
+
+ home = os.environ.get('HOME')
+ if not home:
+ continue
+
+ home_sp = os.path.join(
+ home,
+ 'Library',
+ 'Python',
+ '{}.{}'.format(*sys.version_info),
+ 'site-packages',
+ )
+ sitedirs.append(home_sp)
lib_paths = get_path('purelib'), get_path('platlib')
- for site_lib in lib_paths:
- if site_lib not in sitedirs:
- sitedirs.append(site_lib)
+
+ sitedirs.extend(s for s in lib_paths if s not in sitedirs)
if site.ENABLE_USER_SITE:
sitedirs.append(site.USER_SITE)
- try:
+ with contextlib.suppress(AttributeError):
sitedirs.extend(site.getsitepackages())
- except AttributeError:
- pass
sitedirs = list(map(normalize_path, sitedirs))
return sitedirs
-def expand_paths(inputs):
+def expand_paths(inputs): # noqa: C901 # is too complex (11) # FIXME
"""Yield sys.path directories that might contain "old-style" packages"""
seen = {}
@@ -1481,13 +1480,18 @@ def expand_paths(inputs):
# Yield existing non-dupe, non-import directory lines from it
for line in lines:
- if not line.startswith("import"):
- line = normalize_path(line.rstrip())
- if line not in seen:
- seen[line] = 1
- if not os.path.isdir(line):
- continue
- yield line, os.listdir(line)
+ if line.startswith("import"):
+ continue
+
+ line = normalize_path(line.rstrip())
+ if line in seen:
+ continue
+
+ seen[line] = 1
+ if not os.path.isdir(line):
+ continue
+
+ yield line, os.listdir(line)
def extract_wininst_cfg(dist_filename):
@@ -1520,7 +1524,7 @@ def extract_wininst_cfg(dist_filename):
# Now the config is in bytes, but for RawConfigParser, it should
# be text, so decode it.
config = config.decode(sys.getfilesystemencoding())
- cfg.readfp(six.StringIO(config))
+ cfg.read_file(io.StringIO(config))
except configparser.Error:
return None
if not cfg.has_section('metadata') or not cfg.has_section('Setup'):
@@ -1555,9 +1559,7 @@ def get_exe_prefixes(exe_filename):
if name.endswith('-nspkg.pth'):
continue
if parts[0].upper() in ('PURELIB', 'PLATLIB'):
- contents = z.read(name)
- if six.PY3:
- contents = contents.decode()
+ contents = z.read(name).decode()
for pth in yield_lines(contents):
pth = pth.strip().replace('\\', '/')
if not pth.startswith('import'):
@@ -1580,7 +1582,7 @@ class PthDistributions(Environment):
self.sitedirs = list(map(normalize_path, sitedirs))
self.basedir = normalize_path(os.path.dirname(self.filename))
self._load()
- Environment.__init__(self, [], None, None)
+ super().__init__([], None, None)
for path in yield_lines(self.paths):
list(map(self.add, find_distributions(path, True)))
@@ -1653,14 +1655,14 @@ class PthDistributions(Environment):
if new_path:
self.paths.append(dist.location)
self.dirty = True
- Environment.add(self, dist)
+ super().add(dist)
def remove(self, dist):
"""Remove `dist` from the distribution map"""
while dist.location in self.paths:
self.paths.remove(dist.location)
self.dirty = True
- Environment.remove(self, dist)
+ super().remove(dist)
def make_relative(self, path):
npath, last = os.path.split(normalize_path(path))
@@ -1721,7 +1723,8 @@ def auto_chmod(func, arg, exc):
chmod(arg, stat.S_IWRITE)
return func(arg)
et, ev, _ = sys.exc_info()
- six.reraise(et, (ev[0], ev[1] + (" %s %s" % (func, arg))))
+ # TODO: This code doesn't make sense. What is it trying to do?
+ raise (ev[0], ev[1] + (" %s %s" % (func, arg)))
def update_dist_caches(dist_path, fix_zipimporter_caches):
@@ -2049,7 +2052,7 @@ class WindowsCommandSpec(CommandSpec):
split_args = dict(posix=False)
-class ScriptWriter(object):
+class ScriptWriter:
"""
Encapsulates behavior around writing entry point scripts for console and
gui apps.
@@ -2057,24 +2060,45 @@ class ScriptWriter(object):
template = textwrap.dedent(r"""
# EASY-INSTALL-ENTRY-SCRIPT: %(spec)r,%(group)r,%(name)r
- __requires__ = %(spec)r
import re
import sys
- from pkg_resources import load_entry_point
+
+ # for compatibility with easy_install; see #2198
+ __requires__ = %(spec)r
+
+ try:
+ from importlib.metadata import distribution
+ except ImportError:
+ try:
+ from importlib_metadata import distribution
+ except ImportError:
+ from pkg_resources import load_entry_point
+
+
+ def importlib_load_entry_point(spec, group, name):
+ dist_name, _, _ = spec.partition('==')
+ matches = (
+ entry_point
+ for entry_point in distribution(dist_name).entry_points
+ if entry_point.group == group and entry_point.name == name
+ )
+ return next(matches).load()
+
+
+ globals().setdefault('load_entry_point', importlib_load_entry_point)
+
if __name__ == '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
- sys.exit(
- load_entry_point(%(spec)r, %(group)r, %(name)r)()
- )
- """).lstrip()
+ sys.exit(load_entry_point(%(spec)r, %(group)r, %(name)r)())
+ """).lstrip()
command_spec_class = CommandSpec
@classmethod
def get_script_args(cls, dist, executable=None, wininst=False):
# for backward compatibility
- warnings.warn("Use get_args", DeprecationWarning)
+ warnings.warn("Use get_args", EasyInstallDeprecationWarning)
writer = (WindowsScriptWriter if wininst else ScriptWriter).best()
header = cls.get_script_header("", executable, wininst)
return writer.get_args(dist, header)
@@ -2082,12 +2106,11 @@ class ScriptWriter(object):
@classmethod
def get_script_header(cls, script_text, executable=None, wininst=False):
# for backward compatibility
- warnings.warn("Use get_header", DeprecationWarning)
+ warnings.warn(
+ "Use get_header", EasyInstallDeprecationWarning, stacklevel=2)
if wininst:
executable = "python.exe"
- cmd = cls.command_spec_class.best().from_param(executable)
- cmd.install_options(script_text)
- return cmd.as_header()
+ return cls.get_header(script_text, executable)
@classmethod
def get_args(cls, dist, header=None):
@@ -2119,7 +2142,7 @@ class ScriptWriter(object):
@classmethod
def get_writer(cls, force_windows):
# for backward compatibility
- warnings.warn("Use best", DeprecationWarning)
+ warnings.warn("Use best", EasyInstallDeprecationWarning)
return WindowsScriptWriter.best() if force_windows else cls.best()
@classmethod
@@ -2151,7 +2174,7 @@ class WindowsScriptWriter(ScriptWriter):
@classmethod
def get_writer(cls):
# for backward compatibility
- warnings.warn("Use best", DeprecationWarning)
+ warnings.warn("Use best", EasyInstallDeprecationWarning)
return cls.best()
@classmethod
@@ -2186,7 +2209,7 @@ class WindowsScriptWriter(ScriptWriter):
@classmethod
def _adjust_header(cls, type_, orig_header):
"""
- Make sure 'pythonw' is used for gui and and 'python' is used for
+ Make sure 'pythonw' is used for gui and 'python' is used for
console (regardless of what sys.executable is).
"""
pattern = 'pythonw.exe'
@@ -2256,7 +2279,10 @@ def get_win_launcher(type):
"""
launcher_fn = '%s.exe' % type
if is_64bit():
- launcher_fn = launcher_fn.replace(".", "-64.")
+ if get_platform() == "win-arm64":
+ launcher_fn = launcher_fn.replace(".", "-arm64.")
+ else:
+ launcher_fn = launcher_fn.replace(".", "-64.")
else:
launcher_fn = launcher_fn.replace(".", "-32.")
return resource_string('setuptools', launcher_fn)
@@ -2264,10 +2290,7 @@ def get_win_launcher(type):
def load_launcher_manifest(name):
manifest = pkg_resources.resource_string(__name__, 'launcher manifest.xml')
- if six.PY2:
- return manifest % vars()
- else:
- return manifest.decode('utf-8') % vars()
+ return manifest.decode('utf-8') % vars()
def rmtree(path, ignore_errors=False, onerror=auto_chmod):
@@ -2280,55 +2303,14 @@ 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 only_strs(values):
+ """
+ Exclude non-str values. Ref #3063.
+ """
+ return filter(lambda val: isinstance(val, str), values)
- 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):
+ """
+ Warning for EasyInstall deprecations, bypassing suppression.
+ """
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index f3e604d..6338965 100755..100644
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -8,6 +8,7 @@ from distutils.util import convert_path
from distutils import log
import distutils.errors
import distutils.filelist
+import functools
import os
import re
import sys
@@ -16,8 +17,8 @@ import warnings
import time
import collections
-from setuptools.extern import six
-from setuptools.extern.six.moves import map
+from .._importlib import metadata
+from .. import _entry_points
from setuptools import Command
from setuptools.command.sdist import sdist
@@ -25,15 +26,17 @@ from setuptools.command.sdist import walk_revctrl
from setuptools.command.setopt import edit_config
from setuptools.command import bdist_egg
from pkg_resources import (
- parse_requirements, safe_name, parse_version,
- safe_version, yield_lines, EntryPoint, iter_entry_points, to_filename)
+ Requirement, safe_name, parse_version,
+ safe_version, to_filename)
import setuptools.unicode_utils as unicode_utils
from setuptools.glob import glob
from setuptools.extern import packaging
+from setuptools.extern.jaraco.text import yield_lines
+from setuptools import SetuptoolsDeprecationWarning
-def translate_pattern(glob):
+def translate_pattern(glob): # noqa: C901 # is too complex (14) # FIXME
"""
Translate a file path glob like '*.txt' in to a regular expression.
This differs from fnmatch.translate which allows wildcards to match
@@ -113,10 +116,41 @@ def translate_pattern(glob):
pat += sep
pat += r'\Z'
- return re.compile(pat, flags=re.MULTILINE|re.DOTALL)
+ return re.compile(pat, flags=re.MULTILINE | re.DOTALL)
-class egg_info(Command):
+class InfoCommon:
+ tag_build = None
+ tag_date = None
+
+ @property
+ def name(self):
+ return safe_name(self.distribution.get_name())
+
+ def tagged_version(self):
+ return safe_version(self._maybe_tag(self.distribution.get_version()))
+
+ def _maybe_tag(self, version):
+ """
+ egg_info may be called more than once for a distribution,
+ in which case the version string already contains all tags.
+ """
+ return (
+ version if self.vtags and version.endswith(self.vtags)
+ else version + self.vtags
+ )
+
+ def tags(self):
+ version = ''
+ if self.tag_build:
+ version += self.tag_build
+ if self.tag_date:
+ version += time.strftime("-%Y%m%d")
+ return version
+ vtags = property(tags)
+
+
+class egg_info(InfoCommon, Command):
description = "create a distribution's .egg-info directory"
user_options = [
@@ -133,14 +167,11 @@ class egg_info(Command):
}
def initialize_options(self):
- self.egg_name = None
- self.egg_version = None
self.egg_base = None
+ self.egg_name = None
self.egg_info = None
- self.tag_build = None
- self.tag_date = 0
+ self.egg_version = None
self.broken_egg_info = False
- self.vtags = None
####################################
# allow the 'tag_svn_revision' to be detected and
@@ -168,25 +199,23 @@ class egg_info(Command):
edit_config(filename, dict(egg_info=egg_info))
def finalize_options(self):
- self.egg_name = safe_name(self.distribution.get_name())
- self.vtags = self.tags()
+ # Note: we need to capture the current value returned
+ # by `self.tagged_version()`, so we can later update
+ # `self.distribution.metadata.version` without
+ # repercussions.
+ self.egg_name = self.name
self.egg_version = self.tagged_version()
-
parsed_version = parse_version(self.egg_version)
try:
is_version = isinstance(parsed_version, packaging.version.Version)
- spec = (
- "%s==%s" if is_version else "%s===%s"
- )
- list(
- parse_requirements(spec % (self.egg_name, self.egg_version))
- )
- except ValueError:
+ spec = "%s==%s" if is_version else "%s===%s"
+ Requirement(spec % (self.egg_name, self.egg_version))
+ except ValueError as e:
raise distutils.errors.DistutilsOptionError(
"Invalid distribution name or version syntax: %s-%s" %
(self.egg_name, self.egg_version)
- )
+ ) from e
if self.egg_base is None:
dirs = self.distribution.package_dir
@@ -241,8 +270,7 @@ class egg_info(Command):
to the file.
"""
log.info("writing %s to %s", what, filename)
- if six.PY3:
- data = data.encode("utf-8")
+ data = data.encode("utf-8")
if not self.dry_run:
f = open(filename, 'wb')
f.write(data)
@@ -254,20 +282,12 @@ class egg_info(Command):
if not self.dry_run:
os.unlink(filename)
- def tagged_version(self):
- version = self.distribution.get_version()
- # egg_info may be called more than once for a distribution,
- # in which case the version string already contains all tags.
- if self.vtags and version.endswith(self.vtags):
- return safe_version(version)
- return safe_version(version + self.vtags)
-
def run(self):
self.mkpath(self.egg_info)
- installer = self.distribution.fetch_build_egg
- for ep in iter_entry_points('egg_info.writers'):
- ep.require(installer=installer)
- writer = ep.resolve()
+ os.utime(self.egg_info, None)
+ for ep in metadata.entry_points(group='egg_info.writers'):
+ self.distribution._install_dependencies(ep)
+ writer = ep.load()
writer(self, ep.name, os.path.join(self.egg_info, ep.name))
# Get rid of native_libs.txt if it was put there by older bdist_egg
@@ -277,14 +297,6 @@ class egg_info(Command):
self.find_sources()
- def tags(self):
- version = ''
- if self.tag_build:
- version += self.tag_build
- if self.tag_date:
- version += time.strftime("-%Y%m%d")
- return version
-
def find_sources(self):
"""Generate SOURCES.txt manifest file"""
manifest_filename = os.path.join(self.egg_info, "SOURCES.txt")
@@ -320,70 +332,74 @@ class FileList(_FileList):
# patterns, (dir and patterns), or (dir_pattern).
(action, patterns, dir, dir_pattern) = self._parse_template_line(line)
+ action_map = {
+ 'include': self.include,
+ 'exclude': self.exclude,
+ 'global-include': self.global_include,
+ 'global-exclude': self.global_exclude,
+ 'recursive-include': functools.partial(
+ self.recursive_include, dir,
+ ),
+ 'recursive-exclude': functools.partial(
+ self.recursive_exclude, dir,
+ ),
+ 'graft': self.graft,
+ 'prune': self.prune,
+ }
+ log_map = {
+ 'include': "warning: no files found matching '%s'",
+ 'exclude': (
+ "warning: no previously-included files found "
+ "matching '%s'"
+ ),
+ 'global-include': (
+ "warning: no files found matching '%s' "
+ "anywhere in distribution"
+ ),
+ 'global-exclude': (
+ "warning: no previously-included files matching "
+ "'%s' found anywhere in distribution"
+ ),
+ 'recursive-include': (
+ "warning: no files found matching '%s' "
+ "under directory '%s'"
+ ),
+ 'recursive-exclude': (
+ "warning: no previously-included files matching "
+ "'%s' found under directory '%s'"
+ ),
+ 'graft': "warning: no directories found matching '%s'",
+ 'prune': "no previously-included directories found matching '%s'",
+ }
+
+ try:
+ process_action = action_map[action]
+ except KeyError:
+ raise DistutilsInternalError(
+ "this cannot happen: invalid action '{action!s}'".
+ format(action=action),
+ )
+
# OK, now we know that the action is valid and we have the
# right number of words on the line for that action -- so we
# can proceed with minimal error-checking.
- if action == 'include':
- self.debug_print("include " + ' '.join(patterns))
- for pattern in patterns:
- if not self.include(pattern):
- log.warn("warning: no files found matching '%s'", pattern)
-
- elif action == 'exclude':
- self.debug_print("exclude " + ' '.join(patterns))
- for pattern in patterns:
- if not self.exclude(pattern):
- log.warn(("warning: no previously-included files "
- "found matching '%s'"), pattern)
-
- elif action == 'global-include':
- self.debug_print("global-include " + ' '.join(patterns))
- for pattern in patterns:
- if not self.global_include(pattern):
- log.warn(("warning: no files found matching '%s' "
- "anywhere in distribution"), pattern)
-
- elif action == 'global-exclude':
- self.debug_print("global-exclude " + ' '.join(patterns))
- for pattern in patterns:
- if not self.global_exclude(pattern):
- log.warn(("warning: no previously-included files matching "
- "'%s' found anywhere in distribution"),
- pattern)
-
- elif action == 'recursive-include':
- self.debug_print("recursive-include %s %s" %
- (dir, ' '.join(patterns)))
- for pattern in patterns:
- if not self.recursive_include(dir, pattern):
- log.warn(("warning: no files found matching '%s' "
- "under directory '%s'"),
- pattern, dir)
-
- elif action == 'recursive-exclude':
- self.debug_print("recursive-exclude %s %s" %
- (dir, ' '.join(patterns)))
- for pattern in patterns:
- if not self.recursive_exclude(dir, pattern):
- log.warn(("warning: no previously-included files matching "
- "'%s' found under directory '%s'"),
- pattern, dir)
-
- elif action == 'graft':
- self.debug_print("graft " + dir_pattern)
- if not self.graft(dir_pattern):
- log.warn("warning: no directories found matching '%s'",
- dir_pattern)
-
- elif action == 'prune':
- self.debug_print("prune " + dir_pattern)
- if not self.prune(dir_pattern):
- log.warn(("no previously-included directories found "
- "matching '%s'"), dir_pattern)
-
- else:
- raise DistutilsInternalError(
- "this cannot happen: invalid action '%s'" % action)
+
+ action_is_recursive = action.startswith('recursive-')
+ if action in {'graft', 'prune'}:
+ patterns = [dir_pattern]
+ extra_log_args = (dir, ) if action_is_recursive else ()
+ log_tmpl = log_map[action]
+
+ self.debug_print(
+ ' '.join(
+ [action] +
+ ([dir] if action_is_recursive else []) +
+ patterns,
+ )
+ )
+ for pattern in patterns:
+ if not process_action(pattern):
+ log.warn(log_tmpl, pattern, *extra_log_args)
def _remove_files(self, predicate):
"""
@@ -524,6 +540,7 @@ class manifest_maker(sdist):
self.add_defaults()
if os.path.exists(self.template):
self.read_template()
+ self.add_license_files()
self.prune_file_list()
self.filelist.sort()
self.filelist.remove_duplicates()
@@ -565,9 +582,22 @@ class manifest_maker(sdist):
self.filelist.extend(rcfiles)
elif os.path.exists(self.manifest):
self.read_manifest()
+
+ if os.path.exists("setup.py"):
+ # setup.py should be included by default, even if it's not
+ # the script called to create the sdist
+ self.filelist.append("setup.py")
+
ei_cmd = self.get_finalized_command('egg_info')
self.filelist.graft(ei_cmd.egg_info)
+ def add_license_files(self):
+ license_files = self.distribution.metadata.license_files or []
+ for lf in license_files:
+ log.info("adding license file '%s'", lf)
+ pass
+ self.filelist.extend(license_files)
+
def prune_file_list(self):
build = self.get_finalized_command('build')
base_dir = self.distribution.get_fullname()
@@ -577,6 +607,27 @@ class manifest_maker(sdist):
self.filelist.exclude_pattern(r'(^|' + sep + r')(RCS|CVS|\.svn)' + sep,
is_regex=1)
+ def _safe_data_files(self, build_py):
+ """
+ The parent class implementation of this method
+ (``sdist``) will try to include data files, which
+ might cause recursion problems when
+ ``include_package_data=True``.
+
+ Therefore, avoid triggering any attempt of
+ analyzing/building the manifest again.
+ """
+ if hasattr(build_py, 'get_data_files_without_manifest'):
+ return build_py.get_data_files_without_manifest()
+
+ warnings.warn(
+ "Custom 'build_py' does not implement "
+ "'get_data_files_without_manifest'.\nPlease extend command classes"
+ " from setuptools instead of distutils.",
+ SetuptoolsDeprecationWarning
+ )
+ return build_py.get_data_files()
+
def write_file(filename, contents):
"""Create a file with the specified name and write 'contents' (a
@@ -620,14 +671,16 @@ def warn_depends_obsolete(cmd, basename, filename):
def _write_requirements(stream, reqs):
lines = yield_lines(reqs or ())
- append_cr = lambda line: line + '\n'
+
+ def append_cr(line):
+ return line + '\n'
lines = map(append_cr, lines)
stream.writelines(lines)
def write_requirements(cmd, basename, filename):
dist = cmd.distribution
- data = six.StringIO()
+ data = io.StringIO()
_write_requirements(data, dist.install_requires)
extras_require = dist.extras_require or {}
for extra in sorted(extras_require):
@@ -665,20 +718,9 @@ def write_arg(cmd, basename, filename, force=False):
def write_entries(cmd, basename, filename):
- ep = cmd.distribution.entry_points
-
- if isinstance(ep, six.string_types) or ep is None:
- data = ep
- elif ep is not None:
- data = []
- for section, contents in sorted(ep.items()):
- if not isinstance(contents, six.string_types):
- contents = EntryPoint.parse_group(section, contents)
- contents = '\n'.join(sorted(map(str, contents.values())))
- data.append('[%s]\n%s\n\n' % (section, contents))
- data = ''.join(data)
-
- cmd.write_or_delete_file('entry points', filename, data, True)
+ eps = _entry_points.load(cmd.distribution.entry_points)
+ defn = _entry_points.render(eps)
+ cmd.write_or_delete_file('entry points', filename, defn, True)
def get_pkg_info_revision():
@@ -686,7 +728,8 @@ def get_pkg_info_revision():
Get a -r### off of PKG-INFO Version in case this is an sdist of
a subversion revision.
"""
- warnings.warn("get_pkg_info_revision is deprecated.", DeprecationWarning)
+ warnings.warn(
+ "get_pkg_info_revision is deprecated.", EggInfoDeprecationWarning)
if os.path.exists('PKG-INFO'):
with io.open('PKG-INFO') as f:
for line in f:
@@ -694,3 +737,7 @@ def get_pkg_info_revision():
if match:
return int(match.group(1))
return 0
+
+
+class EggInfoDeprecationWarning(SetuptoolsDeprecationWarning):
+ """Deprecated behavior warning for EggInfo, bypassing suppression."""
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 31a5ddb..35e54d2 100644
--- a/setuptools/command/install.py
+++ b/setuptools/command/install.py
@@ -30,6 +30,13 @@ class install(orig.install):
_nc = dict(new_commands)
def initialize_options(self):
+
+ warnings.warn(
+ "setup.py install is deprecated. "
+ "Use build and pip and other standards-based tools.",
+ setuptools.SetuptoolsDeprecationWarning,
+ )
+
orig.install.initialize_options(self)
self.old_and_unmanageable = None
self.single_version_externally_managed = None
@@ -114,7 +121,7 @@ class install(orig.install):
args.insert(0, setuptools.bootstrap_install_from)
cmd.args = args
- cmd.run()
+ cmd.run(show_deprecation=False)
setuptools.bootstrap_install_from = None
diff --git a/setuptools/command/install_egg_info.py b/setuptools/command/install_egg_info.py
index edc4718..65ede40 100755..100644
--- a/setuptools/command/install_egg_info.py
+++ b/setuptools/command/install_egg_info.py
@@ -4,6 +4,7 @@ import os
from setuptools import Command
from setuptools import namespaces
from setuptools.archive_util import unpack_archive
+from .._path import ensure_directory
import pkg_resources
@@ -37,7 +38,7 @@ class install_egg_info(namespaces.Installer, Command):
elif os.path.exists(self.target):
self.execute(os.unlink, (self.target,), "Removing " + self.target)
if not self.dry_run:
- pkg_resources.ensure_directory(self.target)
+ ensure_directory(self.target)
self.execute(
self.copytree, (), "Copying %s to %s" % (self.source, self.target)
)
diff --git a/setuptools/command/install_lib.py b/setuptools/command/install_lib.py
index 2b31c3e..2e9d875 100644
--- a/setuptools/command/install_lib.py
+++ b/setuptools/command/install_lib.py
@@ -1,5 +1,5 @@
import os
-import imp
+import sys
from itertools import product, starmap
import distutils.command.install_lib as orig
@@ -74,10 +74,11 @@ class install_lib(orig.install_lib):
yield '__init__.pyc'
yield '__init__.pyo'
- if not hasattr(imp, 'get_tag'):
+ if not hasattr(sys, 'implementation'):
return
- base = os.path.join('__pycache__', '__init__.' + imp.get_tag())
+ base = os.path.join(
+ '__pycache__', '__init__.' + sys.implementation.cache_tag)
yield base + '.pyc'
yield base + '.pyo'
yield base + '.opt-1.pyc'
diff --git a/setuptools/command/install_scripts.py b/setuptools/command/install_scripts.py
index 1623427..aeb0e42 100755..100644
--- a/setuptools/command/install_scripts.py
+++ b/setuptools/command/install_scripts.py
@@ -1,9 +1,11 @@
from distutils import log
import distutils.command.install_scripts as orig
+from distutils.errors import DistutilsModuleError
import os
import sys
-from pkg_resources import Distribution, PathMetadata, ensure_directory
+from pkg_resources import Distribution, PathMetadata
+from .._path import ensure_directory
class install_scripts(orig.install_scripts):
@@ -32,8 +34,11 @@ class install_scripts(orig.install_scripts):
)
bs_cmd = self.get_finalized_command('build_scripts')
exec_param = getattr(bs_cmd, 'executable', None)
- bw_cmd = self.get_finalized_command("bdist_wininst")
- is_wininst = getattr(bw_cmd, '_is_running', False)
+ try:
+ bw_cmd = self.get_finalized_command("bdist_wininst")
+ is_wininst = getattr(bw_cmd, '_is_running', False)
+ except (ImportError, DistutilsModuleError):
+ is_wininst = False
writer = ei.ScriptWriter
if is_wininst:
exec_param = "python.exe"
diff --git a/setuptools/command/py36compat.py b/setuptools/command/py36compat.py
index 61063e7..343547a 100644
--- a/setuptools/command/py36compat.py
+++ b/setuptools/command/py36compat.py
@@ -3,8 +3,6 @@ from glob import glob
from distutils.util import convert_path
from distutils.command import sdist
-from setuptools.extern.six.moves import filter
-
class sdist_add_defaults:
"""
@@ -132,5 +130,5 @@ class sdist_add_defaults:
if hasattr(sdist.sdist, '_add_defaults_standards'):
# disable the functionality already available upstream
- class sdist_add_defaults:
+ class sdist_add_defaults: # noqa
pass
diff --git a/setuptools/command/register.py b/setuptools/command/register.py
index 8d6336a..b8266b9 100755..100644
--- a/setuptools/command/register.py
+++ b/setuptools/command/register.py
@@ -1,10 +1,18 @@
+from distutils import log
import distutils.command.register as orig
+from setuptools.errors import RemovedCommandError
+
class register(orig.register):
- __doc__ = orig.register.__doc__
+ """Formerly used to register packages on PyPI."""
def run(self):
- # Make sure that we are using valid current name/version info
- self.run_command('egg_info')
- orig.register.run(self)
+ msg = (
+ "The register command has been removed, use twine to upload "
+ + "instead (https://pypi.org/p/twine)"
+ )
+
+ self.announce("ERROR: " + msg, log.ERROR)
+
+ raise RemovedCommandError(msg)
diff --git a/setuptools/command/rotate.py b/setuptools/command/rotate.py
index b89353f..74795ba 100755..100644
--- a/setuptools/command/rotate.py
+++ b/setuptools/command/rotate.py
@@ -4,8 +4,6 @@ from distutils.errors import DistutilsOptionError
import os
import shutil
-from setuptools.extern import six
-
from setuptools import Command
@@ -36,9 +34,9 @@ class rotate(Command):
raise DistutilsOptionError("Must specify number of files to keep")
try:
self.keep = int(self.keep)
- except ValueError:
- raise DistutilsOptionError("--keep must be an integer")
- if isinstance(self.match, six.string_types):
+ except ValueError as e:
+ raise DistutilsOptionError("--keep must be an integer") from e
+ if isinstance(self.match, str):
self.match = [
convert_path(p.strip()) for p in self.match.split(',')
]
diff --git a/setuptools/command/saveopts.py b/setuptools/command/saveopts.py
index 611cec5..611cec5 100755..100644
--- a/setuptools/command/saveopts.py
+++ b/setuptools/command/saveopts.py
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index bcfae4d..0ffeacf 100755..100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -5,18 +5,16 @@ import sys
import io
import contextlib
-from setuptools.extern import six
-
from .py36compat import sdist_add_defaults
-import pkg_resources
+from .._importlib import metadata
_default_revctrl = list
def walk_revctrl(dirname=''):
"""Find all files under revision control"""
- for ep in pkg_resources.iter_entry_points('setuptools.file_finders'):
+ for ep in metadata.entry_points(group='setuptools.file_finders'):
for item in ep.load()(dirname):
yield item
@@ -33,6 +31,10 @@ class sdist(sdist_add_defaults, orig.sdist):
('dist-dir=', 'd',
"directory to put the source distribution archive(s) in "
"[default: dist]"),
+ ('owner=', 'u',
+ "Owner name used when creating a tar file [default: current user]"),
+ ('group=', 'g',
+ "Group name used when creating a tar file [default: current group]"),
]
negative_opt = {}
@@ -98,49 +100,44 @@ class sdist(sdist_add_defaults, orig.sdist):
if orig_val is not NoValue:
setattr(os, 'link', orig_val)
- def __read_template_hack(self):
- # This grody hack closes the template file (MANIFEST.in) if an
- # exception occurs during read_template.
- # Doing so prevents an error when easy_install attempts to delete the
- # file.
- try:
- orig.sdist.read_template(self)
- except Exception:
- _, _, tb = sys.exc_info()
- tb.tb_next.tb_frame.f_locals['template'].close()
- raise
-
- # Beginning with Python 2.7.2, 3.1.4, and 3.2.1, this leaky file handle
- # has been fixed, so only override the method if we're using an earlier
- # Python.
- has_leaky_handle = (
- sys.version_info < (2, 7, 2)
- or (3, 0) <= sys.version_info < (3, 1, 4)
- or (3, 2) <= sys.version_info < (3, 2, 1)
- )
- if has_leaky_handle:
- read_template = __read_template_hack
+ def _add_defaults_optional(self):
+ super()._add_defaults_optional()
+ if os.path.isfile('pyproject.toml'):
+ self.filelist.append('pyproject.toml')
def _add_defaults_python(self):
"""getting python files"""
if self.distribution.has_pure_modules():
build_py = self.get_finalized_command('build_py')
self.filelist.extend(build_py.get_source_files())
- # This functionality is incompatible with include_package_data, and
- # will in fact create an infinite recursion if include_package_data
- # is True. Use of include_package_data will imply that
- # distutils-style automatic handling of package_data is disabled
- if not self.distribution.include_package_data:
- for _, src_dir, _, filenames in build_py.data_files:
- self.filelist.extend([os.path.join(src_dir, filename)
- for filename in filenames])
+ self._add_data_files(self._safe_data_files(build_py))
+
+ def _safe_data_files(self, build_py):
+ """
+ Since the ``sdist`` class is also used to compute the MANIFEST
+ (via :obj:`setuptools.command.egg_info.manifest_maker`),
+ there might be recursion problems when trying to obtain the list of
+ data_files and ``include_package_data=True`` (which in turn depends on
+ the files included in the MANIFEST).
+
+ To avoid that, ``manifest_maker`` should be able to overwrite this
+ method and avoid recursive attempts to build/analyze the MANIFEST.
+ """
+ return build_py.data_files
+
+ def _add_data_files(self, data_files):
+ """
+ Add data files as found in build_py.data_files.
+ """
+ self.filelist.extend(
+ os.path.join(src_dir, name)
+ for _, src_dir, _, filenames in data_files
+ for name in filenames
+ )
def _add_defaults_data_files(self):
try:
- if six.PY2:
- sdist_add_defaults._add_defaults_data_files(self)
- else:
- super()._add_defaults_data_files()
+ super()._add_defaults_data_files()
except TypeError:
log.warn("data_files contains unexpected objects")
@@ -186,12 +183,11 @@ class sdist(sdist_add_defaults, orig.sdist):
manifest = open(self.manifest, 'rb')
for line in manifest:
# The manifest must contain UTF-8. See #303.
- if six.PY3:
- try:
- line = line.decode('UTF-8')
- except UnicodeDecodeError:
- log.warn("%r not UTF-8 decodable -- skipping" % line)
- continue
+ try:
+ line = line.decode('UTF-8')
+ except UnicodeDecodeError:
+ log.warn("%r not UTF-8 decodable -- skipping" % line)
+ continue
# ignore comments and blank lines
line = line.strip()
if line.startswith('#') or not line:
diff --git a/setuptools/command/setopt.py b/setuptools/command/setopt.py
index 7e57cc0..6358c04 100755..100644
--- a/setuptools/command/setopt.py
+++ b/setuptools/command/setopt.py
@@ -3,8 +3,7 @@ from distutils import log
from distutils.errors import DistutilsOptionError
import distutils
import os
-
-from setuptools.extern.six.moves import configparser
+import configparser
from setuptools import Command
@@ -40,6 +39,7 @@ def edit_config(filename, settings, dry_run=False):
"""
log.debug("Reading configuration from %s", filename)
opts = configparser.RawConfigParser()
+ opts.optionxform = lambda x: x
opts.read([filename])
for section, options in settings.items():
if options is None:
diff --git a/setuptools/command/test.py b/setuptools/command/test.py
index 51aee1f..652f3e4 100644
--- a/setuptools/command/test.py
+++ b/setuptools/command/test.py
@@ -8,17 +8,22 @@ from distutils.errors import DistutilsError, DistutilsOptionError
from distutils import log
from unittest import TestLoader
-from setuptools.extern import six
-from setuptools.extern.six.moves import map, filter
-
-from pkg_resources import (resource_listdir, resource_exists, normalize_path,
- working_set, _namespace_packages, evaluate_marker,
- add_activation_listener, require, EntryPoint)
+from pkg_resources import (
+ resource_listdir,
+ resource_exists,
+ normalize_path,
+ working_set,
+ evaluate_marker,
+ add_activation_listener,
+ require,
+)
+from .._importlib import metadata
from setuptools import Command
+from setuptools.extern.more_itertools import unique_everseen
+from setuptools.extern.jaraco.functools import pass_none
class ScanningLoader(TestLoader):
-
def __init__(self):
TestLoader.__init__(self)
self._visited = set()
@@ -58,7 +63,7 @@ class ScanningLoader(TestLoader):
# adapted from jaraco.classes.properties:NonDataProperty
-class NonDataProperty(object):
+class NonDataProperty:
def __init__(self, fget):
self.fget = fget
@@ -71,12 +76,15 @@ class NonDataProperty(object):
class test(Command):
"""Command to run unit tests after in-place build"""
- description = "run unit tests after in-place build"
+ description = "run unit tests after in-place build (deprecated)"
user_options = [
('test-module=', 'm', "Run 'test_suite' in specified module"),
- ('test-suite=', 's',
- "Run single test, case or suite (e.g. 'module.test_suite')"),
+ (
+ 'test-suite=',
+ 's',
+ "Run single test, case or suite (e.g. 'module.test_suite')",
+ ),
('test-runner=', 'r', "Test runner to use"),
]
@@ -126,30 +134,11 @@ class test(Command):
@contextlib.contextmanager
def project_on_sys_path(self, include_dists=[]):
- with_2to3 = six.PY3 and getattr(self.distribution, 'use_2to3', False)
-
- if with_2to3:
- # If we run 2to3 we can not do this inplace:
+ self.run_command('egg_info')
- # Ensure metadata is up-to-date
- self.reinitialize_command('build_py', inplace=0)
- self.run_command('build_py')
- bpy_cmd = self.get_finalized_command("build_py")
- build_path = normalize_path(bpy_cmd.build_lib)
-
- # Build extensions
- self.reinitialize_command('egg_info', egg_base=build_path)
- self.run_command('egg_info')
-
- self.reinitialize_command('build_ext', inplace=0)
- self.run_command('build_ext')
- else:
- # Without 2to3 inplace works fine:
- self.run_command('egg_info')
-
- # Build extensions in-place
- self.reinitialize_command('build_ext', inplace=1)
- self.run_command('build_ext')
+ # Build extensions in-place
+ self.reinitialize_command('build_ext', inplace=1)
+ self.run_command('build_ext')
ei_cmd = self.get_finalized_command("egg_info")
@@ -184,7 +173,7 @@ class test(Command):
orig_pythonpath = os.environ.get('PYTHONPATH', nothing)
current_pythonpath = os.environ.get('PYTHONPATH', '')
try:
- prefix = os.pathsep.join(paths)
+ prefix = os.pathsep.join(unique_everseen(paths))
to_join = filter(None, [prefix, current_pythonpath])
new_path = os.pathsep.join(to_join)
if new_path:
@@ -205,12 +194,21 @@ class test(Command):
ir_d = dist.fetch_build_eggs(dist.install_requires)
tr_d = dist.fetch_build_eggs(dist.tests_require or [])
er_d = dist.fetch_build_eggs(
- v for k, v in dist.extras_require.items()
+ v
+ for k, v in dist.extras_require.items()
if k.startswith(':') and evaluate_marker(k[1:])
)
return itertools.chain(ir_d, tr_d, er_d)
def run(self):
+ self.announce(
+ "WARNING: Testing via this command is deprecated and will be "
+ "removed in a future version. Users looking for a generic test "
+ "entry point independent of test runner are encouraged to use "
+ "tox.",
+ log.WARN,
+ )
+
installed_dists = self.install_dists(self.distribution)
cmd = ' '.join(self._argv)
@@ -226,23 +224,10 @@ class test(Command):
self.run_tests()
def run_tests(self):
- # Purge modules under test from sys.modules. The test loader will
- # re-import them from the build location. Required when 2to3 is used
- # with namespace packages.
- if six.PY3 and getattr(self.distribution, 'use_2to3', False):
- module = self.test_suite.split('.')[0]
- if module in _namespace_packages:
- del_modules = []
- if module in sys.modules:
- del_modules.append(module)
- module += '.'
- for name in sys.modules:
- if name.startswith(module):
- del_modules.append(name)
- list(map(sys.modules.__delitem__, del_modules))
-
test = unittest.main(
- None, None, self._argv,
+ None,
+ None,
+ self._argv,
testLoader=self._resolve_as_ep(self.test_loader),
testRunner=self._resolve_as_ep(self.test_runner),
exit=False,
@@ -257,12 +242,10 @@ class test(Command):
return ['unittest'] + self.test_args
@staticmethod
+ @pass_none
def _resolve_as_ep(val):
"""
Load the indicated attribute value, called, as a as if it were
specified as an entry point.
"""
- if val is None:
- return
- parsed = EntryPoint.parse("x=" + val)
- return parsed.resolve()()
+ return metadata.EntryPoint(value=val, name=None, group=None).load()()
diff --git a/setuptools/command/upload.py b/setuptools/command/upload.py
index a44173a..ec7f81e 100644
--- a/setuptools/command/upload.py
+++ b/setuptools/command/upload.py
@@ -1,42 +1,17 @@
-import getpass
+from distutils import log
from distutils.command import upload as orig
+from setuptools.errors import RemovedCommandError
+
class upload(orig.upload):
- """
- Override default upload behavior to obtain password
- in a variety of different ways.
- """
+ """Formerly used to upload packages to PyPI."""
- def finalize_options(self):
- orig.upload.finalize_options(self)
- self.username = (
- self.username or
- getpass.getuser()
- )
- # Attempt to obtain password. Short circuit evaluation at the first
- # sign of success.
- self.password = (
- self.password or
- self._load_password_from_keyring() or
- self._prompt_for_password()
+ def run(self):
+ msg = (
+ "The upload command has been removed, use twine to upload "
+ + "instead (https://pypi.org/p/twine)"
)
- def _load_password_from_keyring(self):
- """
- Attempt to load password from keyring. Suppress Exceptions.
- """
- try:
- keyring = __import__('keyring')
- return keyring.get_password(self.repository, self.username)
- except Exception:
- pass
-
- def _prompt_for_password(self):
- """
- Prompt for a password on the tty. Suppress Exceptions.
- """
- try:
- return getpass.getpass()
- except (Exception, KeyboardInterrupt):
- pass
+ self.announce("ERROR: " + msg, log.ERROR)
+ raise RemovedCommandError(msg)
diff --git a/setuptools/command/upload_docs.py b/setuptools/command/upload_docs.py
index 07aa564..a548000 100644
--- a/setuptools/command/upload_docs.py
+++ b/setuptools/command/upload_docs.py
@@ -2,7 +2,7 @@
"""upload_docs
Implements a Distutils 'upload_docs' subcommand (upload documentation to
-PyPI's pythonhosted.org).
+sites other than PyPi such as devpi).
"""
from base64 import standard_b64encode
@@ -15,17 +15,18 @@ import tempfile
import shutil
import itertools
import functools
+import http.client
+import urllib.parse
+import warnings
-from setuptools.extern import six
-from setuptools.extern.six.moves import http_client, urllib
+from .._importlib import metadata
+from .. import SetuptoolsDeprecationWarning
-from pkg_resources import iter_entry_points
from .upload import upload
def _encode(s):
- errors = 'surrogateescape' if six.PY3 else 'strict'
- return s.encode('utf-8', errors)
+ return s.encode('utf-8', 'surrogateescape')
class upload_docs(upload):
@@ -33,7 +34,7 @@ class upload_docs(upload):
# supported by Warehouse (and won't be).
DEFAULT_REPOSITORY = 'https://pypi.python.org/pypi/'
- description = 'Upload documentation to PyPI'
+ description = 'Upload documentation to sites other than PyPi such as devpi'
user_options = [
('repository=', 'r',
@@ -45,9 +46,10 @@ class upload_docs(upload):
boolean_options = upload.boolean_options
def has_sphinx(self):
- if self.upload_dir is None:
- for ep in iter_entry_points('distutils.commands', 'build_sphinx'):
- return True
+ return bool(
+ self.upload_dir is None
+ and metadata.entry_points(group='distutils.commands', name='build_sphinx')
+ )
sub_commands = [('build_sphinx', has_sphinx)]
@@ -61,7 +63,7 @@ class upload_docs(upload):
if self.upload_dir is None:
if self.has_sphinx():
build_sphinx = self.get_finalized_command('build_sphinx')
- self.target_dir = build_sphinx.builder_target_dir
+ self.target_dir = dict(build_sphinx.builder_target_dirs)['html']
else:
build = self.get_finalized_command('build')
self.target_dir = os.path.join(build.build_base, 'docs')
@@ -69,7 +71,7 @@ class upload_docs(upload):
self.ensure_dirname('upload_dir')
self.target_dir = self.upload_dir
if 'pypi.python.org' in self.repository:
- log.warn("Upload_docs command is deprecated. Use RTD instead.")
+ log.warn("Upload_docs command is deprecated for PyPi. Use RTD instead.")
self.announce('Using upload directory %s' % self.target_dir)
def create_zipfile(self, filename):
@@ -89,6 +91,12 @@ class upload_docs(upload):
zip_file.close()
def run(self):
+ warnings.warn(
+ "upload_docs is deprecated and will be removed in a future "
+ "version. Use tools like httpie or curl instead.",
+ SetuptoolsDeprecationWarning,
+ )
+
# Run sub commands
for cmd_name in self.get_sub_commands():
self.run_command(cmd_name)
@@ -127,8 +135,8 @@ class upload_docs(upload):
"""
Build up the MIME payload for the POST data
"""
- boundary = b'--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
- sep_boundary = b'\n--' + boundary
+ boundary = '--------------GHSKFJDLGDS7543FJKLFHRE75642756743254'
+ sep_boundary = b'\n--' + boundary.encode('ascii')
end_boundary = sep_boundary + b'--'
end_items = end_boundary, b"\n",
builder = functools.partial(
@@ -138,7 +146,7 @@ class upload_docs(upload):
part_groups = map(builder, data.items())
parts = itertools.chain.from_iterable(part_groups)
body_items = itertools.chain(parts, end_items)
- content_type = 'multipart/form-data; boundary=%s' % boundary.decode('ascii')
+ content_type = 'multipart/form-data; boundary=%s' % boundary
return b''.join(body_items), content_type
def upload_file(self, filename):
@@ -152,9 +160,7 @@ class upload_docs(upload):
}
# set up the authentication
credentials = _encode(self.username + ':' + self.password)
- credentials = standard_b64encode(credentials)
- if six.PY3:
- credentials = credentials.decode('ascii')
+ credentials = standard_b64encode(credentials).decode('ascii')
auth = "Basic " + credentials
body, ct = self._build_multipart(data)
@@ -169,9 +175,9 @@ class upload_docs(upload):
urllib.parse.urlparse(self.repository)
assert not params and not query and not fragments
if schema == 'http':
- conn = http_client.HTTPConnection(netloc)
+ conn = http.client.HTTPConnection(netloc)
elif schema == 'https':
- conn = http_client.HTTPSConnection(netloc)
+ conn = http.client.HTTPSConnection(netloc)
else:
raise AssertionError("unsupported schema " + schema)