diff options
Diffstat (limited to 'setuptools')
-rw-r--r-- | setuptools/__init__.py | 6 | ||||
-rw-r--r-- | setuptools/_distutils/ccompiler.py | 2 | ||||
-rw-r--r-- | setuptools/_distutils/command/install.py | 26 | ||||
-rw-r--r-- | setuptools/_distutils/tests/test_unixccompiler.py | 7 | ||||
-rw-r--r-- | setuptools/build_meta.py | 13 | ||||
-rw-r--r-- | setuptools/command/build_py.py | 10 | ||||
-rw-r--r-- | setuptools/command/easy_install.py | 6 | ||||
-rw-r--r-- | setuptools/command/egg_info.py | 21 | ||||
-rw-r--r-- | setuptools/command/install.py | 7 | ||||
-rw-r--r-- | setuptools/command/sdist.py | 13 | ||||
-rw-r--r-- | setuptools/dist.py | 8 | ||||
-rw-r--r-- | setuptools/errors.py | 24 | ||||
-rw-r--r-- | setuptools/tests/test_distutils_adoption.py | 5 | ||||
-rw-r--r-- | setuptools/tests/test_sdist.py | 71 | ||||
-rw-r--r-- | setuptools/tests/test_virtualenv.py | 25 |
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', |