From 7718a97b8357db89a3dda12269e4a0094566fe11 Mon Sep 17 00:00:00 2001 From: JinWang An Date: Mon, 27 Mar 2023 17:02:35 +0900 Subject: [PATCH] Imported Upstream version 58.0.0 --- .bumpversion.cfg | 2 +- CHANGES.rst | 13 + docs/build_meta.rst | 6 +- docs/deprecated/distutils/apiref.rst | 18 - docs/deprecated/python3.rst | 94 ----- docs/references/keywords.rst | 15 - docs/userguide/declarative_config.rst | 4 - docs/userguide/dependency_management.rst | 23 +- docs/userguide/development_mode.rst | 8 - docs/userguide/keywords.rst | 12 - pytest.ini | 3 - setup.cfg | 7 +- setuptools/__init__.py | 43 +-- setuptools/_distutils/command/build_py.py | 26 +- .../_distutils/command/build_scripts.py | 10 +- setuptools/_distutils/util.py | 81 ---- setuptools/command/build_py.py | 44 +-- setuptools/command/develop.py | 47 +-- setuptools/command/test.py | 70 ++-- setuptools/config.py | 97 ++--- setuptools/dist.py | 168 +++++---- setuptools/lib2to3_ex.py | 68 ---- setuptools/sandbox.py | 86 +++-- setuptools/tests/__init__.py | 5 +- setuptools/tests/test_config.py | 353 ++++++------------ setuptools/tests/test_develop.py | 75 ++-- setuptools/tests/test_test.py | 101 +---- 27 files changed, 477 insertions(+), 1002 deletions(-) delete mode 100644 docs/deprecated/python3.rst delete mode 100644 setuptools/lib2to3_ex.py diff --git a/.bumpversion.cfg b/.bumpversion.cfg index a1bb0f9..1bfc882 100644 --- a/.bumpversion.cfg +++ b/.bumpversion.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 57.5.0 +current_version = 58.0.0 commit = True tag = True diff --git a/CHANGES.rst b/CHANGES.rst index 2abf009..20d8281 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,3 +1,16 @@ +v58.0.0 +------- + + +Breaking Changes +^^^^^^^^^^^^^^^^ +* #2086: Removed support for 2to3 during builds. Projects should port to a unified codebase or pin to an older version of Setuptools using PEP 518 build-requires. + +Documentation changes +^^^^^^^^^^^^^^^^^^^^^ +* #2746: add python_requires example + + v57.5.0 ------- diff --git a/docs/build_meta.rst b/docs/build_meta.rst index 006ac3f..27df70a 100644 --- a/docs/build_meta.rst +++ b/docs/build_meta.rst @@ -54,7 +54,11 @@ setuptools, the content would be:: [build-system] requires = ["setuptools", "wheel"] - build-backend = "setuptools.build_meta" + build-backend = "setuptools.build_meta" + +The ``setuptools`` package implements the ``build_sdist`` +command and the ``wheel`` package implements the ``build_wheel`` +command; both are required to be compliant with PEP 517. Use ``setuptools``' :ref:`declarative config ` to specify the package information:: diff --git a/docs/deprecated/distutils/apiref.rst b/docs/deprecated/distutils/apiref.rst index 40a360d..f721fc1 100644 --- a/docs/deprecated/distutils/apiref.rst +++ b/docs/deprecated/distutils/apiref.rst @@ -1941,24 +1941,6 @@ Subclasses of :class:`Command` must define the following methods. .. class:: build_py -.. class:: build_py_2to3 - - Alternative implementation of build_py which also runs the - 2to3 conversion library on each .py file that is going to be - installed. To use this in a setup.py file for a distribution - that is designed to run with both Python 2.x and 3.x, add:: - - try: - from distutils.command.build_py import build_py_2to3 as build_py - except ImportError: - from distutils.command.build_py import build_py - - to your setup.py, and later:: - - cmdclass = {'build_py': build_py} - - to the invocation of setup(). - :mod:`distutils.command.build_scripts` --- Build the scripts of a package ========================================================================= diff --git a/docs/deprecated/python3.rst b/docs/deprecated/python3.rst deleted file mode 100644 index 6b55fe7..0000000 --- a/docs/deprecated/python3.rst +++ /dev/null @@ -1,94 +0,0 @@ -===================================================== -Supporting both Python 2 and Python 3 with Setuptools -===================================================== - -Starting with Distribute version 0.6.2 and Setuptools 0.7, the Setuptools -project supported Python 3. Installing and -using setuptools for Python 3 code works exactly the same as for Python 2 -code. - -Setuptools provides a facility to invoke 2to3 on the code as a part of the -build process, by setting the keyword parameter ``use_2to3`` to True, but -the Setuptools project strongly recommends instead developing a unified codebase -using `six `_, -`future `_, or another compatibility -library. - - -Using 2to3 -========== - -Setuptools attempts to make the porting process easier by automatically -running -2to3 as a part of running tests. To do so, you need to configure the -setup.py so that you can run the unit tests with ``python setup.py test``. - -See :ref:`test` for more information on this. - -Once you have the tests running under Python 2, you can add the use_2to3 -keyword parameters to setup(), and start running the tests under Python 3. -The test command will now first run the build command during which the code -will be converted with 2to3, and the tests will then be run from the build -directory, as opposed from the source directory as is normally done. - -Setuptools will convert all Python files, and also all doctests in Python -files. However, if you have doctests located in separate text files, these -will not automatically be converted. By adding them to the -``convert_2to3_doctests`` keyword parameter Setuptools will convert them as -well. - -By default, the conversion uses all fixers in the ``lib2to3.fixers`` package. -To use additional fixers, the parameter ``use_2to3_fixers`` can be set -to a list of names of packages containing fixers. To exclude fixers, the -parameter ``use_2to3_exclude_fixers`` can be set to fixer names to be -skipped. - -An example setup.py might look something like this:: - - from setuptools import setup - - setup( - name='your.module', - version='1.0', - description='This is your awesome module', - author='You', - author_email='your@email', - package_dir={'': 'src'}, - packages=['your', 'you.module'], - test_suite='your.module.tests', - use_2to3=True, - convert_2to3_doctests=['src/your/module/README.txt'], - use_2to3_fixers=['your.fixers'], - use_2to3_exclude_fixers=['lib2to3.fixes.fix_import'], - ) - -Differential conversion ------------------------ - -Note that a file will only be copied and converted during the build process -if the source file has been changed. If you add a file to the doctests -that should be converted, it will not be converted the next time you run -the tests, since it hasn't been modified. You need to remove it from the -build directory. Also if you run the build, install or test commands before -adding the use_2to3 parameter, you will have to remove the build directory -before you run the test command, as the files otherwise will seem updated, -and no conversion will happen. - -In general, if code doesn't seem to be converted, deleting the build directory -and trying again is a good safeguard against the build directory getting -"out of sync" with the source directory. - -Distributing Python 3 modules -============================= - -You can distribute your modules with Python 3 support in different ways. A -normal source distribution will work, but can be slow in installing, as the -2to3 process will be run during the install. But you can also distribute -the module in binary format, such as a binary egg. That egg will contain the -already converted code, and hence no 2to3 conversion is needed during install. - -Advanced features -================= - -If you don't want to run the 2to3 conversion on the doctests in Python files, -you can turn that off by setting ``setuptools.use_2to3_on_doctests = False``. diff --git a/docs/references/keywords.rst b/docs/references/keywords.rst index 619b2d1..c26b9d4 100644 --- a/docs/references/keywords.rst +++ b/docs/references/keywords.rst @@ -330,21 +330,6 @@ Keywords mess with it. For more details on how this argument works, see the section below on :ref:`Automatic Resource Extraction`. -``use_2to3`` - Convert the source code from Python 2 to Python 3 with 2to3 during the - build process. See :doc:`../deprecated/python3` for more details. - -``convert_2to3_doctests`` - List of doctest source files that need to be converted with 2to3. - See :doc:`../deprecated/python3` for more details. - -``use_2to3_fixers`` - A list of modules to search for additional fixers to be used during - the 2to3 conversion. See :doc:`../deprecated/python3` for more details. - -``use_2to3_exclude_fixers`` - List of fixer names to be skipped. - ``project_urls`` An arbitrary map of URL names to hyperlinks, allowing more extensible documentation of where various resources can be found than the simple diff --git a/docs/userguide/declarative_config.rst b/docs/userguide/declarative_config.rst index 3b72611..c50fcf6 100644 --- a/docs/userguide/declarative_config.rst +++ b/docs/userguide/declarative_config.rst @@ -211,10 +211,6 @@ install_requires list-semi extras_require section [#opt-2]_ python_requires str entry_points file:, section 51.0.0 -use_2to3 bool -use_2to3_fixers list-comma -use_2to3_exclude_fixers list-comma -convert_2to3_doctests list-comma scripts list-comma eager_resources list-comma dependency_links list-comma diff --git a/docs/userguide/dependency_management.rst b/docs/userguide/dependency_management.rst index b97896b..23578a5 100644 --- a/docs/userguide/dependency_management.rst +++ b/docs/userguide/dependency_management.rst @@ -341,4 +341,25 @@ In some cases, you might need to specify the minimum required python version. This is handled with the ``python_requires`` keyword supplied to ``setup.cfg`` or ``setup.py``. -Example WIP + +.. tab:: setup.cfg + + .. code-block:: ini + + [metadata] + name = Project-B + #... + + [options] + #... + python_requires = >=3.6 + +.. tab:: setup.py + + .. code-block:: python + + setup( + name="Project-B", + python_requires=[">=3.6"], + ..., + ) diff --git a/docs/userguide/development_mode.rst b/docs/userguide/development_mode.rst index 3c477ec..90bc567 100644 --- a/docs/userguide/development_mode.rst +++ b/docs/userguide/development_mode.rst @@ -28,14 +28,6 @@ Python's ``site-packages`` directory, it will also update the ``easy-install.pth`` file to include your project's source code, thereby making it available on ``sys.path`` for all programs using that Python installation. -If you have enabled the ``use_2to3`` flag, then of course the ``.egg-link`` -will not link directly to your source code when run under Python 3, since -that source code would be made for Python 2 and not work under Python 3. -Instead the ``setup.py develop`` will build Python 3 code under the ``build`` -directory, and link there. This means that after doing code changes you will -have to run ``setup.py build`` before these changes are picked up by your -Python 3 installation. - In addition, the ``develop`` command creates wrapper scripts in the target script directory that will run your in-development scripts after ensuring that all your ``install_requires`` packages are available on ``sys.path``. diff --git a/docs/userguide/keywords.rst b/docs/userguide/keywords.rst index 268e4f4..5388ffe 100644 --- a/docs/userguide/keywords.rst +++ b/docs/userguide/keywords.rst @@ -157,18 +157,6 @@ unless you need the associated ``setuptools`` feature. mess with it. For more details on how this argument works, see the section below on :ref:`Automatic Resource Extraction`. -``use_2to3`` - Convert the source code from Python 2 to Python 3 with 2to3 during the - build process. See :doc:`../deprecated/python3` for more details. - -``convert_2to3_doctests`` - List of doctest source files that need to be converted with 2to3. - See :doc:`../deprecated/python3` for more details. - -``use_2to3_fixers`` - A list of modules to search for additional fixers to be used during - the 2to3 conversion. See :doc:`../deprecated/python3` for more details. - ``project_urls`` An arbitrary map of URL names to hyperlinks, allowing more extensible documentation of where various resources can be found than the simple diff --git a/pytest.ini b/pytest.ini index 307c553..eebb212 100644 --- a/pytest.ini +++ b/pytest.ini @@ -26,6 +26,3 @@ filterwarnings= ignore:Parent module 'setuptools' not found while handling absolute import:RuntimeWarning # Suppress use of bytes for filenames on Windows until fixed #2016 ignore:The Windows bytes API has been deprecated:DeprecationWarning - # https://github.com/pypa/setuptools/issues/2081 - ignore:lib2to3 package is deprecated:PendingDeprecationWarning - ignore:lib2to3 package is deprecated:DeprecationWarning diff --git a/setup.cfg b/setup.cfg index b1afa54..7de45d8 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,6 +1,6 @@ [metadata] name = setuptools -version = 57.5.0 +version = 58.0.0 author = Python Packaging Authority author_email = distutils-sig@python.org description = Easily download, build, install, upgrade, and uninstall Python packages @@ -106,7 +106,6 @@ distutils.commands = setuptools.finalize_distribution_options = parent_finalize = setuptools.dist:_Distribution.finalize_options keywords = setuptools.dist:Distribution._finalize_setup_keywords - 2to3_doctests = setuptools.dist:Distribution._finalize_2to3_doctests distutils.setup_keywords = eager_resources = setuptools.dist:assert_string_list namespace_packages = setuptools.dist:check_nsp @@ -125,10 +124,6 @@ distutils.setup_keywords = dependency_links = setuptools.dist:assert_string_list test_loader = setuptools.dist:check_importable test_runner = setuptools.dist:check_importable - use_2to3 = setuptools.dist:assert_bool - convert_2to3_doctests = setuptools.dist:assert_string_list - use_2to3_fixers = setuptools.dist:assert_string_list - use_2to3_exclude_fixers = setuptools.dist:assert_string_list egg_info.writers = PKG-INFO = setuptools.command.egg_info:write_pkg_info requires.txt = setuptools.command.egg_info:write_requirements diff --git a/setuptools/__init__.py b/setuptools/__init__.py index 4d9b835..9d6f0bc 100644 --- a/setuptools/__init__.py +++ b/setuptools/__init__.py @@ -21,21 +21,20 @@ from . import monkey __all__ = [ - 'setup', 'Distribution', 'Command', 'Extension', 'Require', + 'setup', + 'Distribution', + 'Command', + 'Extension', + 'Require', 'SetuptoolsDeprecationWarning', - 'find_packages', 'find_namespace_packages', + 'find_packages', + 'find_namespace_packages', ] __version__ = setuptools.version.__version__ bootstrap_install_from = None -# If we run 2to3 on .py files, should we also convert docstrings? -# Default: yes; assume that we can detect doctests reliably -run_2to3_on_doctests = True -# Standard package names for fixer packages -lib2to3_fixer_packages = ['lib2to3.fixes'] - class PackageFinder: """ @@ -60,10 +59,13 @@ class PackageFinder: shell style wildcard patterns just like 'exclude'. """ - return list(cls._find_packages_iter( - convert_path(where), - cls._build_filter('ez_setup', '*__pycache__', *exclude), - cls._build_filter(*include))) + return list( + cls._find_packages_iter( + convert_path(where), + cls._build_filter('ez_setup', '*__pycache__', *exclude), + cls._build_filter(*include), + ) + ) @classmethod def _find_packages_iter(cls, where, exclude, include): @@ -82,7 +84,7 @@ class PackageFinder: package = rel_path.replace(os.path.sep, '.') # Skip directory trees that are not valid packages - if ('.' in dir or not cls._looks_like_package(full_path)): + if '.' in dir or not cls._looks_like_package(full_path): continue # Should this package be included? @@ -125,12 +127,10 @@ def _install_setup_requires(attrs): A minimal version of a distribution for supporting the fetch_build_eggs interface. """ + def __init__(self, attrs): _incl = 'dependency_links', 'setup_requires' - filtered = { - k: attrs[k] - for k in set(_incl) & set(attrs) - } + filtered = {k: attrs[k] for k in set(_incl) & set(attrs)} distutils.core.Distribution.__init__(self, filtered) def finalize_options(self): @@ -178,8 +178,9 @@ class Command(_Command): setattr(self, option, default) return default elif not isinstance(val, str): - raise DistutilsOptionError("'%s' must be a %s (got `%s`)" - % (option, what, val)) + raise DistutilsOptionError( + "'%s' must be a %s (got `%s`)" % (option, what, val) + ) return val def ensure_string_list(self, option): @@ -200,8 +201,8 @@ class Command(_Command): ok = False if not ok: raise DistutilsOptionError( - "'%s' must be a list of strings (got %r)" - % (option, val)) + "'%s' must be a list of strings (got %r)" % (option, val) + ) def reinitialize_command(self, command, reinit_subcommands=0, **kw): cmd = _Command.reinitialize_command(self, command, reinit_subcommands) diff --git a/setuptools/_distutils/command/build_py.py b/setuptools/_distutils/command/build_py.py index edc2171..7ef9bce 100644 --- a/setuptools/_distutils/command/build_py.py +++ b/setuptools/_distutils/command/build_py.py @@ -9,7 +9,7 @@ import glob from distutils.core import Command from distutils.errors import * -from distutils.util import convert_path, Mixin2to3 +from distutils.util import convert_path from distutils import log class build_py (Command): @@ -390,27 +390,3 @@ class build_py (Command): if self.optimize > 0: byte_compile(files, optimize=self.optimize, force=self.force, prefix=prefix, dry_run=self.dry_run) - -class build_py_2to3(build_py, Mixin2to3): - def run(self): - self.updated_files = [] - - # Base class code - if self.py_modules: - self.build_modules() - if self.packages: - self.build_packages() - self.build_package_data() - - # 2to3 - self.run_2to3(self.updated_files) - - # Remaining base class code - self.byte_compile(self.get_outputs(include_bytecode=0)) - - def build_module(self, module, module_file, package): - res = build_py.build_module(self, module, module_file, package) - if res[1]: - # file was copied - self.updated_files.append(res[0]) - return res diff --git a/setuptools/_distutils/command/build_scripts.py b/setuptools/_distutils/command/build_scripts.py index ccc70e6..e3312cf 100644 --- a/setuptools/_distutils/command/build_scripts.py +++ b/setuptools/_distutils/command/build_scripts.py @@ -7,7 +7,7 @@ from stat import ST_MODE from distutils import sysconfig from distutils.core import Command from distutils.dep_util import newer -from distutils.util import convert_path, Mixin2to3 +from distutils.util import convert_path from distutils import log import tokenize @@ -150,11 +150,3 @@ class build_scripts(Command): os.chmod(file, newmode) # XXX should we modify self.outfiles? return outfiles, updated_files - -class build_scripts_2to3(build_scripts, Mixin2to3): - - def copy_scripts(self): - outfiles, updated_files = build_scripts.copy_scripts(self) - if not self.dry_run: - self.run_2to3(updated_files) - return outfiles, updated_files diff --git a/setuptools/_distutils/util.py b/setuptools/_distutils/util.py index 4232fd2..64f06dd 100644 --- a/setuptools/_distutils/util.py +++ b/setuptools/_distutils/util.py @@ -533,84 +533,3 @@ def rfc822_escape (header): lines = header.split('\n') sep = '\n' + 8 * ' ' return sep.join(lines) - -# 2to3 support - -def run_2to3(files, fixer_names=None, options=None, explicit=None): - """Invoke 2to3 on a list of Python files. - The files should all come from the build area, as the - modification is done in-place. To reduce the build time, - only files modified since the last invocation of this - function should be passed in the files argument.""" - - if not files: - return - - # Make this class local, to delay import of 2to3 - from lib2to3.refactor import RefactoringTool, get_fixers_from_package - class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - if fixer_names is None: - fixer_names = get_fixers_from_package('lib2to3.fixes') - r = DistutilsRefactoringTool(fixer_names, options=options) - r.refactor(files, write=True) - -def copydir_run_2to3(src, dest, template=None, fixer_names=None, - options=None, explicit=None): - """Recursively copy a directory, only copying new and changed files, - running run_2to3 over all newly copied Python modules afterward. - - If you give a template string, it's parsed like a MANIFEST.in. - """ - from distutils.dir_util import mkpath - from distutils.file_util import copy_file - from distutils.filelist import FileList - filelist = FileList() - curdir = os.getcwd() - os.chdir(src) - try: - filelist.findall() - finally: - os.chdir(curdir) - filelist.files[:] = filelist.allfiles - if template: - for line in template.splitlines(): - line = line.strip() - if not line: continue - filelist.process_template_line(line) - copied = [] - for filename in filelist.files: - outname = os.path.join(dest, filename) - mkpath(os.path.dirname(outname)) - res = copy_file(os.path.join(src, filename), outname, update=1) - if res[1]: copied.append(outname) - run_2to3([fn for fn in copied if fn.lower().endswith('.py')], - fixer_names=fixer_names, options=options, explicit=explicit) - return copied - -class Mixin2to3: - '''Mixin class for commands that run 2to3. - To configure 2to3, setup scripts may either change - the class variables, or inherit from individual commands - to override how 2to3 is invoked.''' - - # provide list of fixers to run; - # defaults to all from lib2to3.fixers - fixer_names = None - - # options dictionary - options = None - - # list of fixers to invoke even though they are marked as explicit - explicit = None - - def run_2to3(self, files): - return run_2to3(files, self.fixer_names, self.options, self.explicit) diff --git a/setuptools/command/build_py.py b/setuptools/command/build_py.py index df6fd32..6a61543 100644 --- a/setuptools/command/build_py.py +++ b/setuptools/command/build_py.py @@ -10,20 +10,12 @@ import itertools import stat from setuptools.extern.more_itertools import unique_everseen -try: - from setuptools.lib2to3_ex import Mixin2to3 -except Exception: - - class Mixin2to3: - def run_2to3(self, files, doctests=True): - "do nothing" - def make_writable(target): os.chmod(target, os.stat(target).st_mode | stat.S_IWRITE) -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()'. @@ -36,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""" @@ -55,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)) @@ -71,8 +57,7 @@ class build_py(orig.build_py, Mixin2to3): return orig.build_py.__getattr__(self, attr) def build_module(self, module, module_file, package): - 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 @@ -123,9 +108,6 @@ class build_py(orig.build_py, Mixin2to3): 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 = {} @@ -202,18 +184,11 @@ 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)) @@ -241,12 +216,17 @@ def assert_relative(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 faf8c98..24fb0a7 100644 --- a/setuptools/command/develop.py +++ b/setuptools/command/develop.py @@ -63,7 +63,8 @@ class develop(namespaces.DevelopInstaller, easy_install): target = pkg_resources.normalize_path(self.egg_base) egg_path = pkg_resources.normalize_path( - os.path.join(self.install_dir, self.egg_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" @@ -74,7 +75,7 @@ class develop(namespaces.DevelopInstaller, easy_install): self.dist = pkg_resources.Distribution( target, pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)), - project_name=ei.egg_name + project_name=ei.egg_name, ) self.setup_path = self._resolve_setup_path( @@ -99,41 +100,18 @@ class develop(namespaces.DevelopInstaller, easy_install): if resolved != pkg_resources.normalize_path(os.curdir): raise DistutilsOptionError( "Can't get a consistent path to setup script from" - " installation directory", resolved, - pkg_resources.normalize_path(os.curdir)) + " installation directory", + resolved, + pkg_resources.normalize_path(os.curdir), + ) return path_to_setup def install_for_development(self): - if 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 = pkg_resources.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 = pkg_resources.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') if setuptools.bootstrap_install_from: self.easy_install(setuptools.bootstrap_install_from) @@ -156,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: diff --git a/setuptools/command/test.py b/setuptools/command/test.py index de4f3d1..4a389e4 100644 --- a/setuptools/command/test.py +++ b/setuptools/command/test.py @@ -8,15 +8,21 @@ from distutils.errors import DistutilsError, DistutilsOptionError from distutils import log from unittest import TestLoader -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, + EntryPoint, +) from setuptools import Command from setuptools.extern.more_itertools import unique_everseen class ScanningLoader(TestLoader): - def __init__(self): TestLoader.__init__(self) self._visited = set() @@ -73,8 +79,11 @@ class test(Command): 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"), ] @@ -124,30 +133,11 @@ class test(Command): @contextlib.contextmanager def project_on_sys_path(self, include_dists=[]): - with_2to3 = 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") @@ -203,7 +193,8 @@ 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) @@ -232,23 +223,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 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, diff --git a/setuptools/config.py b/setuptools/config.py index 7ab6bfd..e3e44c2 100644 --- a/setuptools/config.py +++ b/setuptools/config.py @@ -21,6 +21,7 @@ class StaticModule: """ Attempt to load the module by the name """ + def __init__(self, name): spec = importlib.util.find_spec(name) with open(spec.origin) as strm: @@ -56,8 +57,7 @@ def patch_path(path): sys.path.remove(path) -def read_configuration( - filepath, find_others=False, ignore_option_errors=False): +def read_configuration(filepath, find_others=False, ignore_option_errors=False): """Read given configuration file and returns options from it as a dict. :param str|unicode filepath: Path to configuration file @@ -78,8 +78,7 @@ def read_configuration( filepath = os.path.abspath(filepath) if not os.path.isfile(filepath): - raise DistutilsFileError( - 'Configuration file %s does not exist.' % filepath) + raise DistutilsFileError('Configuration file %s does not exist.' % filepath) current_directory = os.getcwd() os.chdir(os.path.dirname(filepath)) @@ -94,8 +93,8 @@ def read_configuration( _Distribution.parse_config_files(dist, filenames=filenames) handlers = parse_configuration( - dist, dist.command_options, - ignore_option_errors=ignore_option_errors) + dist, dist.command_options, ignore_option_errors=ignore_option_errors + ) finally: os.chdir(current_directory) @@ -133,8 +132,7 @@ def configuration_to_dict(handlers): return config_dict -def parse_configuration( - distribution, command_options, ignore_option_errors=False): +def parse_configuration(distribution, command_options, ignore_option_errors=False): """Performs additional parsing of configuration options for a distribution. @@ -148,13 +146,15 @@ def parse_configuration( If False exceptions are propagated as expected. :rtype: list """ - options = ConfigOptionsHandler( - distribution, command_options, ignore_option_errors) + options = ConfigOptionsHandler(distribution, command_options, ignore_option_errors) options.parse() meta = ConfigMetadataHandler( - distribution.metadata, command_options, ignore_option_errors, - distribution.package_dir) + distribution.metadata, + command_options, + ignore_option_errors, + distribution.package_dir, + ) meta.parse() return meta, options @@ -196,7 +196,8 @@ class ConfigHandler: def parsers(self): """Metadata item name to parser function mapping.""" raise NotImplementedError( - '%s must provide .parsers property' % self.__class__.__name__) + '%s must provide .parsers property' % self.__class__.__name__ + ) def __setitem__(self, option_name, value): unknown = tuple() @@ -297,7 +298,8 @@ class ConfigHandler: key, sep, val = line.partition(separator) if sep != separator: raise DistutilsOptionError( - 'Unable to parse option value to dict: %s' % value) + 'Unable to parse option value to dict: %s' % value + ) result[key.strip()] = val.strip() return result @@ -323,13 +325,16 @@ class ConfigHandler: :param key: :rtype: callable """ + def parser(value): exclude_directive = 'file:' if value.startswith(exclude_directive): raise ValueError( 'Only strings are accepted for the {0} field, ' - 'files are not accepted'.format(key)) + 'files are not accepted'.format(key) + ) return value + return parser @classmethod @@ -354,20 +359,18 @@ class ConfigHandler: if not value.startswith(include_directive): return value - spec = value[len(include_directive):] + spec = value[len(include_directive) :] filepaths = (os.path.abspath(path.strip()) for path in spec.split(',')) return '\n'.join( cls._read_file(path) for path in filepaths - if (cls._assert_local(path) or True) - and os.path.isfile(path) + if (cls._assert_local(path) or True) and os.path.isfile(path) ) @staticmethod def _assert_local(filepath): if not filepath.startswith(os.getcwd()): - raise DistutilsOptionError( - '`file:` directive can not access %s' % filepath) + raise DistutilsOptionError('`file:` directive can not access %s' % filepath) @staticmethod def _read_file(filepath): @@ -429,6 +432,7 @@ class ConfigHandler: :param parse_methods: :rtype: callable """ + def parse(value): parsed = value @@ -482,22 +486,25 @@ class ConfigHandler: self, # Dots in section names are translated into dunderscores. ('parse_section%s' % method_postfix).replace('.', '__'), - None) + None, + ) if section_parser_method is None: raise DistutilsOptionError( - 'Unsupported distribution option section: [%s.%s]' % ( - self.section_prefix, section_name)) + 'Unsupported distribution option section: [%s.%s]' + % (self.section_prefix, section_name) + ) section_parser_method(section_options) def _deprecated_config_handler(self, func, msg, warning_class): - """ this function will wrap around parameters that are deprecated + """this function will wrap around parameters that are deprecated :param msg: deprecation message :param warning_class: class of warning exception to be raised :param func: function to be wrapped around """ + @wraps(func) def config_handler(*args, **kwargs): warnings.warn(msg, warning_class) @@ -523,10 +530,12 @@ class ConfigMetadataHandler(ConfigHandler): """ - def __init__(self, target_obj, options, ignore_option_errors=False, - package_dir=None): - super(ConfigMetadataHandler, self).__init__(target_obj, options, - ignore_option_errors) + def __init__( + self, target_obj, options, ignore_option_errors=False, package_dir=None + ): + super(ConfigMetadataHandler, self).__init__( + target_obj, options, ignore_option_errors + ) self.package_dir = package_dir @property @@ -545,7 +554,8 @@ class ConfigMetadataHandler(ConfigHandler): parse_list, "The requires parameter is deprecated, please use " "install_requires for runtime dependencies.", - DeprecationWarning), + DeprecationWarning, + ), 'obsoletes': parse_list, 'classifiers': self._get_parser_compound(parse_file, parse_list), 'license': exclude_files_parser('license'), @@ -553,7 +563,8 @@ class ConfigMetadataHandler(ConfigHandler): exclude_files_parser('license_file'), "The license_file parameter is deprecated, " "use license_files instead.", - DeprecationWarning), + DeprecationWarning, + ), 'license_files': parse_list, 'description': parse_file, 'long_description': parse_file, @@ -612,12 +623,8 @@ class ConfigOptionsHandler(ConfigHandler): return { 'zip_safe': parse_bool, - 'use_2to3': parse_bool, 'include_package_data': parse_bool, 'package_dir': parse_dict, - 'use_2to3_fixers': parse_list, - 'use_2to3_exclude_fixers': parse_list, - 'convert_2to3_doctests': parse_list, 'scripts': parse_list, 'eager_resources': parse_list, 'dependency_links': parse_list, @@ -635,17 +642,14 @@ class ConfigOptionsHandler(ConfigHandler): def _parse_cmdclass(self, value): def resolve_class(qualified_class_name): idx = qualified_class_name.rfind('.') - class_name = qualified_class_name[idx+1:] + class_name = qualified_class_name[idx + 1 :] pkg_name = qualified_class_name[:idx] module = __import__(pkg_name) return getattr(module, class_name) - return { - k: resolve_class(v) - for k, v in self._parse_dict(value).items() - } + return {k: resolve_class(v) for k, v in self._parse_dict(value).items()} def _parse_packages(self, value): """Parses `packages` option value. @@ -663,7 +667,8 @@ class ConfigOptionsHandler(ConfigHandler): # Read function arguments from a dedicated section. find_kwargs = self.parse_section_packages__find( - self.sections.get('packages.find', {})) + self.sections.get('packages.find', {}) + ) if findns: from setuptools import find_namespace_packages as find_packages @@ -679,13 +684,13 @@ class ConfigOptionsHandler(ConfigHandler): :param dict section_options: """ - section_data = self._parse_section_to_dict( - section_options, self._parse_list) + section_data = self._parse_section_to_dict(section_options, self._parse_list) valid_keys = ['where', 'include', 'exclude'] find_kwargs = dict( - [(k, v) for k, v in section_data.items() if k in valid_keys and v]) + [(k, v) for k, v in section_data.items() if k in valid_keys and v] + ) where = find_kwargs.get('where') if where is not None: @@ -723,8 +728,7 @@ class ConfigOptionsHandler(ConfigHandler): :param dict section_options: """ - self['exclude_package_data'] = self._parse_package_data( - section_options) + self['exclude_package_data'] = self._parse_package_data(section_options) def parse_section_extras_require(self, section_options): """Parses `extras_require` configuration file section. @@ -733,7 +737,8 @@ class ConfigOptionsHandler(ConfigHandler): """ parse_list = partial(self._parse_list, separator=';') self['extras_require'] = self._parse_section_to_dict( - section_options, parse_list) + section_options, parse_list + ) def parse_section_data_files(self, section_options): """Parses `data_files` configuration file section. diff --git a/setuptools/dist.py b/setuptools/dist.py index df071c1..eec0b27 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -65,9 +65,7 @@ def rfc822_unescape(content: str) -> str: lines = content.splitlines() if len(lines) == 1: return lines[0].lstrip() - return '\n'.join( - (lines[0].lstrip(), - textwrap.dedent('\n'.join(lines[1:])))) + return '\n'.join((lines[0].lstrip(), textwrap.dedent('\n'.join(lines[1:])))) def _read_field_from_msg(msg: "Message", field: str) -> Optional[str]: @@ -157,8 +155,7 @@ def single_line(val): # Based on Python 3.5 version def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME - """Write the PKG-INFO format data to a file object. - """ + """Write the PKG-INFO format data to a file object.""" version = self.get_metadata_version() def write_field(key, value): @@ -209,10 +206,7 @@ def write_pkg_file(self, file): # noqa: C901 # is too complex (14) # FIXME # PEP 566 if self.long_description_content_type: - write_field( - 'Description-Content-Type', - self.long_description_content_type - ) + write_field('Description-Content-Type', self.long_description_content_type) if self.provides_extras: for extra in self.provides_extras: write_field('Provides-Extra', extra) @@ -231,8 +225,7 @@ def check_importable(dist, attr, value): assert not ep.extras except (TypeError, ValueError, AttributeError, AssertionError) as e: raise DistutilsSetupError( - "%r must be importable 'module:attrs' string (got %r)" - % (attr, value) + "%r must be importable 'module:attrs' string (got %r)" % (attr, value) ) from e @@ -257,14 +250,16 @@ def check_nsp(dist, attr, value): for nsp in ns_packages: if not dist.has_contents_for(nsp): raise DistutilsSetupError( - "Distribution contains no modules or packages for " + - "namespace package %r" % nsp + "Distribution contains no modules or packages for " + + "namespace package %r" % nsp ) parent, sep, child = nsp.rpartition('.') if parent and parent not in ns_packages: distutils.log.warn( "WARNING: %r is declared as a package namespace, but %r" - " is not: please correct this in setup.py", nsp, parent + " is not: please correct this in setup.py", + nsp, + parent, ) @@ -305,9 +300,7 @@ def check_requirements(dist, attr, value): "{attr!r} must be a string or list of strings " "containing valid project/version requirement specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_specifier(dist, attr, value): @@ -316,12 +309,9 @@ def check_specifier(dist, attr, value): packaging.specifiers.SpecifierSet(value) except (packaging.specifiers.InvalidSpecifier, AttributeError) as error: tmpl = ( - "{attr!r} must be a string " - "containing valid version specifiers; {error}" + "{attr!r} must be a string " "containing valid version specifiers; {error}" ) - raise DistutilsSetupError( - tmpl.format(attr=attr, error=error) - ) from error + raise DistutilsSetupError(tmpl.format(attr=attr, error=error)) from error def check_entry_points(dist, attr, value): @@ -342,12 +332,12 @@ def check_package_data(dist, attr, value): if not isinstance(value, dict): raise DistutilsSetupError( "{!r} must be a dictionary mapping package names to lists of " - "string wildcard patterns".format(attr)) + "string wildcard patterns".format(attr) + ) for k, v in value.items(): if not isinstance(k, str): raise DistutilsSetupError( - "keys of {!r} dict must be strings (got {!r})" - .format(attr, k) + "keys of {!r} dict must be strings (got {!r})".format(attr, k) ) assert_string_list(dist, 'values of {!r} dict'.format(attr), v) @@ -357,7 +347,8 @@ def check_packages(dist, attr, value): if not re.match(r'\w+(\.\w+)*', pkgname): distutils.log.warn( "WARNING: %r not a valid package name; please use only " - ".-separated package names in setup.py", pkgname + ".-separated package names in setup.py", + pkgname, ) @@ -452,15 +443,20 @@ class Distribution(_Distribution): self.setup_requires = attrs.pop('setup_requires', []) for ep in pkg_resources.iter_entry_points('distutils.setup_keywords'): vars(self).setdefault(ep.name, None) - _Distribution.__init__(self, { - k: v for k, v in attrs.items() - if k not in self._DISTUTILS_UNSUPPORTED_METADATA - }) + _Distribution.__init__( + self, + { + k: v + for k, v in attrs.items() + if k not in self._DISTUTILS_UNSUPPORTED_METADATA + }, + ) self._set_metadata_defaults(attrs) self.metadata.version = self._normalize_version( - self._validate_version(self.metadata.version)) + self._validate_version(self.metadata.version) + ) self._finalize_requires() def _set_metadata_defaults(self, attrs): @@ -596,7 +592,8 @@ class Distribution(_Distribution): patterns = ('LICEN[CS]E*', 'COPYING*', 'NOTICE*', 'AUTHORS*') self.metadata.license_files = list( - unique_everseen(self._expand_patterns(patterns))) + unique_everseen(self._expand_patterns(patterns)) + ) @staticmethod def _expand_patterns(patterns): @@ -610,8 +607,7 @@ class Distribution(_Distribution): path for pattern in patterns for path in sorted(iglob(pattern)) - if not path.endswith('~') - and os.path.isfile(path) + if not path.endswith('~') and os.path.isfile(path) ) # FIXME: 'Distribution._parse_config_files' is too complex (14) @@ -624,12 +620,25 @@ class Distribution(_Distribution): from configparser import ConfigParser # Ignore install directory options if we have a venv - ignore_options = [] if sys.prefix == sys.base_prefix else [ - 'install-base', 'install-platbase', 'install-lib', - 'install-platlib', 'install-purelib', 'install-headers', - 'install-scripts', 'install-data', 'prefix', 'exec-prefix', - 'home', 'user', 'root', - ] + ignore_options = ( + [] + if sys.prefix == sys.base_prefix + else [ + 'install-base', + 'install-platbase', + 'install-lib', + 'install-platlib', + 'install-purelib', + 'install-headers', + 'install-scripts', + 'install-data', + 'prefix', + 'exec-prefix', + 'home', + 'user', + 'root', + ] + ) ignore_options = frozenset(ignore_options) @@ -683,21 +692,26 @@ class Distribution(_Distribution): def warn_dash_deprecation(self, opt, section): if section in ( - 'options.extras_require', 'options.data_files', + 'options.extras_require', + 'options.data_files', ): return opt underscore_opt = opt.replace('-', '_') commands = distutils.command.__all__ + self._setuptools_commands() - if (not section.startswith('options') and section != 'metadata' - and section not in commands): + if ( + not section.startswith('options') + and section != 'metadata' + and section not in commands + ): return underscore_opt if '-' in opt: warnings.warn( "Usage of dash-separated '%s' will not be supported in future " "versions. Please use the underscore name '%s' instead" - % (opt, underscore_opt)) + % (opt, underscore_opt) + ) return underscore_opt def _setuptools_commands(self): @@ -741,11 +755,9 @@ class Distribution(_Distribution): self.announce(" setting options for '%s' command:" % command_name) for (option, (source, value)) in option_dict.items(): if DEBUG: - self.announce(" %s = %s (from %s)" % (option, value, - source)) + self.announce(" %s = %s (from %s)" % (option, value, source)) try: - bool_opts = [translate_longopt(o) - for o in command_obj.boolean_options] + bool_opts = [translate_longopt(o) for o in command_obj.boolean_options] except AttributeError: bool_opts = [] try: @@ -764,7 +776,8 @@ class Distribution(_Distribution): else: raise DistutilsOptionError( "error in %s: command '%s' has no such option '%s'" - % (source, command_name, option)) + % (source, command_name, option) + ) except ValueError as e: raise DistutilsOptionError(e) from e @@ -775,8 +788,9 @@ class Distribution(_Distribution): """ self._parse_config_files(filenames=filenames) - parse_configuration(self, self.command_options, - ignore_option_errors=ignore_option_errors) + parse_configuration( + self, self.command_options, ignore_option_errors=ignore_option_errors + ) self._finalize_requires() self._finalize_license_files() @@ -802,6 +816,7 @@ class Distribution(_Distribution): def by_order(hook): return getattr(hook, 'order', 0) + eps = map(lambda e: e.load(), pkg_resources.iter_entry_points(group)) for ep in sorted(eps, key=by_order): ep(self) @@ -813,16 +828,6 @@ class Distribution(_Distribution): ep.require(installer=self.fetch_build_egg) ep.load()(self, ep.name, value) - def _finalize_2to3_doctests(self): - if getattr(self, 'convert_2to3_doctests', None): - # XXX may convert to set here when we can rely on set being builtin - self.convert_2to3_doctests = [ - os.path.abspath(p) - for p in self.convert_2to3_doctests - ] - else: - self.convert_2to3_doctests = [] - def get_egg_cache_dir(self): egg_cache_dir = os.path.join(os.curdir, '.eggs') if not os.path.exists(egg_cache_dir): @@ -830,10 +835,14 @@ class Distribution(_Distribution): windows_support.hide_file(egg_cache_dir) readme_txt_filename = os.path.join(egg_cache_dir, 'README.txt') with open(readme_txt_filename, 'w') as f: - f.write('This directory contains eggs that were downloaded ' - 'by setuptools to build, test, and run plug-ins.\n\n') - f.write('This directory caches those eggs to prevent ' - 'repeated downloads.\n\n') + f.write( + 'This directory contains eggs that were downloaded ' + 'by setuptools to build, test, and run plug-ins.\n\n' + ) + f.write( + 'This directory caches those eggs to prevent ' + 'repeated downloads.\n\n' + ) f.write('However, it is safe to delete this directory.\n\n') return egg_cache_dir @@ -841,6 +850,7 @@ class Distribution(_Distribution): def fetch_build_egg(self, req): """Fetch an egg needed for building""" from setuptools.installer import fetch_build_egg + return fetch_build_egg(self, req) def get_command_class(self, command): @@ -900,19 +910,18 @@ class Distribution(_Distribution): pfx = package + '.' if self.packages: self.packages = [ - p for p in self.packages - if p != package and not p.startswith(pfx) + p for p in self.packages if p != package and not p.startswith(pfx) ] if self.py_modules: self.py_modules = [ - p for p in self.py_modules - if p != package and not p.startswith(pfx) + p for p in self.py_modules if p != package and not p.startswith(pfx) ] if self.ext_modules: self.ext_modules = [ - p for p in self.ext_modules + p + for p in self.ext_modules if p.name != package and not p.name.startswith(pfx) ] @@ -934,9 +943,7 @@ class Distribution(_Distribution): try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is not None and not isinstance(old, sequence): raise DistutilsSetupError( name + ": this setting cannot be changed via include/exclude" @@ -948,15 +955,11 @@ class Distribution(_Distribution): """Handle 'include()' for list/tuple attrs without a special handler""" if not isinstance(value, sequence): - raise DistutilsSetupError( - "%s: setting must be a list (%r)" % (name, value) - ) + raise DistutilsSetupError("%s: setting must be a list (%r)" % (name, value)) try: old = getattr(self, name) except AttributeError as e: - raise DistutilsSetupError( - "%s: No such distribution setting" % name - ) from e + raise DistutilsSetupError("%s: No such distribution setting" % name) from e if old is None: setattr(self, name, value) elif not isinstance(old, sequence): @@ -1009,6 +1012,7 @@ class Distribution(_Distribution): src, alias = aliases[command] del aliases[command] # ensure each alias can expand only once! import shlex + args[:1] = shlex.split(alias, True) command = args[0] @@ -1108,12 +1112,14 @@ class Distribution(_Distribution): line_buffering = sys.stdout.line_buffering sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), 'utf-8', errors, newline, line_buffering) + sys.stdout.detach(), 'utf-8', errors, newline, line_buffering + ) try: return _Distribution.handle_display_options(self, option_order) finally: sys.stdout = io.TextIOWrapper( - sys.stdout.detach(), encoding, errors, newline, line_buffering) + sys.stdout.detach(), encoding, errors, newline, line_buffering + ) class DistDeprecationWarning(SetuptoolsDeprecationWarning): diff --git a/setuptools/lib2to3_ex.py b/setuptools/lib2to3_ex.py deleted file mode 100644 index c176abf..0000000 --- a/setuptools/lib2to3_ex.py +++ /dev/null @@ -1,68 +0,0 @@ -""" -Customized Mixin2to3 support: - - - adds support for converting doctests -""" - -import warnings -from distutils.util import Mixin2to3 as _Mixin2to3 -from distutils import log -from lib2to3.refactor import RefactoringTool, get_fixers_from_package - -import setuptools -from ._deprecation_warning import SetuptoolsDeprecationWarning - - -class DistutilsRefactoringTool(RefactoringTool): - def log_error(self, msg, *args, **kw): - log.error(msg, *args) - - def log_message(self, msg, *args): - log.info(msg, *args) - - def log_debug(self, msg, *args): - log.debug(msg, *args) - - -class Mixin2to3(_Mixin2to3): - def run_2to3(self, files, doctests=False): - # See of the distribution option has been set, otherwise check the - # setuptools default. - if self.distribution.use_2to3 is not True: - return - if not files: - return - - warnings.warn( - "2to3 support is deprecated. If the project still " - "requires Python 2 support, please migrate to " - "a single-codebase solution or employ an " - "independent conversion process.", - SetuptoolsDeprecationWarning) - log.info("Fixing " + " ".join(files)) - self.__build_fixer_names() - self.__exclude_fixers() - if doctests: - if setuptools.run_2to3_on_doctests: - r = DistutilsRefactoringTool(self.fixer_names) - r.refactor(files, write=True, doctests_only=True) - else: - _Mixin2to3.run_2to3(self, files) - - def __build_fixer_names(self): - if self.fixer_names: - return - self.fixer_names = [] - for p in setuptools.lib2to3_fixer_packages: - self.fixer_names.extend(get_fixers_from_package(p)) - if self.distribution.use_2to3_fixers is not None: - for p in self.distribution.use_2to3_fixers: - self.fixer_names.extend(get_fixers_from_package(p)) - - def __exclude_fixers(self): - excluded_fixers = getattr(self, 'exclude_fixers', []) - if self.distribution.use_2to3_exclude_fixers is not None: - excluded_fixers.extend(self.distribution.use_2to3_exclude_fixers) - for fixer_name in excluded_fixers: - if fixer_name in self.fixer_names: - self.fixer_names.remove(fixer_name) diff --git a/setuptools/sandbox.py b/setuptools/sandbox.py index 91b960d..034fc80 100644 --- a/setuptools/sandbox.py +++ b/setuptools/sandbox.py @@ -26,7 +26,10 @@ _open = open __all__ = [ - "AbstractSandbox", "DirectorySandbox", "SandboxViolation", "run_setup", + "AbstractSandbox", + "DirectorySandbox", + "SandboxViolation", + "run_setup", ] @@ -106,6 +109,7 @@ class UnpickleableException(Exception): except Exception: # get UnpickleableException inside the sandbox from setuptools.sandbox import UnpickleableException as cls + return cls.dump(cls, cls(repr(exc))) @@ -154,7 +158,8 @@ def save_modules(): sys.modules.update(saved) # remove any modules imported since del_modules = ( - mod_name for mod_name in sys.modules + mod_name + for mod_name in sys.modules if mod_name not in saved # exclude any encodings modules. See #285 and not mod_name.startswith('encodings.') @@ -265,7 +270,8 @@ class AbstractSandbox: def __init__(self): self._attrs = [ - name for name in dir(_os) + name + for name in dir(_os) if not name.startswith('_') and hasattr(self, name) ] @@ -320,9 +326,25 @@ class AbstractSandbox: _file = _mk_single_path_wrapper('file', _file) _open = _mk_single_path_wrapper('open', _open) for name in [ - "stat", "listdir", "chdir", "open", "chmod", "chown", "mkdir", - "remove", "unlink", "rmdir", "utime", "lchown", "chroot", "lstat", - "startfile", "mkfifo", "mknod", "pathconf", "access" + "stat", + "listdir", + "chdir", + "open", + "chmod", + "chown", + "mkdir", + "remove", + "unlink", + "rmdir", + "utime", + "lchown", + "chroot", + "lstat", + "startfile", + "mkfifo", + "mknod", + "pathconf", + "access", ]: if hasattr(_os, name): locals()[name] = _mk_single_path_wrapper(name) @@ -373,7 +395,7 @@ class AbstractSandbox: """Called for path pairs like rename, link, and symlink operations""" return ( self._remap_input(operation + '-from', src, *args, **kw), - self._remap_input(operation + '-to', dst, *args, **kw) + self._remap_input(operation + '-to', dst, *args, **kw), ) @@ -386,28 +408,38 @@ else: class DirectorySandbox(AbstractSandbox): """Restrict operations to a single subdirectory - pseudo-chroot""" - write_ops = dict.fromkeys([ - "open", "chmod", "chown", "mkdir", "remove", "unlink", "rmdir", - "utime", "lchown", "chroot", "mkfifo", "mknod", "tempnam", - ]) + write_ops = dict.fromkeys( + [ + "open", + "chmod", + "chown", + "mkdir", + "remove", + "unlink", + "rmdir", + "utime", + "lchown", + "chroot", + "mkfifo", + "mknod", + "tempnam", + ] + ) - _exception_patterns = [ - # Allow lib2to3 to attempt to save a pickled grammar object (#121) - r'.*lib2to3.*\.pickle$', - ] + _exception_patterns = [] "exempt writing to paths that match the pattern" def __init__(self, sandbox, exceptions=_EXCEPTIONS): self._sandbox = os.path.normcase(os.path.realpath(sandbox)) self._prefix = os.path.join(self._sandbox, '') self._exceptions = [ - os.path.normcase(os.path.realpath(path)) - for path in exceptions + os.path.normcase(os.path.realpath(path)) for path in exceptions ] AbstractSandbox.__init__(self) def _violation(self, operation, *args, **kw): from setuptools.sandbox import SandboxViolation + raise SandboxViolation(operation, args, kw) if _file: @@ -440,12 +472,10 @@ class DirectorySandbox(AbstractSandbox): def _exempted(self, filepath): start_matches = ( - filepath.startswith(exception) - for exception in self._exceptions + filepath.startswith(exception) for exception in self._exceptions ) pattern_matches = ( - re.match(pattern, filepath) - for pattern in self._exception_patterns + re.match(pattern, filepath) for pattern in self._exception_patterns ) candidates = itertools.chain(start_matches, pattern_matches) return any(candidates) @@ -470,16 +500,19 @@ class DirectorySandbox(AbstractSandbox): WRITE_FLAGS = functools.reduce( - operator.or_, [ - getattr(_os, a, 0) for a in - "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split()] + operator.or_, + [ + getattr(_os, a, 0) + for a in "O_WRONLY O_RDWR O_APPEND O_CREAT O_TRUNC O_TEMPORARY".split() + ], ) class SandboxViolation(DistutilsError): """A setup script attempted to modify the filesystem outside the sandbox""" - tmpl = textwrap.dedent(""" + tmpl = textwrap.dedent( + """ SandboxViolation: {cmd}{args!r} {kwargs} The package setup script has attempted to modify files on your system @@ -489,7 +522,8 @@ class SandboxViolation(DistutilsError): support alternate installation locations even if you run its setup script by hand. Please inform the package's author and the EasyInstall maintainers to find out if a fix or workaround is available. - """).lstrip() + """ + ).lstrip() def __str__(self): cmd, args, kwargs = self.args diff --git a/setuptools/tests/__init__.py b/setuptools/tests/__init__.py index a7a2112..564adf2 100644 --- a/setuptools/tests/__init__.py +++ b/setuptools/tests/__init__.py @@ -3,11 +3,8 @@ import locale import pytest -__all__ = ['fail_on_ascii', 'ack_2to3'] +__all__ = ['fail_on_ascii'] is_ascii = locale.getpreferredencoding() == 'ANSI_X3.4-1968' fail_on_ascii = pytest.mark.xfail(is_ascii, reason="Test fails in this locale") - - -ack_2to3 = pytest.mark.filterwarnings('ignore:2to3 support is deprecated') diff --git a/setuptools/tests/test_config.py b/setuptools/tests/test_config.py index ec65250..005742e 100644 --- a/setuptools/tests/test_config.py +++ b/setuptools/tests/test_config.py @@ -30,14 +30,11 @@ def make_package_dir(name, base_dir, ns=False): def fake_env( - tmpdir, setup_cfg, setup_py=None, - encoding='ascii', package_path='fake_package'): + tmpdir, setup_cfg, setup_py=None, encoding='ascii', package_path='fake_package' +): if setup_py is None: - setup_py = ( - 'from setuptools import setup\n' - 'setup()\n' - ) + setup_py = 'from setuptools import setup\n' 'setup()\n' tmpdir.join('setup.py').write(setup_py) config = tmpdir.join('setup.cfg') @@ -78,7 +75,6 @@ def test_parsers_implemented(): class TestConfigurationReader: - def test_basic(self, tmpdir): _, config = fake_env( tmpdir, @@ -87,7 +83,7 @@ class TestConfigurationReader: 'keywords = one, two\n' '\n' '[options]\n' - 'scripts = bin/a.py, bin/b.py\n' + 'scripts = bin/a.py, bin/b.py\n', ) config_dict = read_configuration('%s' % config) assert config_dict['metadata']['version'] == '10.1.1' @@ -101,15 +97,12 @@ class TestConfigurationReader: def test_ignore_errors(self, tmpdir): _, config = fake_env( tmpdir, - '[metadata]\n' - 'version = attr: none.VERSION\n' - 'keywords = one, two\n' + '[metadata]\n' 'version = attr: none.VERSION\n' 'keywords = one, two\n', ) with pytest.raises(ImportError): read_configuration('%s' % config) - config_dict = read_configuration( - '%s' % config, ignore_option_errors=True) + config_dict = read_configuration('%s' % config, ignore_option_errors=True) assert config_dict['metadata']['keywords'] == ['one', 'two'] assert 'version' not in config_dict['metadata'] @@ -118,7 +111,6 @@ class TestConfigurationReader: class TestMetadata: - def test_basic(self, tmpdir): fake_env( @@ -133,7 +125,7 @@ class TestMetadata: 'provides = package, package.sub\n' 'license = otherlic\n' 'download_url = http://test.test.com/test/\n' - 'maintainer_email = test@test.com\n' + 'maintainer_email = test@test.com\n', ) tmpdir.join('README').write('readme contents\nline2') @@ -160,12 +152,14 @@ class TestMetadata: def test_license_cfg(self, tmpdir): fake_env( tmpdir, - DALS(""" + DALS( + """ [metadata] name=foo version=0.0.1 license=Apache 2.0 - """) + """ + ), ) with get_dist(tmpdir) as dist: @@ -179,9 +173,7 @@ class TestMetadata: fake_env( tmpdir, - '[metadata]\n' - 'long_description = file: README.rst, CHANGES.rst\n' - '\n' + '[metadata]\n' 'long_description = file: README.rst, CHANGES.rst\n' '\n', ) tmpdir.join('README.rst').write('readme contents\nline2') @@ -189,17 +181,12 @@ class TestMetadata: with get_dist(tmpdir) as dist: assert dist.metadata.long_description == ( - 'readme contents\nline2\n' - 'changelog contents\nand stuff' + 'readme contents\nline2\n' 'changelog contents\nand stuff' ) def test_file_sandboxed(self, tmpdir): - fake_env( - tmpdir, - '[metadata]\n' - 'long_description = file: ../../README\n' - ) + fake_env(tmpdir, '[metadata]\n' 'long_description = file: ../../README\n') with get_dist(tmpdir, parse=False) as dist: with pytest.raises(DistutilsOptionError): @@ -216,7 +203,7 @@ class TestMetadata: 'platform = a, b\n' 'classifier =\n' ' Framework :: Django\n' - ' Programming Language :: Python :: 3.5\n' + ' Programming Language :: Python :: 3.5\n', ) with get_dist(tmpdir) as dist: @@ -241,7 +228,7 @@ class TestMetadata: ' two\n' 'classifiers =\n' ' Framework :: Django\n' - ' Programming Language :: Python :: 3.5\n' + ' Programming Language :: Python :: 3.5\n', ) with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -258,7 +245,7 @@ class TestMetadata: '[metadata]\n' 'project_urls =\n' ' Link One = https://example.com/one/\n' - ' Link Two = https://example.com/two/\n' + ' Link Two = https://example.com/two/\n', ) with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -270,9 +257,7 @@ class TestMetadata: def test_version(self, tmpdir): package_dir, config = fake_env( - tmpdir, - '[metadata]\n' - 'version = attr: fake_package.VERSION\n' + tmpdir, '[metadata]\n' 'version = attr: fake_package.VERSION\n' ) sub_a = package_dir.mkdir('subpkg_a') @@ -282,37 +267,28 @@ class TestMetadata: sub_b = package_dir.mkdir('subpkg_b') sub_b.join('__init__.py').write('') sub_b.join('mod.py').write( - 'import third_party_module\n' - 'VERSION = (2016, 11, 26)' + 'import third_party_module\n' 'VERSION = (2016, 11, 26)' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '1.2.3' - config.write( - '[metadata]\n' - 'version = attr: fake_package.get_version\n' - ) + config.write('[metadata]\n' 'version = attr: fake_package.get_version\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '3.4.5.dev' - config.write( - '[metadata]\n' - 'version = attr: fake_package.VERSION_MAJOR\n' - ) + config.write('[metadata]\n' 'version = attr: fake_package.VERSION_MAJOR\n') with get_dist(tmpdir) as dist: assert dist.metadata.version == '1' config.write( - '[metadata]\n' - 'version = attr: fake_package.subpkg_a.mod.VERSION\n' + '[metadata]\n' 'version = attr: fake_package.subpkg_a.mod.VERSION\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' config.write( - '[metadata]\n' - 'version = attr: fake_package.subpkg_b.mod.VERSION\n' + '[metadata]\n' 'version = attr: fake_package.subpkg_b.mod.VERSION\n' ) with get_dist(tmpdir) as dist: assert dist.metadata.version == '2016.11.26' @@ -320,9 +296,7 @@ class TestMetadata: def test_version_file(self, tmpdir): _, config = fake_env( - tmpdir, - '[metadata]\n' - 'version = file: fake_package/version.txt\n' + tmpdir, '[metadata]\n' 'version = file: fake_package/version.txt\n' ) tmpdir.join('fake_package', 'version.txt').write('1.2.3\n') @@ -343,7 +317,7 @@ class TestMetadata: '[options]\n' 'package_dir =\n' ' = src\n', - package_path='src/fake_package_simple' + package_path='src/fake_package_simple', ) with get_dist(tmpdir) as dist: @@ -358,7 +332,7 @@ class TestMetadata: '[options]\n' 'package_dir =\n' ' fake_package_rename = fake_dir\n', - package_path='fake_dir' + package_path='fake_dir', ) with get_dist(tmpdir) as dist: @@ -373,7 +347,7 @@ class TestMetadata: '[options]\n' 'package_dir =\n' ' fake_package_complex = src/fake_dir\n', - package_path='src/fake_dir' + package_path='src/fake_dir', ) with get_dist(tmpdir) as dist: @@ -381,39 +355,28 @@ class TestMetadata: def test_unknown_meta_item(self, tmpdir): - fake_env( - tmpdir, - '[metadata]\n' - 'name = fake_name\n' - 'unknown = some\n' - ) + fake_env(tmpdir, '[metadata]\n' 'name = fake_name\n' 'unknown = some\n') with get_dist(tmpdir, parse=False) as dist: dist.parse_config_files() # Skip unknown. def test_usupported_section(self, tmpdir): - fake_env( - tmpdir, - '[metadata.some]\n' - 'key = val\n' - ) + fake_env(tmpdir, '[metadata.some]\n' 'key = val\n') with get_dist(tmpdir, parse=False) as dist: with pytest.raises(DistutilsOptionError): dist.parse_config_files() def test_classifiers(self, tmpdir): - expected = set([ - 'Framework :: Django', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.5', - ]) + expected = set( + [ + 'Framework :: Django', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.5', + ] + ) # From file. - _, config = fake_env( - tmpdir, - '[metadata]\n' - 'classifiers = file: classifiers\n' - ) + _, config = fake_env(tmpdir, '[metadata]\n' 'classifiers = file: classifiers\n') tmpdir.join('classifiers').write( 'Framework :: Django\n' @@ -441,7 +404,7 @@ class TestMetadata: '[metadata]\n' 'version = 10.1.1\n' 'description = Some description\n' - 'requires = some, requirement\n' + 'requires = some, requirement\n', ) with pytest.deprecated_call(): @@ -453,41 +416,26 @@ class TestMetadata: assert metadata.requires == ['some', 'requirement'] def test_interpolation(self, tmpdir): - fake_env( - tmpdir, - '[metadata]\n' - 'description = %(message)s\n' - ) + fake_env(tmpdir, '[metadata]\n' 'description = %(message)s\n') with pytest.raises(configparser.InterpolationMissingOptionError): with get_dist(tmpdir): pass def test_non_ascii_1(self, tmpdir): - fake_env( - tmpdir, - '[metadata]\n' - 'description = éàïôñ\n', - encoding='utf-8' - ) + fake_env(tmpdir, '[metadata]\n' 'description = éàïôñ\n', encoding='utf-8') with get_dist(tmpdir): pass def test_non_ascii_3(self, tmpdir): - fake_env( - tmpdir, - '\n' - '# -*- coding: invalid\n' - ) + fake_env(tmpdir, '\n' '# -*- coding: invalid\n') with get_dist(tmpdir): pass def test_non_ascii_4(self, tmpdir): fake_env( tmpdir, - '# -*- coding: utf-8\n' - '[metadata]\n' - 'description = éàïôñ\n', - encoding='utf-8' + '# -*- coding: utf-8\n' '[metadata]\n' 'description = éàïôñ\n', + encoding='utf-8', ) with get_dist(tmpdir) as dist: assert dist.metadata.description == 'éàïôñ' @@ -501,7 +449,7 @@ class TestMetadata: '# vim: set fileencoding=iso-8859-15 :\n' '[metadata]\n' 'description = éàïôñ\n', - encoding='iso-8859-15' + encoding='iso-8859-15', ) with pytest.raises(UnicodeDecodeError): with get_dist(tmpdir): @@ -514,11 +462,13 @@ class TestMetadata: tmpdir, '[metadata]\n' 'author-email = test@test.com\n' - 'maintainer_email = foo@foo.com\n' - ) - msg = ("Usage of dash-separated 'author-email' will not be supported " - "in future versions. " - "Please use the underscore name 'author_email' instead") + 'maintainer_email = foo@foo.com\n', + ) + msg = ( + "Usage of dash-separated 'author-email' will not be supported " + "in future versions. " + "Please use the underscore name 'author_email' instead" + ) with pytest.warns(UserWarning, match=msg): with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -530,14 +480,13 @@ class TestMetadata: # remove this test and the method make_option_lowercase() in setuptools.dist # when no longer needed fake_env( - tmpdir, - '[metadata]\n' - 'Name = foo\n' - 'description = Some description\n' + tmpdir, '[metadata]\n' 'Name = foo\n' 'description = Some description\n' + ) + msg = ( + "Usage of uppercase key 'Name' in 'metadata' will be deprecated in " + "future versions. " + "Please use lowercase 'name' instead" ) - msg = ("Usage of uppercase key 'Name' in 'metadata' will be deprecated in " - "future versions. " - "Please use lowercase 'name' instead") with pytest.warns(UserWarning, match=msg): with get_dist(tmpdir) as dist: metadata = dist.metadata @@ -547,21 +496,16 @@ class TestMetadata: class TestOptions: - def test_basic(self, tmpdir): fake_env( tmpdir, '[options]\n' 'zip_safe = True\n' - 'use_2to3 = 1\n' 'include_package_data = yes\n' 'package_dir = b=c, =src\n' 'packages = pack_a, pack_b.subpack\n' 'namespace_packages = pack1, pack2\n' - 'use_2to3_fixers = your.fixers, or.here\n' - 'use_2to3_exclude_fixers = one.here, two.there\n' - 'convert_2to3_doctests = src/tests/one.txt, src/two.txt\n' 'scripts = bin/one.py, bin/two.py\n' 'eager_resources = bin/one.py, bin/two.py\n' 'install_requires = docutils>=0.3; pack ==1.1, ==1.3; hey\n' @@ -570,34 +514,24 @@ class TestOptions: 'dependency_links = http://some.com/here/1, ' 'http://some.com/there/2\n' 'python_requires = >=1.0, !=2.8\n' - 'py_modules = module1, module2\n' + 'py_modules = module1, module2\n', ) with get_dist(tmpdir) as dist: assert dist.zip_safe - assert dist.use_2to3 assert dist.include_package_data assert dist.package_dir == {'': 'src', 'b': 'c'} assert dist.packages == ['pack_a', 'pack_b.subpack'] assert dist.namespace_packages == ['pack1', 'pack2'] - assert dist.use_2to3_fixers == ['your.fixers', 'or.here'] - assert dist.use_2to3_exclude_fixers == ['one.here', 'two.there'] - assert dist.convert_2to3_doctests == ([ - 'src/tests/one.txt', 'src/two.txt']) assert dist.scripts == ['bin/one.py', 'bin/two.py'] - assert dist.dependency_links == ([ - 'http://some.com/here/1', - 'http://some.com/there/2' - ]) - assert dist.install_requires == ([ - 'docutils>=0.3', - 'pack==1.1,==1.3', - 'hey' - ]) - assert dist.setup_requires == ([ - 'docutils>=0.3', - 'spack ==1.1, ==1.3', - 'there' - ]) + assert dist.dependency_links == ( + ['http://some.com/here/1', 'http://some.com/there/2'] + ) + assert dist.install_requires == ( + ['docutils>=0.3', 'pack==1.1,==1.3', 'hey'] + ) + assert dist.setup_requires == ( + ['docutils>=0.3', 'spack ==1.1, ==1.3', 'there'] + ) assert dist.tests_require == ['mock==0.7.2', 'pytest'] assert dist.python_requires == '>=1.0, !=2.8' assert dist.py_modules == ['module1', 'module2'] @@ -615,15 +549,6 @@ class TestOptions: 'namespace_packages = \n' ' pack1\n' ' pack2\n' - 'use_2to3_fixers = \n' - ' your.fixers\n' - ' or.here\n' - 'use_2to3_exclude_fixers = \n' - ' one.here\n' - ' two.there\n' - 'convert_2to3_doctests = \n' - ' src/tests/one.txt\n' - ' src/two.txt\n' 'scripts = \n' ' bin/one.py\n' ' bin/two.py\n' @@ -643,39 +568,26 @@ class TestOptions: ' there\n' 'dependency_links = \n' ' http://some.com/here/1\n' - ' http://some.com/there/2\n' + ' http://some.com/there/2\n', ) with get_dist(tmpdir) as dist: assert dist.package_dir == {'': 'src', 'b': 'c'} assert dist.packages == ['pack_a', 'pack_b.subpack'] assert dist.namespace_packages == ['pack1', 'pack2'] - assert dist.use_2to3_fixers == ['your.fixers', 'or.here'] - assert dist.use_2to3_exclude_fixers == ['one.here', 'two.there'] - assert dist.convert_2to3_doctests == ( - ['src/tests/one.txt', 'src/two.txt']) assert dist.scripts == ['bin/one.py', 'bin/two.py'] - assert dist.dependency_links == ([ - 'http://some.com/here/1', - 'http://some.com/there/2' - ]) - assert dist.install_requires == ([ - 'docutils>=0.3', - 'pack==1.1,==1.3', - 'hey' - ]) - assert dist.setup_requires == ([ - 'docutils>=0.3', - 'spack ==1.1, ==1.3', - 'there' - ]) + assert dist.dependency_links == ( + ['http://some.com/here/1', 'http://some.com/there/2'] + ) + assert dist.install_requires == ( + ['docutils>=0.3', 'pack==1.1,==1.3', 'hey'] + ) + assert dist.setup_requires == ( + ['docutils>=0.3', 'spack ==1.1, ==1.3', 'there'] + ) assert dist.tests_require == ['mock==0.7.2', 'pytest'] def test_package_dir_fail(self, tmpdir): - fake_env( - tmpdir, - '[options]\n' - 'package_dir = a b\n' - ) + fake_env(tmpdir, '[options]\n' 'package_dir = a b\n') with get_dist(tmpdir, parse=False) as dist: with pytest.raises(DistutilsOptionError): dist.parse_config_files() @@ -689,7 +601,7 @@ class TestOptions: '\n' '[options.exclude_package_data]\n' '* = fake1.txt, fake2.txt\n' - 'hello = *.dat\n' + 'hello = *.dat\n', ) with get_dist(tmpdir) as dist: @@ -703,29 +615,21 @@ class TestOptions: } def test_packages(self, tmpdir): - fake_env( - tmpdir, - '[options]\n' - 'packages = find:\n' - ) + fake_env(tmpdir, '[options]\n' 'packages = find:\n') with get_dist(tmpdir) as dist: assert dist.packages == ['fake_package'] def test_find_directive(self, tmpdir): - dir_package, config = fake_env( - tmpdir, - '[options]\n' - 'packages = find:\n' - ) + dir_package, config = fake_env(tmpdir, '[options]\n' 'packages = find:\n') dir_sub_one, _ = make_package_dir('sub_one', dir_package) dir_sub_two, _ = make_package_dir('sub_two', dir_package) with get_dist(tmpdir) as dist: - assert set(dist.packages) == set([ - 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one' - ]) + assert set(dist.packages) == set( + ['fake_package', 'fake_package.sub_two', 'fake_package.sub_one'] + ) config.write( '[options]\n' @@ -749,14 +653,11 @@ class TestOptions: ' fake_package.sub_one\n' ) with get_dist(tmpdir) as dist: - assert set(dist.packages) == set( - ['fake_package', 'fake_package.sub_two']) + assert set(dist.packages) == set(['fake_package', 'fake_package.sub_two']) def test_find_namespace_directive(self, tmpdir): dir_package, config = fake_env( - tmpdir, - '[options]\n' - 'packages = find_namespace:\n' + tmpdir, '[options]\n' 'packages = find_namespace:\n' ) dir_sub_one, _ = make_package_dir('sub_one', dir_package) @@ -764,7 +665,9 @@ class TestOptions: with get_dist(tmpdir) as dist: assert set(dist.packages) == { - 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one' + 'fake_package', + 'fake_package.sub_two', + 'fake_package.sub_one', } config.write( @@ -789,9 +692,7 @@ class TestOptions: ' fake_package.sub_one\n' ) with get_dist(tmpdir) as dist: - assert set(dist.packages) == { - 'fake_package', 'fake_package.sub_two' - } + assert set(dist.packages) == {'fake_package', 'fake_package.sub_two'} def test_extras_require(self, tmpdir): fake_env( @@ -800,29 +701,21 @@ class TestOptions: 'pdf = ReportLab>=1.2; RXP\n' 'rest = \n' ' docutils>=0.3\n' - ' pack ==1.1, ==1.3\n' + ' pack ==1.1, ==1.3\n', ) with get_dist(tmpdir) as dist: assert dist.extras_require == { 'pdf': ['ReportLab>=1.2', 'RXP'], - 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'] + 'rest': ['docutils>=0.3', 'pack==1.1,==1.3'], } assert dist.metadata.provides_extras == set(['pdf', 'rest']) def test_dash_preserved_extras_require(self, tmpdir): - fake_env( - tmpdir, - '[options.extras_require]\n' - 'foo-a = foo\n' - 'foo_b = test\n' - ) + fake_env(tmpdir, '[options.extras_require]\n' 'foo-a = foo\n' 'foo_b = test\n') with get_dist(tmpdir) as dist: - assert dist.extras_require == { - 'foo-a': ['foo'], - 'foo_b': ['test'] - } + assert dist.extras_require == {'foo-a': ['foo'], 'foo_b': ['test']} def test_entry_points(self, tmpdir): _, config = fake_env( @@ -830,7 +723,7 @@ class TestOptions: '[options.entry_points]\n' 'group1 = point1 = pack.module:func, ' '.point2 = pack.module2:func_rest [rest]\n' - 'group2 = point3 = pack.module:func2\n' + 'group2 = point3 = pack.module:func2\n', ) with get_dist(tmpdir) as dist: @@ -839,7 +732,7 @@ class TestOptions: 'point1 = pack.module:func', '.point2 = pack.module2:func_rest [rest]', ], - 'group2': ['point3 = pack.module:func2'] + 'group2': ['point3 = pack.module:func2'], } expected = ( @@ -850,10 +743,7 @@ class TestOptions: tmpdir.join('entry_points').write(expected) # From file. - config.write( - '[options]\n' - 'entry_points = file: entry_points\n' - ) + config.write('[options]\n' 'entry_points = file: entry_points\n') with get_dist(tmpdir) as dist: assert dist.entry_points == expected @@ -864,7 +754,7 @@ class TestOptions: '[options.entry_points]\n' 'GROUP1 = point1 = pack.module:func, ' '.point2 = pack.module2:func_rest [rest]\n' - 'group2 = point3 = pack.module:func2\n' + 'group2 = point3 = pack.module:func2\n', ) with get_dist(tmpdir) as dist: @@ -873,7 +763,7 @@ class TestOptions: 'point1 = pack.module:func', '.point2 = pack.module2:func_rest [rest]', ], - 'group2': ['point3 = pack.module:func2'] + 'group2': ['point3 = pack.module:func2'], } def test_data_files(self, tmpdir): @@ -883,7 +773,7 @@ class TestOptions: 'cfg =\n' ' a/b.conf\n' ' c/d.conf\n' - 'data = e/f.dat, g/h.dat\n' + 'data = e/f.dat, g/h.dat\n', ) with get_dist(tmpdir) as dist: @@ -931,10 +821,12 @@ class TestOptions: def test_python_requires_simple(self, tmpdir): fake_env( tmpdir, - DALS(""" + DALS( + """ [options] python_requires=>=2.7 - """), + """ + ), ) with get_dist(tmpdir) as dist: dist.parse_config_files() @@ -942,10 +834,12 @@ class TestOptions: def test_python_requires_compound(self, tmpdir): fake_env( tmpdir, - DALS(""" + DALS( + """ [options] python_requires=>=2.7,!=3.0.* - """), + """ + ), ) with get_dist(tmpdir) as dist: dist.parse_config_files() @@ -953,10 +847,12 @@ class TestOptions: def test_python_requires_invalid(self, tmpdir): fake_env( tmpdir, - DALS(""" + DALS( + """ [options] python_requires=invalid - """), + """ + ), ) with pytest.raises(Exception): with get_dist(tmpdir) as dist: @@ -974,9 +870,7 @@ class TestOptions: fake_env( tmpdir, - '[options]\n' - 'cmdclass =\n' - ' customcmd = custom_build.CustomCmd\n' + '[options]\n' 'cmdclass =\n' ' customcmd = custom_build.CustomCmd\n', ) with get_dist(tmpdir) as dist: @@ -1000,24 +894,23 @@ class TestExternalSetters: def _fake_distribution_init(self, dist, attrs): saved_dist_init(dist, attrs) # see self._DISTUTUILS_UNSUPPORTED_METADATA - setattr(dist.metadata, 'long_description_content_type', - 'text/something') + setattr(dist.metadata, 'long_description_content_type', 'text/something') # Test overwrite setup() args - setattr(dist.metadata, 'project_urls', { - 'Link One': 'https://example.com/one/', - 'Link Two': 'https://example.com/two/', - }) + setattr( + dist.metadata, + 'project_urls', + { + 'Link One': 'https://example.com/one/', + 'Link Two': 'https://example.com/two/', + }, + ) return None @patch.object(_Distribution, '__init__', autospec=True) def test_external_setters(self, mock_parent_init, tmpdir): mock_parent_init.side_effect = self._fake_distribution_init - dist = Distribution(attrs={ - 'project_urls': { - 'will_be': 'ignored' - } - }) + dist = Distribution(attrs={'project_urls': {'will_be': 'ignored'}}) assert dist.metadata.long_description_content_type == 'text/something' assert dist.metadata.project_urls == { diff --git a/setuptools/tests/test_develop.py b/setuptools/tests/test_develop.py index df8db4e..70c5794 100644 --- a/setuptools/tests/test_develop.py +++ b/setuptools/tests/test_develop.py @@ -2,9 +2,7 @@ """ import os -import site import sys -import io import subprocess import platform import pathlib @@ -16,7 +14,6 @@ import pytest from setuptools.command.develop import develop from setuptools.dist import Distribution -from setuptools.tests import ack_2to3 from . import contexts from . import namespaces @@ -25,7 +22,6 @@ from setuptools import setup setup(name='foo', packages=['foo'], - use_2to3=True, ) """ @@ -62,43 +58,6 @@ class TestDevelop: in_virtualenv = hasattr(sys, 'real_prefix') in_venv = hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix - @pytest.mark.skipif( - in_virtualenv or in_venv, - reason="Cannot run when invoked in a virtualenv or venv") - @ack_2to3 - def test_2to3_user_mode(self, test_env): - settings = dict( - name='foo', - packages=['foo'], - use_2to3=True, - version='0.0', - ) - dist = Distribution(settings) - dist.script_name = 'setup.py' - cmd = develop(dist) - cmd.user = 1 - cmd.ensure_finalized() - cmd.install_dir = site.USER_SITE - cmd.user = 1 - with contexts.quiet(): - cmd.run() - - # let's see if we got our egg link at the right place - content = os.listdir(site.USER_SITE) - content.sort() - assert content == ['easy-install.pth', 'foo.egg-link'] - - # Check that we are using the right code. - fn = os.path.join(site.USER_SITE, 'foo.egg-link') - with io.open(fn) as egg_link_file: - path = egg_link_file.read().split()[0].strip() - fn = os.path.join(path, 'foo', '__init__.py') - with io.open(fn) as init_file: - init = init_file.read().strip() - - expected = 'print("foo")' - assert init == expected - def test_console_scripts(self, tmpdir): """ Test that console scripts are installed and that they reference @@ -106,7 +65,8 @@ class TestDevelop: """ pytest.skip( "TODO: needs a fixture to cause 'develop' " - "to be invoked without mutating environment.") + "to be invoked without mutating environment." + ) settings = dict( name='foo', packages=['foo'], @@ -132,6 +92,7 @@ class TestResolver: of what _resolve_setup_path is intending to do. Come up with more meaningful cases that look like real-world scenarios. """ + def test_resolve_setup_path_cwd(self): assert develop._resolve_setup_path('.', '.', '.') == '.' @@ -143,7 +104,6 @@ class TestResolver: class TestNamespaces: - @staticmethod def install_develop(src_dir, target): @@ -151,7 +111,8 @@ class TestNamespaces: sys.executable, 'setup.py', 'develop', - '--install-dir', str(target), + '--install-dir', + str(target), ] with src_dir.as_cwd(): with test.test.paths_on_pythonpath([str(target)]): @@ -182,14 +143,16 @@ class TestNamespaces: 'pip', 'install', str(pkg_A), - '-t', str(target), + '-t', + str(target), ] subprocess.check_call(install_cmd) self.install_develop(pkg_B, target) namespaces.make_site_dir(target) try_import = [ sys.executable, - '-c', 'import myns.pkgA; import myns.pkgB', + '-c', + 'import myns.pkgA; import myns.pkgB', ] with test.test.paths_on_pythonpath([str(target)]): subprocess.check_call(try_import) @@ -197,7 +160,8 @@ class TestNamespaces: # additionally ensure that pkg_resources import works pkg_resources_imp = [ sys.executable, - '-c', 'import pkg_resources', + '-c', + 'import pkg_resources', ] with test.test.paths_on_pythonpath([str(target)]): subprocess.check_call(pkg_resources_imp) @@ -206,12 +170,16 @@ class TestNamespaces: def install_workaround(site_packages): site_packages.mkdir(parents=True) sc = site_packages / 'sitecustomize.py' - sc.write_text(textwrap.dedent(""" + sc.write_text( + textwrap.dedent( + """ import site import pathlib here = pathlib.Path(__file__).parent site.addsitedir(str(here)) - """).lstrip()) + """ + ).lstrip() + ) @pytest.mark.xfail( platform.python_implementation() == 'PyPy', @@ -228,8 +196,7 @@ class TestNamespaces: site_packages = prefix / next( pathlib.Path(path).relative_to(sys.prefix) for path in sys.path - if 'site-packages' in path - and path.startswith(sys.prefix) + if 'site-packages' in path and path.startswith(sys.prefix) ) # install the workaround @@ -238,11 +205,13 @@ class TestNamespaces: env = dict(os.environ, PYTHONPATH=str(site_packages)) cmd = [ sys.executable, - '-m', 'pip', + '-m', + 'pip', 'install', '--editable', str(sample_project), - '--prefix', str(prefix), + '--prefix', + str(prefix), '--no-build-isolation', ] subprocess.check_call(cmd, env=env) diff --git a/setuptools/tests/test_test.py b/setuptools/tests/test_test.py index 180562e..6bce8e2 100644 --- a/setuptools/tests/test_test.py +++ b/setuptools/tests/test_test.py @@ -1,4 +1,3 @@ -import mock from distutils import log import os @@ -6,12 +5,12 @@ import pytest from setuptools.command.test import test from setuptools.dist import Distribution -from setuptools.tests import ack_2to3 from .textwrap import DALS -SETUP_PY = DALS(""" +SETUP_PY = DALS( + """ from setuptools import setup setup(name='foo', @@ -19,9 +18,11 @@ SETUP_PY = DALS(""" namespace_packages=['name'], test_suite='name.space.tests.test_suite', ) - """) + """ +) -NS_INIT = DALS(""" +NS_INIT = DALS( + """ # -*- coding: Latin-1 -*- # Söme Arbiträry Ünicode to test Distribute Issüé 310 try: @@ -29,17 +30,20 @@ NS_INIT = DALS(""" except ImportError: from pkgutil import extend_path __path__ = extend_path(__path__, __name__) - """) + """ +) -TEST_PY = DALS(""" +TEST_PY = DALS( + """ import unittest class TestTest(unittest.TestCase): def test_test(self): - print "Foo" # Should fail under Python 3 unless 2to3 is used + print "Foo" # Should fail under Python 3 test_suite = unittest.makeSuite(TestTest) - """) + """ +) @pytest.fixture @@ -70,25 +74,6 @@ def quiet_log(): log.set_verbosity(0) -@pytest.mark.usefixtures('sample_test', 'quiet_log') -@ack_2to3 -def test_test(capfd): - params = dict( - name='foo', - packages=['name', 'name.space', 'name.space.tests'], - namespace_packages=['name'], - test_suite='name.space.tests.test_suite', - use_2to3=True, - ) - dist = Distribution(params) - dist.script_name = 'setup.py' - cmd = test(dist) - cmd.ensure_finalized() - cmd.run() - out, err = capfd.readouterr() - assert out == 'Foo\n' - - @pytest.mark.usefixtures('tmpdir_cwd', 'quiet_log') def test_tests_are_run_once(capfd): params = dict( @@ -104,13 +89,16 @@ def test_tests_are_run_once(capfd): with open('dummy/__init__.py', 'wt'): pass with open('dummy/test_dummy.py', 'wt') as f: - f.write(DALS( - """ + f.write( + DALS( + """ import unittest class TestTest(unittest.TestCase): def test_test(self): print('Foo') - """)) + """ + ) + ) dist = Distribution(params) dist.script_name = 'setup.py' cmd = test(dist) @@ -118,54 +106,3 @@ def test_tests_are_run_once(capfd): cmd.run() out, err = capfd.readouterr() assert out == 'Foo\n' - - -@pytest.mark.usefixtures('sample_test') -@ack_2to3 -def test_warns_deprecation(capfd): - params = dict( - name='foo', - packages=['name', 'name.space', 'name.space.tests'], - namespace_packages=['name'], - test_suite='name.space.tests.test_suite', - use_2to3=True - ) - dist = Distribution(params) - dist.script_name = 'setup.py' - cmd = test(dist) - cmd.ensure_finalized() - cmd.announce = mock.Mock() - cmd.run() - capfd.readouterr() - msg = ( - "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." - ) - cmd.announce.assert_any_call(msg, log.WARN) - - -@pytest.mark.usefixtures('sample_test') -@ack_2to3 -def test_deprecation_stderr(capfd): - params = dict( - name='foo', - packages=['name', 'name.space', 'name.space.tests'], - namespace_packages=['name'], - test_suite='name.space.tests.test_suite', - use_2to3=True - ) - dist = Distribution(params) - dist.script_name = 'setup.py' - cmd = test(dist) - cmd.ensure_finalized() - cmd.run() - out, err = capfd.readouterr() - msg = ( - "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.\n" - ) - assert msg in err -- 2.34.1