summaryrefslogtreecommitdiff
path: root/setuptools
diff options
context:
space:
mode:
Diffstat (limited to 'setuptools')
-rw-r--r--setuptools/__init__.py6
-rw-r--r--setuptools/_distutils/ccompiler.py2
-rw-r--r--setuptools/_distutils/command/install.py26
-rw-r--r--setuptools/_distutils/tests/test_unixccompiler.py7
-rw-r--r--setuptools/build_meta.py13
-rw-r--r--setuptools/command/build_py.py10
-rw-r--r--setuptools/command/easy_install.py6
-rw-r--r--setuptools/command/egg_info.py21
-rw-r--r--setuptools/command/install.py7
-rw-r--r--setuptools/command/sdist.py13
-rw-r--r--setuptools/dist.py8
-rw-r--r--setuptools/errors.py24
-rw-r--r--setuptools/tests/test_distutils_adoption.py5
-rw-r--r--setuptools/tests/test_sdist.py71
-rw-r--r--setuptools/tests/test_virtualenv.py25
15 files changed, 206 insertions, 38 deletions
diff --git a/setuptools/__init__.py b/setuptools/__init__.py
index 9d6f0bc0..a623262e 100644
--- a/setuptools/__init__.py
+++ b/setuptools/__init__.py
@@ -4,6 +4,7 @@ from fnmatch import fnmatchcase
import functools
import os
import re
+import warnings
import _distutils_hack.override # noqa: F401
@@ -144,6 +145,11 @@ def _install_setup_requires(attrs):
# Honor setup.cfg's options.
dist.parse_config_files(ignore_option_errors=True)
if dist.setup_requires:
+ warnings.warn(
+ "setup_requires is deprecated. Supply build "
+ "dependencies using PEP 517 pyproject.toml build-requires.",
+ SetuptoolsDeprecationWarning,
+ )
dist.fetch_build_eggs(dist.setup_requires)
diff --git a/setuptools/_distutils/ccompiler.py b/setuptools/_distutils/ccompiler.py
index 48d160d2..777fc661 100644
--- a/setuptools/_distutils/ccompiler.py
+++ b/setuptools/_distutils/ccompiler.py
@@ -802,7 +802,7 @@ int main (int argc, char **argv) {
except (LinkError, TypeError):
return False
else:
- os.remove("a.out")
+ os.remove(os.path.join(self.output_dir or '', "a.out"))
finally:
for fn in objects:
os.remove(fn)
diff --git a/setuptools/_distutils/command/install.py b/setuptools/_distutils/command/install.py
index 866e2d59..e98f0491 100644
--- a/setuptools/_distutils/command/install.py
+++ b/setuptools/_distutils/command/install.py
@@ -29,16 +29,16 @@ WINDOWS_SCHEME = {
INSTALL_SCHEMES = {
'unix_prefix': {
- 'purelib': '$base/lib/python$py_version_short/site-packages',
- 'platlib': '$platbase/$platlibdir/python$py_version_short/site-packages',
- 'headers': '$base/include/python$py_version_short$abiflags/$dist_name',
+ 'purelib': '$base/lib/$implementation_lower$py_version_short/site-packages',
+ 'platlib': '$platbase/$platlibdir/$implementation_lower$py_version_short/site-packages',
+ 'headers': '$base/include/$implementation_lower$py_version_short$abiflags/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
'unix_home': {
- 'purelib': '$base/lib/python',
- 'platlib': '$base/$platlibdir/python',
- 'headers': '$base/include/python/$dist_name',
+ 'purelib': '$base/lib/$implementation_lower',
+ 'platlib': '$base/$platlibdir/$implementation_lower',
+ 'headers': '$base/include/$implementation_lower/$dist_name',
'scripts': '$base/bin',
'data' : '$base',
},
@@ -64,8 +64,8 @@ if HAS_USER_SITE:
INSTALL_SCHEMES['nt_user'] = {
'purelib': '$usersite',
'platlib': '$usersite',
- 'headers': '$userbase/Python$py_version_nodot/Include/$dist_name',
- 'scripts': '$userbase/Python$py_version_nodot/Scripts',
+ 'headers': '$userbase/$implementation$py_version_nodot/Include/$dist_name',
+ 'scripts': '$userbase/$implementation$py_version_nodot/Scripts',
'data' : '$userbase',
}
@@ -73,7 +73,7 @@ if HAS_USER_SITE:
'purelib': '$usersite',
'platlib': '$usersite',
'headers':
- '$userbase/include/python$py_version_short$abiflags/$dist_name',
+ '$userbase/include/$implementation_lower$py_version_short$abiflags/$dist_name',
'scripts': '$userbase/bin',
'data' : '$userbase',
}
@@ -83,6 +83,12 @@ if HAS_USER_SITE:
# and to SCHEME_KEYS here.
SCHEME_KEYS = ('purelib', 'platlib', 'headers', 'scripts', 'data')
+def _get_implementation():
+ if hasattr(sys, 'pypy_version_info'):
+ return 'PyPy'
+ else:
+ return 'Python'
+
class install(Command):
@@ -313,6 +319,8 @@ class install(Command):
'exec_prefix': exec_prefix,
'abiflags': abiflags,
'platlibdir': getattr(sys, 'platlibdir', 'lib'),
+ 'implementation_lower': _get_implementation().lower(),
+ 'implementation': _get_implementation(),
}
if HAS_USER_SITE:
diff --git a/setuptools/_distutils/tests/test_unixccompiler.py b/setuptools/_distutils/tests/test_unixccompiler.py
index ee2fe99c..63c7dd37 100644
--- a/setuptools/_distutils/tests/test_unixccompiler.py
+++ b/setuptools/_distutils/tests/test_unixccompiler.py
@@ -232,6 +232,13 @@ class UnixCCompilerTestCase(unittest.TestCase):
sysconfig.customize_compiler(self.cc)
self.assertEqual(self.cc.linker_so[0], 'my_ld')
+ def test_has_function(self):
+ # Issue https://github.com/pypa/distutils/issues/64:
+ # ensure that setting output_dir does not raise
+ # FileNotFoundError: [Errno 2] No such file or directory: 'a.out'
+ self.cc.output_dir = 'scratch'
+ self.cc.has_function('abort', includes=['stdlib.h'])
+
def test_suite():
return unittest.makeSuite(UnixCCompilerTestCase)
diff --git a/setuptools/build_meta.py b/setuptools/build_meta.py
index 9dfb2f24..d0ac613b 100644
--- a/setuptools/build_meta.py
+++ b/setuptools/build_meta.py
@@ -33,6 +33,7 @@ import tokenize
import shutil
import contextlib
import tempfile
+import warnings
import setuptools
import distutils
@@ -118,6 +119,13 @@ def _open_setup_script(setup_script):
return getattr(tokenize, 'open', open)(setup_script)
+@contextlib.contextmanager
+def suppress_known_deprecation():
+ with warnings.catch_warnings():
+ warnings.filterwarnings('ignore', 'setup.py install is deprecated')
+ yield
+
+
class _BuildMetaBackend(object):
def _fix_config(self, config_settings):
@@ -218,8 +226,9 @@ class _BuildMetaBackend(object):
def build_wheel(self, wheel_directory, config_settings=None,
metadata_directory=None):
- return self._build_with_temp_dir(['bdist_wheel'], '.whl',
- wheel_directory, config_settings)
+ with suppress_known_deprecation():
+ return self._build_with_temp_dir(['bdist_wheel'], '.whl',
+ wheel_directory, config_settings)
def build_sdist(self, sdist_directory, config_settings=None):
return self._build_with_temp_dir(['sdist', '--formats', 'gztar'],
diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py
index 6a615433..c3fdc092 100644
--- a/setuptools/command/build_py.py
+++ b/setuptools/command/build_py.py
@@ -67,6 +67,16 @@ class build_py(orig.build_py):
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)
diff --git a/setuptools/command/easy_install.py b/setuptools/command/easy_install.py
index b88c3e9a..1aed0e87 100644
--- a/setuptools/command/easy_install.py
+++ b/setuptools/command/easy_install.py
@@ -153,6 +153,12 @@ class easy_install(Command):
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
diff --git a/setuptools/command/egg_info.py b/setuptools/command/egg_info.py
index 57bc7982..d7abfe38 100644
--- a/setuptools/command/egg_info.py
+++ b/setuptools/command/egg_info.py
@@ -608,6 +608,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
diff --git a/setuptools/command/install.py b/setuptools/command/install.py
index 72b9a3e4..35e54d20 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
diff --git a/setuptools/command/sdist.py b/setuptools/command/sdist.py
index e8062f2e..0285b690 100644
--- a/setuptools/command/sdist.py
+++ b/setuptools/command/sdist.py
@@ -114,12 +114,15 @@ class sdist(sdist_add_defaults, orig.sdist):
def _safe_data_files(self, build_py):
"""
- Extracting data_files from build_py is known to cause
- infinite recursion errors when `include_package_data`
- is enabled, so suppress it in that case.
+ 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.
"""
- if self.distribution.include_package_data:
- return ()
return build_py.data_files
def _add_data_files(self, data_files):
diff --git a/setuptools/dist.py b/setuptools/dist.py
index 8e2111a5..848d6b0f 100644
--- a/setuptools/dist.py
+++ b/setuptools/dist.py
@@ -145,11 +145,11 @@ def read_pkg_file(self, file):
def single_line(val):
- # quick and dirty validation for description pypa/setuptools#1390
+ """Validate that the value does not have line breaks."""
+ # Ref: https://github.com/pypa/setuptools/issues/1390
if '\n' in val:
- # TODO after 2021-07-31: Replace with `raise ValueError("newlines not allowed")`
- warnings.warn("newlines not allowed and will break in the future")
- val = val.replace('\n', ' ')
+ raise ValueError('Newlines are not allowed')
+
return val
diff --git a/setuptools/errors.py b/setuptools/errors.py
index 2701747f..f4d35a63 100644
--- a/setuptools/errors.py
+++ b/setuptools/errors.py
@@ -3,6 +3,7 @@
Provides exceptions used by setuptools modules.
"""
+from distutils import errors as _distutils_errors
from distutils.errors import DistutilsError
@@ -14,3 +15,26 @@ class RemovedCommandError(DistutilsError, RuntimeError):
error is raised if a command exists in ``distutils`` but has been actively
removed in ``setuptools``.
"""
+
+
+# Re-export errors from distutils to facilitate the migration to PEP632
+
+ByteCompileError = _distutils_errors.DistutilsByteCompileError
+CCompilerError = _distutils_errors.CCompilerError
+ClassError = _distutils_errors.DistutilsClassError
+CompileError = _distutils_errors.CompileError
+ExecError = _distutils_errors.DistutilsExecError
+FileError = _distutils_errors.DistutilsFileError
+InternalError = _distutils_errors.DistutilsInternalError
+LibError = _distutils_errors.LibError
+LinkError = _distutils_errors.LinkError
+ModuleError = _distutils_errors.DistutilsModuleError
+OptionError = _distutils_errors.DistutilsOptionError
+PlatformError = _distutils_errors.DistutilsPlatformError
+PreprocessError = _distutils_errors.PreprocessError
+SetupError = _distutils_errors.DistutilsSetupError
+TemplateError = _distutils_errors.DistutilsTemplateError
+UnknownFileError = _distutils_errors.UnknownFileError
+
+# The root error class in the hierarchy
+BaseError = _distutils_errors.DistutilsError
diff --git a/setuptools/tests/test_distutils_adoption.py b/setuptools/tests/test_distutils_adoption.py
index 0e89921c..b6b9c00e 100644
--- a/setuptools/tests/test_distutils_adoption.py
+++ b/setuptools/tests/test_distutils_adoption.py
@@ -14,6 +14,11 @@ IS_PYPY = '__pypy__' in sys.builtin_module_names
class VirtualEnv(jaraco.envs.VirtualEnv):
name = '.env'
+ # Some version of PyPy will import distutils on startup, implicitly
+ # importing setuptools, and thus leading to BackendInvalid errors
+ # when upgrading Setuptools. Bypass this behavior by avoiding the
+ # early availability and need to upgrade.
+ create_opts = ['--no-setuptools']
def run(self, cmd, *args, **kwargs):
cmd = [self.exe(cmd[0])] + cmd[1:]
diff --git a/setuptools/tests/test_sdist.py b/setuptools/tests/test_sdist.py
index 049fdcc0..66f46ad0 100644
--- a/setuptools/tests/test_sdist.py
+++ b/setuptools/tests/test_sdist.py
@@ -6,10 +6,12 @@ import tempfile
import unicodedata
import contextlib
import io
+from unittest import mock
import pytest
import pkg_resources
+from setuptools import SetuptoolsDeprecationWarning
from setuptools.command.sdist import sdist
from setuptools.command.egg_info import manifest_maker
from setuptools.dist import Distribution
@@ -106,6 +108,13 @@ class TestSdistTest:
with tmpdir.as_cwd():
yield
+ def assert_package_data_in_manifest(self, cmd):
+ manifest = cmd.filelist.files
+ assert os.path.join('sdist_test', 'a.txt') in manifest
+ assert os.path.join('sdist_test', 'b.txt') in manifest
+ assert os.path.join('sdist_test', 'c.rst') not in manifest
+ assert os.path.join('d', 'e.dat') in manifest
+
def test_package_data_in_sdist(self):
"""Regression test for pull request #4: ensures that files listed in
package_data are included in the manifest even if they're not added to
@@ -120,11 +129,63 @@ class TestSdistTest:
with quiet():
cmd.run()
- manifest = cmd.filelist.files
- assert os.path.join('sdist_test', 'a.txt') in manifest
- assert os.path.join('sdist_test', 'b.txt') in manifest
- assert os.path.join('sdist_test', 'c.rst') not in manifest
- assert os.path.join('d', 'e.dat') in manifest
+ self.assert_package_data_in_manifest(cmd)
+
+ def test_package_data_and_include_package_data_in_sdist(self):
+ """
+ Ensure package_data and include_package_data work
+ together.
+ """
+ setup_attrs = {**SETUP_ATTRS, 'include_package_data': True}
+ assert setup_attrs['package_data']
+
+ dist = Distribution(setup_attrs)
+ dist.script_name = 'setup.py'
+ cmd = sdist(dist)
+ cmd.ensure_finalized()
+
+ with quiet():
+ cmd.run()
+
+ self.assert_package_data_in_manifest(cmd)
+
+ def test_custom_build_py(self):
+ """
+ Ensure projects defining custom build_py don't break
+ when creating sdists (issue #2849)
+ """
+ from distutils.command.build_py import build_py as OrigBuildPy
+
+ using_custom_command_guard = mock.Mock()
+
+ class CustomBuildPy(OrigBuildPy):
+ """
+ Some projects have custom commands inheriting from `distutils`
+ """
+
+ def get_data_files(self):
+ using_custom_command_guard()
+ return super().get_data_files()
+
+ setup_attrs = {**SETUP_ATTRS, 'include_package_data': True}
+ assert setup_attrs['package_data']
+
+ dist = Distribution(setup_attrs)
+ dist.script_name = 'setup.py'
+ cmd = sdist(dist)
+ cmd.ensure_finalized()
+
+ # Make sure we use the custom command
+ cmd.cmdclass = {'build_py': CustomBuildPy}
+ cmd.distribution.cmdclass = {'build_py': CustomBuildPy}
+ assert cmd.distribution.get_command_class('build_py') == CustomBuildPy
+
+ msg = "setuptools instead of distutils"
+ with quiet(), pytest.warns(SetuptoolsDeprecationWarning, match=msg):
+ cmd.run()
+
+ using_custom_command_guard.assert_called()
+ self.assert_package_data_in_manifest(cmd)
def test_setup_py_exists(self):
dist = Distribution(SETUP_ATTRS)
diff --git a/setuptools/tests/test_virtualenv.py b/setuptools/tests/test_virtualenv.py
index 462e20c7..00f5f185 100644
--- a/setuptools/tests/test_virtualenv.py
+++ b/setuptools/tests/test_virtualenv.py
@@ -75,18 +75,15 @@ def _get_pip_versions():
def skip_network(param):
return param if network else mark(param, pytest.mark.skip(reason="no network"))
- issue2599 = pytest.mark.skipif(
- sys.version_info > (3, 10),
- reason="pypa/setuptools#2599",
- )
-
network_versions = [
- mark('pip==9.0.3', issue2599),
- mark('pip==10.0.1', issue2599),
- mark('pip==18.1', issue2599),
- mark('pip==19.3.1', pytest.mark.xfail(reason='pypa/pip#6599')),
- 'pip==20.0.2',
- 'https://github.com/pypa/pip/archive/main.zip',
+ mark('pip<20', pytest.mark.xfail(reason='pypa/pip#6599')),
+ 'pip<20.1',
+ 'pip<21',
+ 'pip<22',
+ mark(
+ 'https://github.com/pypa/pip/archive/main.zip',
+ pytest.mark.skipif('sys.version_info < (3, 7)'),
+ ),
]
versions = itertools.chain(
@@ -97,6 +94,10 @@ def _get_pip_versions():
return list(versions)
+@pytest.mark.skipif(
+ 'platform.python_implementation() == "PyPy"',
+ reason="https://github.com/pypa/setuptools/pull/2865#issuecomment-965834995",
+)
@pytest.mark.parametrize('pip_version', _get_pip_versions())
def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv):
"""
@@ -107,7 +108,7 @@ def test_pip_upgrade_from_source(pip_version, tmp_src, virtualenv):
if pip_version is None:
upgrade_pip = ()
else:
- upgrade_pip = ('python -m pip install -U {pip_version} --retries=1',)
+ upgrade_pip = ('python -m pip install -U "{pip_version}" --retries=1',)
virtualenv.run(' && '.join((
'pip uninstall -y setuptools',
'pip install -U wheel',