Imported Upstream version 40.7.0 upstream/40.7.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 29 Dec 2020 22:03:37 +0000 (07:03 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Tue, 29 Dec 2020 22:03:37 +0000 (07:03 +0900)
47 files changed:
.mergify.yml [new file with mode: 0644]
CHANGES.rst
docs/_templates/indexsidebar.html
docs/developer-guide.txt
docs/requirements.txt
docs/setuptools.txt
pyproject.toml
setup.cfg
setup.py
setuptools/__init__.py
setuptools/build_meta.py
setuptools/command/develop.py
setuptools/command/upload.py
setuptools/config.py
setuptools/dist.py
setuptools/package_index.py
setuptools/pep425tags.py
setuptools/py36compat.py [deleted file]
setuptools/ssl_support.py
setuptools/tests/files.py
setuptools/tests/server.py
setuptools/tests/test_build_clib.py
setuptools/tests/test_build_meta.py
setuptools/tests/test_config.py
setuptools/tests/test_depends.py
setuptools/tests/test_dist.py
setuptools/tests/test_easy_install.py
setuptools/tests/test_egg_info.py
setuptools/tests/test_find_packages.py
setuptools/tests/test_install_scripts.py
setuptools/tests/test_integration.py
setuptools/tests/test_manifest.py
setuptools/tests/test_msvc.py
setuptools/tests/test_namespaces.py
setuptools/tests/test_packageindex.py
setuptools/tests/test_pep425tags.py
setuptools/tests/test_sandbox.py
setuptools/tests/test_setuptools.py
setuptools/tests/test_test.py
setuptools/tests/test_upload.py
setuptools/tests/test_virtualenv.py
setuptools/tests/test_wheel.py
setuptools/tests/test_windows_wrappers.py
setuptools/unicode_utils.py
setuptools/wheel.py
tests/requirements.txt
tox.ini

diff --git a/.mergify.yml b/.mergify.yml
new file mode 100644 (file)
index 0000000..7f0df53
--- /dev/null
@@ -0,0 +1,11 @@
+pull_request_rules:
+- name: auto-merge
+  conditions:
+  - base=master
+  - label=auto-merge
+  - status-success=continuous-integration/appveyor/pr
+  - status-success=continuous-integration/travis-ci/pr
+  - status-success=deploy/netlify
+  actions:
+    merge:
+      method: merge
index 48a176a815b5ade11821276d2b2e8f4ed1d80d71..ca7122e9931b2c084a661309643c82d87b426b38 100644 (file)
@@ -1,3 +1,13 @@
+v40.7.0
+-------
+
+* #1551: File inputs for the `license` field in `setup.cfg` files now explicitly raise an error.
+* #1180: Add support for non-ASCII in setup.cfg (#1062). Add support for native strings on some parameters (#1136).
+* #1499: ``setuptools.package_index`` no longer relies on the deprecated ``urllib.parse.splituser`` per Python #27485.
+* #1544: Added tests for PackageIndex.download (for git URLs).
+* #1625: In PEP 517 build_meta builder, ensure that sdists are built as gztar per the spec.
+
+
 v40.6.3
 -------
 
index b8c6148e1f993f52372df9f4b64ef6e4e537be47..504de6b0691de635f51c6e9235d01272a8dea00d 100644 (file)
@@ -5,11 +5,11 @@
 
 <h3>Questions? Suggestions? Contributions?</h3>
 
-<p>Visit the <a href="https://github.com/pypa/setuptools">Setuptools project page</a> </p>
+<p>Visit the <a href="{{ package_url }}">Project page</a> </p>
 
 <h3 class="donation">Professional support</h3>
 
 <p>
 Professionally-supported {{ project }} is available with the
-<a href="https://tidelift.com/subscription/pkg/pypi-setuptools?utm_source=pypi-setuptools&utm_medium=readme">Tidelift Subscription</a>.
+<a href="https://tidelift.com/subscription/pkg/pypi-{{ project }}?utm_source=pypi-{{ project }}&utm_medium=readme">Tidelift Subscription</a>.
 </p>
index c011491a30c8211078f1369d3e1c585d53ab23fb..a5942c8b0f6320bd954aa4f7755c5a4f513a4e4a 100644 (file)
@@ -67,8 +67,8 @@ All PRs with code changes should include tests. All changes should include a
 changelog entry.
 
 ``setuptools`` uses `towncrier <https://pypi.org/project/towncrier/>`_
-for changelog managment, so when making a PR, please add a news fragment in the
-``changelog.d/`` folder. Changelog files are written in Restructured Text and
+for changelog management, so when making a PR, please add a news fragment in the
+``changelog.d/`` folder. Changelog files are written in reStructuredText and
 should be a 1 or 2 sentence description of the substantive changes in the PR.
 They should be named ``<pr_number>.<category>.rst``, where the categories are:
 
@@ -76,7 +76,7 @@ They should be named ``<pr_number>.<category>.rst``, where the categories are:
 - ``breaking``: Any backwards-compatibility breaking change
 - ``doc``: A change to the documentation
 - ``misc``: Changes internal to the repo like CI, test and build changes
-- ``deprecation``: For deprecations of an existing feature of behavior
+- ``deprecation``: For deprecations of an existing feature or behavior
 
 A pull request may have more than one of these components, for example a code
 change may introduce a new feature that deprecates an old feature, in which
@@ -89,6 +89,17 @@ code changes. See the following for an example news fragment:
     $ cat changelog.d/1288.change.rst
     Add support for maintainer in PKG-INFO
 
+-------------------
+Auto-Merge Requests
+-------------------
+
+To support running all code through CI, even lightweight contributions,
+the project employs Mergify to auto-merge pull requests tagged as
+auto-merge.
+
+Use ``hub pull-request -l auto-merge`` to create such a pull request
+from the command line after pushing a new branch.
+
 -------
 Testing
 -------
index c6d594e8d59d8a64d5d46421be208dfbd15c9dd1..bc27165b223c4930227b6df73088dc1ecb41643a 100644 (file)
@@ -1,5 +1,5 @@
 sphinx!=1.8.0
 rst.linker>=1.9
-jaraco.packaging>=3.2
+jaraco.packaging>=6.1
 
 setuptools>=34
index bca211bdc3d9133b392cf80e588419773cc0f3b1..cf166e9c16d7219d93c275dc52dee4cb0298ca68 100644 (file)
@@ -2358,7 +2358,7 @@ Metadata
     but their use is not advised.
 
 ==============================  =================  =================  =============== =====
-Key                             Aliases            Type               Minumum Version Notes
+Key                             Aliases            Type               Minimum Version Notes
 ==============================  =================  =================  =============== =====
 name                                               str
 version                                            attr:, file:, str  39.2.0          (1)
@@ -2370,7 +2370,7 @@ author_email                    author-email       str
 maintainer                                         str
 maintainer_email                maintainer-email   str
 classifiers                     classifier         file:, list-comma
-license                                            file:, str
+license                                            str
 description                     summary            file:, str
 long_description                long-description   file:, str
 long_description_content_type                      str                38.6.0
index 07c23bb5f5cfa63dff16e813dbd2b06f903c2803..4ef804ecdc3668f03c0143dba635cfdab816d0d9 100644 (file)
@@ -1,5 +1,6 @@
 [build-system]
 requires = ["wheel"]
+build-backend = "setuptools.build_meta"
 
 [tool.towncrier]
     package = "setuptools"
index 57ab623435174f7f1abb53a5c500653c8fcc77f5..78eb7596fd5632ea122afc2bcbd501c67461609b 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 40.6.3
+current_version = 40.7.0
 commit = True
 tag = True
 
index 67d5691d3b21b71065cb073b86247cc1e4a1b195..00db0f0a33f5a8f3ea9baef4bdbfe80ff3fd039e 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -89,7 +89,7 @@ def pypi_link(pkg_filename):
 
 setup_params = dict(
     name="setuptools",
-    version="40.6.3",
+    version="40.7.0",
     description=(
         "Easily download, build, install, upgrade, and uninstall "
         "Python packages"
index e438036a8ce9aeca7230c07d78e096c21838eecf..a71b2bbdc6170963a66959c48080c1dedc7bb703 100644 (file)
@@ -5,12 +5,14 @@ import sys
 import functools
 import distutils.core
 import distutils.filelist
+import re
+from distutils.errors import DistutilsOptionError
 from distutils.util import convert_path
 from fnmatch import fnmatchcase
 
 from ._deprecation_warning import SetuptoolsDeprecationWarning
 
-from setuptools.extern.six import PY3
+from setuptools.extern.six import PY3, string_types
 from setuptools.extern.six.moves import filter, map
 
 import setuptools.version
@@ -161,6 +163,37 @@ class Command(_Command):
         _Command.__init__(self, dist)
         vars(self).update(kw)
 
+    def _ensure_stringlike(self, option, what, default=None):
+        val = getattr(self, option)
+        if val is None:
+            setattr(self, option, default)
+            return default
+        elif not isinstance(val, string_types):
+            raise DistutilsOptionError("'%s' must be a %s (got `%s`)"
+                                       % (option, what, val))
+        return val
+
+    def ensure_string_list(self, option):
+        r"""Ensure that 'option' is a list of strings.  If 'option' is
+        currently a string, we split it either on /,\s*/ or /\s+/, so
+        "foo bar baz", "foo,bar,baz", and "foo,   bar baz" all become
+        ["foo", "bar", "baz"].
+        """
+        val = getattr(self, option)
+        if val is None:
+            return
+        elif isinstance(val, string_types):
+            setattr(self, option, re.split(r',\s*|\s+', val))
+        else:
+            if isinstance(val, list):
+                ok = all(isinstance(v, string_types) for v in val)
+            else:
+                ok = False
+            if not ok:
+                raise DistutilsOptionError(
+                      "'%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)
         vars(cmd).update(kw)
index 463d3757a34b3ea5cbee499afc8890134604a393..c883d92f142585393bad4306beee4365b0d2fec9 100644 (file)
@@ -149,6 +149,15 @@ def prepare_metadata_for_build_wheel(metadata_directory, config_settings=None):
     return dist_infos[0]
 
 
+def _file_with_extension(directory, extension):
+    matching = (
+        f for f in os.listdir(directory)
+        if f.endswith(extension)
+    )
+    file, = matching
+    return file
+
+
 def build_wheel(wheel_directory, config_settings=None,
                 metadata_directory=None):
     config_settings = _fix_config(config_settings)
@@ -160,23 +169,15 @@ def build_wheel(wheel_directory, config_settings=None,
         shutil.rmtree(wheel_directory)
         shutil.copytree('dist', wheel_directory)
 
-    wheels = [f for f in os.listdir(wheel_directory)
-              if f.endswith('.whl')]
-
-    assert len(wheels) == 1
-    return wheels[0]
+    return _file_with_extension(wheel_directory, '.whl')
 
 
 def build_sdist(sdist_directory, config_settings=None):
     config_settings = _fix_config(config_settings)
     sdist_directory = os.path.abspath(sdist_directory)
-    sys.argv = sys.argv[:1] + ['sdist'] + \
+    sys.argv = sys.argv[:1] + ['sdist', '--formats', 'gztar'] + \
         config_settings["--global-option"] + \
         ["--dist-dir", sdist_directory]
     _run_setup()
 
-    sdists = [f for f in os.listdir(sdist_directory)
-              if f.endswith('.tar.gz')]
-
-    assert len(sdists) == 1
-    return sdists[0]
+    return _file_with_extension(sdist_directory, '.tar.gz')
index fdc9fc432b539cfe0274a6691d84aa16eaaa0a8c..009e4f9368f5b29fffd160f3b712fb0cd19807bd 100644 (file)
@@ -7,7 +7,7 @@ import io
 
 from setuptools.extern import six
 
-from pkg_resources import Distribution, PathMetadata, normalize_path
+import pkg_resources
 from setuptools.command.easy_install import easy_install
 from setuptools import namespaces
 import setuptools
@@ -65,9 +65,9 @@ class develop(namespaces.DevelopInstaller, easy_install):
         if self.egg_path is None:
             self.egg_path = os.path.abspath(ei.egg_base)
 
-        target = normalize_path(self.egg_base)
-        egg_path = normalize_path(os.path.join(self.install_dir,
-                                               self.egg_path))
+        target = pkg_resources.normalize_path(self.egg_base)
+        egg_path = pkg_resources.normalize_path(
+            os.path.join(self.install_dir, self.egg_path))
         if egg_path != target:
             raise DistutilsOptionError(
                 "--egg-path must be a relative path from the install"
@@ -75,9 +75,9 @@ class develop(namespaces.DevelopInstaller, easy_install):
             )
 
         # Make a distribution for the package's source
-        self.dist = Distribution(
+        self.dist = pkg_resources.Distribution(
             target,
-            PathMetadata(target, os.path.abspath(ei.egg_info)),
+            pkg_resources.PathMetadata(target, os.path.abspath(ei.egg_info)),
             project_name=ei.egg_name
         )
 
@@ -97,13 +97,14 @@ class develop(namespaces.DevelopInstaller, easy_install):
         path_to_setup = egg_base.replace(os.sep, '/').rstrip('/')
         if path_to_setup != os.curdir:
             path_to_setup = '../' * (path_to_setup.count('/') + 1)
-        resolved = normalize_path(
+        resolved = pkg_resources.normalize_path(
             os.path.join(install_dir, egg_path, path_to_setup)
         )
-        if resolved != normalize_path(os.curdir):
+        if resolved != pkg_resources.normalize_path(os.curdir):
             raise DistutilsOptionError(
                 "Can't get a consistent path to setup script from"
-                " installation directory", resolved, normalize_path(os.curdir))
+                " installation directory", resolved,
+                pkg_resources.normalize_path(os.curdir))
         return path_to_setup
 
     def install_for_development(self):
@@ -114,7 +115,7 @@ class develop(namespaces.DevelopInstaller, easy_install):
             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_path = pkg_resources.normalize_path(bpy_cmd.build_lib)
 
             # Build extensions
             self.reinitialize_command('egg_info', egg_base=build_path)
@@ -128,7 +129,8 @@ class develop(namespaces.DevelopInstaller, easy_install):
             self.egg_path = build_path
             self.dist.location = build_path
             # XXX
-            self.dist._provider = PathMetadata(build_path, ei_cmd.egg_info)
+            self.dist._provider = pkg_resources.PathMetadata(
+                build_path, ei_cmd.egg_info)
         else:
             # Without 2to3 inplace works fine:
             self.run_command('egg_info')
@@ -200,6 +202,7 @@ class VersionlessRequirement:
     name as the 'requirement' so that scripts will work across
     multiple versions.
 
+    >>> from pkg_resources import Distribution
     >>> dist = Distribution(project_name='foo', version='1.0')
     >>> str(dist.as_requirement())
     'foo==1.0'
index dd17f7a925b285fa08562b64d7f5f8263efc3a6e..6db8888bb29cc59ef23e42a4b3335c311a6ef0ad 100644 (file)
@@ -2,7 +2,6 @@ import io
 import os
 import hashlib
 import getpass
-import platform
 
 from base64 import standard_b64encode
 
@@ -16,6 +15,7 @@ from setuptools.extern.six.moves.urllib.request import urlopen, Request
 from setuptools.extern.six.moves.urllib.error import HTTPError
 from setuptools.extern.six.moves.urllib.parse import urlparse
 
+
 class upload(orig.upload):
     """
     Override default upload behavior to obtain password
@@ -80,7 +80,7 @@ class upload(orig.upload):
             'version': meta.get_version(),
 
             # file content
-            'content': (os.path.basename(filename),content),
+            'content': (os.path.basename(filename), content),
             'filetype': command,
             'pyversion': pyversion,
             'md5_digest': hashlib.md5(content).hexdigest(),
index d1ac6734dc520ce5a7b3155ef31b0723a7182862..b6626043fce15e07dbe89ff1f06bb97d77e80c06 100644 (file)
@@ -246,6 +246,26 @@ class ConfigHandler:
         value = value.lower()
         return value in ('1', 'true', 'yes')
 
+    @classmethod
+    def _exclude_files_parser(cls, key):
+        """Returns a parser function to make sure field inputs
+        are not files.
+
+        Parses a value after getting the key so error messages are
+        more informative.
+
+        :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))
+            return value
+        return parser
+
     @classmethod
     def _parse_file(cls, value):
         """Represents value as a string, allowing including text
@@ -255,7 +275,6 @@ class ConfigHandler:
         directory with setup.py.
 
         Examples:
-            file: LICENSE
             file: README.rst, CHANGELOG.md, src/file.txt
 
         :param str value:
@@ -394,7 +413,7 @@ class ConfigHandler:
 
             section_parser_method = getattr(
                 self,
-                # Dots in section names are tranlsated into dunderscores.
+                # Dots in section names are translated into dunderscores.
                 ('parse_section%s' % method_postfix).replace('.', '__'),
                 None)
 
@@ -407,8 +426,8 @@ class ConfigHandler:
 
     def _deprecated_config_handler(self, func, msg, warning_class):
         """ this function will wrap around parameters that are deprecated
-        
-        :param msg: deprecation message 
+
+        :param msg: deprecation message
         :param warning_class: class of warning exception to be raised
         :param func: function to be wrapped around
         """
@@ -416,7 +435,7 @@ class ConfigHandler:
         def config_handler(*args, **kwargs):
             warnings.warn(msg, warning_class)
             return func(*args, **kwargs)
-        
+
         return config_handler
 
 
@@ -449,18 +468,20 @@ class ConfigMetadataHandler(ConfigHandler):
         parse_list = self._parse_list
         parse_file = self._parse_file
         parse_dict = self._parse_dict
+        exclude_files_parser = self._exclude_files_parser
 
         return {
             'platforms': parse_list,
             'keywords': parse_list,
             'provides': parse_list,
-            'requires': self._deprecated_config_handler(parse_list,
-                "The requires parameter is deprecated, please use " +
+            'requires': self._deprecated_config_handler(
+                parse_list,
+                "The requires parameter is deprecated, please use "
                 "install_requires for runtime dependencies.",
                 DeprecationWarning),
             'obsoletes': parse_list,
             'classifiers': self._get_parser_compound(parse_file, parse_list),
-            'license': parse_file,
+            'license': exclude_files_parser('license'),
             'description': parse_file,
             'long_description': parse_file,
             'version': self._parse_version,
index 7062ae8d8cc66f3f34d44c7c33032c9e1c7b087d..b8551228eb220423f5421b47c76e5ce095e44709 100644 (file)
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 __all__ = ['Distribution']
 
+import io
+import sys
 import re
 import os
 import warnings
@@ -9,9 +11,12 @@ import distutils.log
 import distutils.core
 import distutils.cmd
 import distutils.dist
+from distutils.errors import DistutilsOptionError
+from distutils.util import strtobool
+from distutils.debug import DEBUG
+from distutils.fancy_getopt import translate_longopt
 import itertools
 
-
 from collections import defaultdict
 from email import message_from_file
 
@@ -31,8 +36,8 @@ from setuptools.depends import Require
 from setuptools import windows_support
 from setuptools.monkey import get_unpatched
 from setuptools.config import parse_configuration
+from .unicode_utils import detect_encoding
 import pkg_resources
-from .py36compat import Distribution_parse_config_files
 
 __import__('setuptools.extern.packaging.specifiers')
 __import__('setuptools.extern.packaging.version')
@@ -332,7 +337,7 @@ def check_packages(dist, attr, value):
 _Distribution = get_unpatched(distutils.core.Distribution)
 
 
-class Distribution(Distribution_parse_config_files, _Distribution):
+class Distribution(_Distribution):
     """Distribution with support for features, tests, and package data
 
     This is an enhanced version of 'distutils.dist.Distribution' that
@@ -556,12 +561,125 @@ class Distribution(Distribution_parse_config_files, _Distribution):
         req.marker = None
         return req
 
+    def _parse_config_files(self, filenames=None):
+        """
+        Adapted from distutils.dist.Distribution.parse_config_files,
+        this method provides the same functionality in subtly-improved
+        ways.
+        """
+        from setuptools.extern.six.moves.configparser import ConfigParser
+
+        # Ignore install directory options if we have a venv
+        if six.PY3 and sys.prefix != sys.base_prefix:
+            ignore_options = [
+                'install-base', 'install-platbase', 'install-lib',
+                'install-platlib', 'install-purelib', 'install-headers',
+                'install-scripts', 'install-data', 'prefix', 'exec-prefix',
+                'home', 'user', 'root']
+        else:
+            ignore_options = []
+
+        ignore_options = frozenset(ignore_options)
+
+        if filenames is None:
+            filenames = self.find_config_files()
+
+        if DEBUG:
+            self.announce("Distribution.parse_config_files():")
+
+        parser = ConfigParser()
+        for filename in filenames:
+            with io.open(filename, 'rb') as fp:
+                encoding = detect_encoding(fp)
+                if DEBUG:
+                    self.announce("  reading %s [%s]" % (
+                        filename, encoding or 'locale')
+                    )
+                reader = io.TextIOWrapper(fp, encoding=encoding)
+                (parser.read_file if six.PY3 else parser.readfp)(reader)
+            for section in parser.sections():
+                options = parser.options(section)
+                opt_dict = self.get_option_dict(section)
+
+                for opt in options:
+                    if opt != '__name__' and opt not in ignore_options:
+                        val = parser.get(section, opt)
+                        opt = opt.replace('-', '_')
+                        opt_dict[opt] = (filename, val)
+
+            # Make the ConfigParser forget everything (so we retain
+            # the original filenames that options come from)
+            parser.__init__()
+
+        # If there was a "global" section in the config file, use it
+        # to set Distribution options.
+
+        if 'global' in self.command_options:
+            for (opt, (src, val)) in self.command_options['global'].items():
+                alias = self.negative_opt.get(opt)
+                try:
+                    if alias:
+                        setattr(self, alias, not strtobool(val))
+                    elif opt in ('verbose', 'dry_run'):  # ugh!
+                        setattr(self, opt, strtobool(val))
+                    else:
+                        setattr(self, opt, val)
+                except ValueError as msg:
+                    raise DistutilsOptionError(msg)
+
+    def _set_command_options(self, command_obj, option_dict=None):
+        """
+        Set the options for 'command_obj' from 'option_dict'.  Basically
+        this means copying elements of a dictionary ('option_dict') to
+        attributes of an instance ('command').
+
+        'command_obj' must be a Command instance.  If 'option_dict' is not
+        supplied, uses the standard option dictionary for this command
+        (from 'self.command_options').
+
+        (Adopted from distutils.dist.Distribution._set_command_options)
+        """
+        command_name = command_obj.get_command_name()
+        if option_dict is None:
+            option_dict = self.get_option_dict(command_name)
+
+        if DEBUG:
+            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))
+            try:
+                bool_opts = [translate_longopt(o)
+                             for o in command_obj.boolean_options]
+            except AttributeError:
+                bool_opts = []
+            try:
+                neg_opt = command_obj.negative_opt
+            except AttributeError:
+                neg_opt = {}
+
+            try:
+                is_string = isinstance(value, six.string_types)
+                if option in neg_opt and is_string:
+                    setattr(command_obj, neg_opt[option], not strtobool(value))
+                elif option in bool_opts and is_string:
+                    setattr(command_obj, option, strtobool(value))
+                elif hasattr(command_obj, option):
+                    setattr(command_obj, option, value)
+                else:
+                    raise DistutilsOptionError(
+                        "error in %s: command '%s' has no such option '%s'"
+                        % (source, command_name, option))
+            except ValueError as msg:
+                raise DistutilsOptionError(msg)
+
     def parse_config_files(self, filenames=None, ignore_option_errors=False):
         """Parses configuration files from various levels
         and loads configuration.
 
         """
-        _Distribution.parse_config_files(self, filenames=filenames)
+        self._parse_config_files(filenames=filenames)
 
         parse_configuration(self, self.command_options,
                             ignore_option_errors=ignore_option_errors)
index 1608b91acedf82845cc3ea03ab8927393db889f3..7e9517cefadb864ab2c0b69d66c78ad7d4d62c42 100644 (file)
@@ -850,13 +850,16 @@ class PackageIndex(Environment):
 
     def _download_svn(self, url, filename):
         warnings.warn("SVN download support is deprecated", UserWarning)
+        def splituser(host):
+            user, delim, host = host.rpartition('@')
+            return user, host
         url = url.split('#', 1)[0]  # remove any fragment for svn's sake
         creds = ''
         if url.lower().startswith('svn:') and '@' in url:
             scheme, netloc, path, p, q, f = urllib.parse.urlparse(url)
             if not netloc and path.startswith('//') and '/' in path[2:]:
                 netloc, path = path[2:].split('/', 1)
-                auth, host = urllib.parse.splituser(netloc)
+                auth, host = splituser(netloc)
                 if auth:
                     if ':' in auth:
                         user, pw = auth.split(':', 1)
@@ -1047,15 +1050,16 @@ class PyPIConfig(configparser.RawConfigParser):
 def open_with_auth(url, opener=urllib.request.urlopen):
     """Open a urllib2 request, handling HTTP authentication"""
 
-    scheme, netloc, path, params, query, frag = urllib.parse.urlparse(url)
+    parsed = urllib.parse.urlparse(url)
+    scheme, netloc, path, params, query, frag = parsed
 
     # Double scheme does not raise on Mac OS X as revealed by a
     # failing test. We would expect "nonnumeric port". Refs #20.
     if netloc.endswith(':'):
         raise http_client.InvalidURL("nonnumeric port: ''")
 
-    if scheme in ('http', 'https'):
-        auth, host = urllib.parse.splituser(netloc)
+    if scheme in ('http', 'https') and parsed.username:
+        auth = ':'.join((parsed.username, parsed.password))
     else:
         auth = None
 
@@ -1068,7 +1072,7 @@ def open_with_auth(url, opener=urllib.request.urlopen):
 
     if auth:
         auth = "Basic " + _encode_auth(auth)
-        parts = scheme, host, path, params, query, frag
+        parts = scheme, parsed.hostname, path, params, query, frag
         new_url = urllib.parse.urlunparse(parts)
         request = urllib.request.Request(new_url)
         request.add_header("Authorization", auth)
@@ -1082,7 +1086,7 @@ def open_with_auth(url, opener=urllib.request.urlopen):
         # Put authentication info back into request URL if same host,
         # so that links found on the page will work
         s2, h2, path2, param2, query2, frag2 = urllib.parse.urlparse(fp.url)
-        if s2 == scheme and h2 == host:
+        if s2 == scheme and h2 == parsed.hostname:
             parts = s2, netloc, path2, param2, query2, frag2
             fp.url = urllib.parse.urlunparse(parts)
 
index 8bf4277dbd25a9fd2af555c023889d3933031ca7..48745a29027400f1c921b52b7d492a26d4e5d5a2 100644 (file)
@@ -161,7 +161,7 @@ def is_manylinux1_compatible():
 
 def get_darwin_arches(major, minor, machine):
     """Return a list of supported arches (including group arches) for
-    the given major, minor and machine architecture of an macOS machine.
+    the given major, minor and machine architecture of a macOS machine.
     """
     arches = []
 
diff --git a/setuptools/py36compat.py b/setuptools/py36compat.py
deleted file mode 100644 (file)
index f527969..0000000
+++ /dev/null
@@ -1,82 +0,0 @@
-import sys
-from distutils.errors import DistutilsOptionError
-from distutils.util import strtobool
-from distutils.debug import DEBUG
-
-
-class Distribution_parse_config_files:
-    """
-    Mix-in providing forward-compatibility for functionality to be
-    included by default on Python 3.7.
-
-    Do not edit the code in this class except to update functionality
-    as implemented in distutils.
-    """
-    def parse_config_files(self, filenames=None):
-        from configparser import ConfigParser
-
-        # Ignore install directory options if we have a venv
-        if sys.prefix != sys.base_prefix:
-            ignore_options = [
-                'install-base', 'install-platbase', 'install-lib',
-                'install-platlib', 'install-purelib', 'install-headers',
-                'install-scripts', 'install-data', 'prefix', 'exec-prefix',
-                'home', 'user', 'root']
-        else:
-            ignore_options = []
-
-        ignore_options = frozenset(ignore_options)
-
-        if filenames is None:
-            filenames = self.find_config_files()
-
-        if DEBUG:
-            self.announce("Distribution.parse_config_files():")
-
-        parser = ConfigParser(interpolation=None)
-        for filename in filenames:
-            if DEBUG:
-                self.announce("  reading %s" % filename)
-            parser.read(filename)
-            for section in parser.sections():
-                options = parser.options(section)
-                opt_dict = self.get_option_dict(section)
-
-                for opt in options:
-                    if opt != '__name__' and opt not in ignore_options:
-                        val = parser.get(section,opt)
-                        opt = opt.replace('-', '_')
-                        opt_dict[opt] = (filename, val)
-
-            # Make the ConfigParser forget everything (so we retain
-            # the original filenames that options come from)
-            parser.__init__()
-
-        # If there was a "global" section in the config file, use it
-        # to set Distribution options.
-
-        if 'global' in self.command_options:
-            for (opt, (src, val)) in self.command_options['global'].items():
-                alias = self.negative_opt.get(opt)
-                try:
-                    if alias:
-                        setattr(self, alias, not strtobool(val))
-                    elif opt in ('verbose', 'dry_run'): # ugh!
-                        setattr(self, opt, strtobool(val))
-                    else:
-                        setattr(self, opt, val)
-                except ValueError as msg:
-                    raise DistutilsOptionError(msg)
-
-
-if sys.version_info < (3,):
-    # Python 2 behavior is sufficient
-    class Distribution_parse_config_files:
-        pass
-
-
-if False:
-    # When updated behavior is available upstream,
-    # disable override here.
-    class Distribution_parse_config_files:
-        pass
index 6362f1f426910ab36f8b26d1382adf08f43ba992..226db694bb38791147c6bf2881c4b86025dd2f8f 100644 (file)
@@ -59,7 +59,7 @@ if not match_hostname:
     def _dnsname_match(dn, hostname, max_wildcards=1):
         """Matching according to RFC 6125, section 6.4.3
 
-        http://tools.ietf.org/html/rfc6125#section-6.4.3
+        https://tools.ietf.org/html/rfc6125#section-6.4.3
         """
         pats = []
         if not dn:
index 465a6b41e668b8f385697d202148d09fcca5f910..bad2189d45ff21e84871f342dad461c18d84ea05 100644 (file)
@@ -6,10 +6,13 @@ import pkg_resources.py31compat
 
 def build_files(file_defs, prefix=""):
     """
-    Build a set of files/directories, as described by the file_defs dictionary.
+    Build a set of files/directories, as described by the
+    file_defs dictionary.
 
-    Each key/value pair in the dictionary is interpreted as a filename/contents
-    pair. If the contents value is a dictionary, a directory is created, and the
+    Each key/value pair in the dictionary is interpreted as
+    a filename/contents
+    pair. If the contents value is a dictionary, a directory
+    is created, and the
     dictionary interpreted as the files within it, recursively.
 
     For example:
index 35312120bb20a5b8b0ffd14f352e6450f16ece9b..fc3a5975ef8d2d453b2439d379b757258e5b9998 100644 (file)
@@ -19,10 +19,11 @@ class IndexServer(BaseHTTPServer.HTTPServer):
         s.stop()
     """
 
-    def __init__(self, server_address=('', 0),
+    def __init__(
+            self, server_address=('', 0),
             RequestHandlerClass=SimpleHTTPServer.SimpleHTTPRequestHandler):
-        BaseHTTPServer.HTTPServer.__init__(self, server_address,
-            RequestHandlerClass)
+        BaseHTTPServer.HTTPServer.__init__(
+            self, server_address, RequestHandlerClass)
         self._run = True
 
     def start(self):
@@ -56,10 +57,11 @@ class MockServer(BaseHTTPServer.HTTPServer, threading.Thread):
     A simple HTTP Server that records the requests made to it.
     """
 
-    def __init__(self, server_address=('', 0),
+    def __init__(
+            self, server_address=('', 0),
             RequestHandlerClass=RequestRecorder):
-        BaseHTTPServer.HTTPServer.__init__(self, server_address,
-            RequestHandlerClass)
+        BaseHTTPServer.HTTPServer.__init__(
+            self, server_address, RequestHandlerClass)
         threading.Thread.__init__(self)
         self.setDaemon(True)
         self.requests = []
index aebcc350ecb1c9e6435d22e5af817cc2fed41c7d..3779e679eaca702389974eb4c0b99237cf13f623 100644 (file)
@@ -1,6 +1,4 @@
 import pytest
-import os
-import shutil
 
 import mock
 from distutils.errors import DistutilsSetupError
@@ -40,13 +38,14 @@ class TestBuildCLib:
         # with that out of the way, let's see if the crude dependency
         # system works
         cmd.compiler = mock.MagicMock(spec=cmd.compiler)
-        mock_newer.return_value = ([],[])
+        mock_newer.return_value = ([], [])
 
         obj_deps = {'': ('global.h',), 'example.c': ('example.h',)}
-        libs = [('example', {'sources': ['example.c'] ,'obj_deps': obj_deps})]
+        libs = [('example', {'sources': ['example.c']'obj_deps': obj_deps})]
 
         cmd.build_libraries(libs)
-        assert [['example.c', 'global.h', 'example.h']] in mock_newer.call_args[0]
+        assert [['example.c', 'global.h', 'example.h']] in \
+            mock_newer.call_args[0]
         assert not cmd.compiler.compile.called
         assert cmd.compiler.create_static_lib.call_count == 1
 
index 2a0794469622ca2824f8a774129f92de090beac5..82b44c894420c110422587e1534b8c4c5c9c34cb 100644 (file)
@@ -172,18 +172,21 @@ def test_build_sdist_version_change(build_backend):
     sdist_name = build_backend.build_sdist(sdist_into_directory)
     assert os.path.isfile(os.path.join(sdist_into_directory, sdist_name))
 
-    # if the setup.py changes subsequent call of the build meta should still succeed, given the
+    # if the setup.py changes subsequent call of the build meta
+    # should still succeed, given the
     # sdist_directory the frontend specifies is empty
     with open(os.path.abspath("setup.py"), 'rt') as file_handler:
         content = file_handler.read()
     with open(os.path.abspath("setup.py"), 'wt') as file_handler:
-        file_handler.write(content.replace("version='0.0.0'", "version='0.0.1'"))
+        file_handler.write(
+            content.replace("version='0.0.0'", "version='0.0.1'"))
 
     shutil.rmtree(sdist_into_directory)
     os.makedirs(sdist_into_directory)
 
     sdist_name = build_backend.build_sdist("out_sdist")
-    assert os.path.isfile(os.path.join(os.path.abspath("out_sdist"), sdist_name))
+    assert os.path.isfile(
+        os.path.join(os.path.abspath("out_sdist"), sdist_name))
 
 
 def test_build_sdist_setup_py_exists(tmpdir_cwd):
@@ -215,3 +218,21 @@ def test_build_sdist_setup_py_manifest_excluded(tmpdir_cwd):
     with tarfile.open(os.path.join("temp", targz_path)) as tar:
         assert not any('setup.py' in name for name in tar.getnames())
 
+
+def test_build_sdist_builds_targz_even_if_zip_indicated(tmpdir_cwd):
+    files = {
+        'setup.py': DALS("""
+            __import__('setuptools').setup(
+                name='foo',
+                version='0.0.0',
+                py_modules=['hello']
+            )"""),
+        'hello.py': '',
+        'setup.cfg': DALS("""
+            [sdist]
+            formats=zip
+            """)
+    }
+
+    build_files(files)
+    build_sdist("temp")
index 736c184dcad7035acabcf37e823ffe268c7244e7..6b17770969760f79d116752d7c4d19877e72f394 100644 (file)
@@ -1,10 +1,18 @@
+# -*- coding: UTF-8 -*-
+from __future__ import unicode_literals
+
 import contextlib
 import pytest
+
 from distutils.errors import DistutilsOptionError, DistutilsFileError
 from mock import patch
 from setuptools.dist import Distribution, _Distribution
 from setuptools.config import ConfigHandler, read_configuration
+from setuptools.extern.six.moves.configparser import InterpolationMissingOptionError
+from setuptools.tests import is_ascii
 from . import py2_only, py3_only
+from .textwrap import DALS
+
 
 class ErrConfigHandler(ConfigHandler):
     """Erroneous handler. Fails to implement required methods."""
@@ -16,12 +24,12 @@ def make_package_dir(name, base_dir, ns=False):
         dir_package = dir_package.mkdir(dir_name)
     init_file = None
     if not ns:
-      init_file = dir_package.join('__init__.py')
-      init_file.write('')
+        init_file = dir_package.join('__init__.py')
+        init_file.write('')
     return dir_package, init_file
 
 
-def fake_env(tmpdir, setup_cfg, setup_py=None, package_path='fake_package'):
+def fake_env(tmpdir, setup_cfg, setup_py=None, encoding='ascii', package_path='fake_package'):
 
     if setup_py is None:
         setup_py = (
@@ -31,7 +39,7 @@ def fake_env(tmpdir, setup_cfg, setup_py=None, package_path='fake_package'):
 
     tmpdir.join('setup.py').write(setup_py)
     config = tmpdir.join('setup.cfg')
-    config.write(setup_cfg)
+    config.write(setup_cfg.encode(encoding), mode='wb')
 
     package_dir, init_file = make_package_dir(package_path, tmpdir)
 
@@ -146,6 +154,24 @@ class TestMetadata:
             assert metadata.download_url == 'http://test.test.com/test/'
             assert metadata.maintainer_email == 'test@test.com'
 
+    def test_license_cfg(self, tmpdir):
+        fake_env(
+            tmpdir,
+            DALS("""
+            [metadata]
+            name=foo
+            version=0.0.1
+            license=Apache 2.0
+            """)
+        )
+
+        with get_dist(tmpdir) as dist:
+            metadata = dist.metadata
+
+            assert metadata.name == "foo"
+            assert metadata.version == "0.0.1"
+            assert metadata.license == "Apache 2.0"
+
     def test_file_mixed(self, tmpdir):
 
         fake_env(
@@ -288,7 +314,7 @@ class TestMetadata:
         tmpdir.join('fake_package', 'version.txt').write('1.2.3\n4.5.6\n')
         with pytest.raises(DistutilsOptionError):
             with get_dist(tmpdir) as dist:
-                _ = dist.metadata.version
+                dist.metadata.version
 
     def test_version_with_package_dir_simple(self, tmpdir):
 
@@ -408,6 +434,72 @@ class TestMetadata:
                 assert metadata.description == 'Some description'
                 assert metadata.requires == ['some', 'requirement']
 
+    def test_interpolation(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'description = %(message)s\n'
+        )
+        with pytest.raises(InterpolationMissingOptionError):
+            with get_dist(tmpdir):
+                pass
+
+    skip_if_not_ascii = pytest.mark.skipif(not is_ascii, reason='Test not supported with this locale')
+
+    @skip_if_not_ascii
+    def test_non_ascii_1(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '[metadata]\n'
+            'description = Ã©Ã Ã¯Ã´Ã±\n',
+            encoding='utf-8'
+        )
+        with pytest.raises(UnicodeDecodeError):
+            with get_dist(tmpdir):
+                pass
+
+    def test_non_ascii_2(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '# -*- coding: invalid\n'
+        )
+        with pytest.raises(LookupError):
+            with get_dist(tmpdir):
+                pass
+
+    def test_non_ascii_3(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '\n'
+            '# -*- coding: invalid\n'
+        )
+        with get_dist(tmpdir):
+            pass
+
+    @skip_if_not_ascii
+    def test_non_ascii_4(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '# -*- coding: utf-8\n'
+            '[metadata]\n'
+            'description = Ã©Ã Ã¯Ã´Ã±\n',
+            encoding='utf-8'
+        )
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.description == 'éàïôñ'
+
+    @skip_if_not_ascii
+    def test_non_ascii_5(self, tmpdir):
+        fake_env(
+            tmpdir,
+            '# vim: set fileencoding=iso-8859-15 :\n'
+            '[metadata]\n'
+            'description = Ã©Ã Ã¯Ã´Ã±\n',
+            encoding='iso-8859-15'
+        )
+        with get_dist(tmpdir) as dist:
+            assert dist.metadata.description == 'éàïôñ'
+
 
 class TestOptions:
 
@@ -431,7 +523,7 @@ class TestOptions:
             'tests_require = mock==0.7.2; pytest\n'
             'setup_requires = docutils>=0.3; spack ==1.1, ==1.3; there\n'
             'dependency_links = http://some.com/here/1, '
-                'http://some.com/there/2\n'
+            'http://some.com/there/2\n'
             'python_requires = >=1.0, !=2.8\n'
             'py_modules = module1, module2\n'
         )
@@ -639,7 +731,7 @@ class TestOptions:
         dir_sub_two, _ = make_package_dir('sub_two', dir_package, ns=True)
 
         with get_dist(tmpdir) as dist:
-            assert set(dist.packages) == { 
+            assert set(dist.packages) == {
                 'fake_package', 'fake_package.sub_two', 'fake_package.sub_one'
             }
 
@@ -691,7 +783,7 @@ class TestOptions:
             tmpdir,
             '[options.entry_points]\n'
             'group1 = point1 = pack.module:func, '
-                '.point2 = pack.module2:func_rest [rest]\n'
+            '.point2 = pack.module2:func_rest [rest]\n'
             'group2 = point3 = pack.module:func2\n'
         )
 
@@ -737,7 +829,10 @@ class TestOptions:
             ]
             assert sorted(dist.data_files) == sorted(expected)
 
+
 saved_dist_init = _Distribution.__init__
+
+
 class TestExternalSetters:
     # During creation of the setuptools Distribution() object, we call
     # the init of the parent distutils Distribution object via
index e0cfa88049d7ab7e93b8af06cdac45ee96c0714d..bff1dfb19901b97e5846fe771cac5a8d1eec7a1e 100644 (file)
@@ -5,12 +5,12 @@ from setuptools import depends
 
 class TestGetModuleConstant:
 
-       def test_basic(self):
-               """
-               Invoke get_module_constant on a module in
-               the test package.
-               """
-               mod_name = 'setuptools.tests.mod_with_constant'
-               val = depends.get_module_constant(mod_name, 'value')
-               assert val == 'three, sir!'
-               assert 'setuptools.tests.mod_with_constant' not in sys.modules
+    def test_basic(self):
+        """
+        Invoke get_module_constant on a module in
+        the test package.
+        """
+        mod_name = 'setuptools.tests.mod_with_constant'
+        val = depends.get_module_constant(mod_name, 'value')
+        assert val == 'three, sir!'
+        assert 'setuptools.tests.mod_with_constant' not in sys.modules
index cf830b437d7954d21bc9758734e287bada2047f0..390c3dfcf0140b93b63bdcfb188a4f77dcc7b357 100644 (file)
@@ -14,6 +14,7 @@ from .test_easy_install import make_nspkg_sdist
 
 import pytest
 
+
 def test_dist_fetch_build_egg(tmpdir):
     """
     Check multiple calls to `Distribution.fetch_build_egg` work as expected.
@@ -90,30 +91,32 @@ def __read_test_cases():
             'classifiers': [
                 'Programming Language :: Python :: 3',
                 'Programming Language :: Python :: 3.7',
-                'License :: OSI Approved :: MIT License'
-        ]})),
+                'License :: OSI Approved :: MIT License',
+            ]})),
         ('Metadata version 1.1: Download URL', merge_dicts(base_attrs, {
             'download_url': 'https://example.com'
         })),
         ('Metadata Version 1.2: Requires-Python', merge_dicts(base_attrs, {
             'python_requires': '>=3.7'
         })),
-        pytest.param('Metadata Version 1.2: Project-Url',
+        pytest.param(
+            'Metadata Version 1.2: Project-Url',
             merge_dicts(base_attrs, {
                 'project_urls': {
                     'Foo': 'https://example.bar'
                 }
             }), marks=pytest.mark.xfail(
                 reason="Issue #1578: project_urls not read"
-        )),
+            )),
         ('Metadata Version 2.1: Long Description Content Type',
          merge_dicts(base_attrs, {
              'long_description_content_type': 'text/x-rst; charset=UTF-8'
          })),
-        pytest.param('Metadata Version 2.1: Provides Extra',
+        pytest.param(
+            'Metadata Version 2.1: Provides Extra',
             merge_dicts(base_attrs, {
                 'provides_extras': ['foo', 'bar']
-        }), marks=pytest.mark.xfail(reason="provides_extras not read")),
+            }), marks=pytest.mark.xfail(reason="provides_extras not read")),
         ('Missing author, missing author e-mail',
          {'name': 'foo', 'version': '1.0.0'}),
         ('Missing author',
index 2cf65ae7e52c67ac0178888f04811395218f7a02..c3fd1c6ef427678e0f75841926a0c4ac9f2e3423 100644 (file)
@@ -15,7 +15,9 @@ import distutils.errors
 import io
 import zipfile
 import mock
-from setuptools.command.easy_install import EasyInstallDeprecationWarning, ScriptWriter, WindowsScriptWriter
+from setuptools.command.easy_install import (
+    EasyInstallDeprecationWarning, ScriptWriter, WindowsScriptWriter,
+)
 import time
 from setuptools.extern import six
 from setuptools.extern.six.moves import urllib
@@ -287,7 +289,6 @@ class TestEasyInstallTest:
         cmd.easy_install(sdist_script)
         assert (target / 'mypkg_script').exists()
 
-
     def test_dist_get_script_args_deprecated(self):
         with pytest.warns(EasyInstallDeprecationWarning):
             ScriptWriter.get_script_args(None, None)
@@ -304,6 +305,7 @@ class TestEasyInstallTest:
         with pytest.warns(EasyInstallDeprecationWarning):
             WindowsScriptWriter.get_writer()
 
+
 @pytest.mark.filterwarnings('ignore:Unbuilt egg')
 class TestPTHFileWriter:
     def test_add_from_cwd_site_sets_dirty(self):
index f97b3f1d8113a8c7bdc1f093a901563e5f8f7d36..d5fa255858f2ea7f5cb5899c22819b77414938c0 100644 (file)
@@ -1,4 +1,3 @@
-import datetime
 import sys
 import ast
 import os
@@ -7,7 +6,9 @@ import re
 import stat
 import time
 
-from setuptools.command.egg_info import egg_info, manifest_maker, EggInfoDeprecationWarning, get_pkg_info_revision
+from setuptools.command.egg_info import (
+    egg_info, manifest_maker, EggInfoDeprecationWarning, get_pkg_info_revision,
+)
 from setuptools.dist import Distribution
 from setuptools.extern.six.moves import map
 
@@ -148,6 +149,37 @@ class TestEggInfo:
         ]
         assert sorted(actual) == expected
 
+    def test_license_is_a_string(self, tmpdir_cwd, env):
+        setup_config = DALS("""
+            [metadata]
+            name=foo
+            version=0.0.1
+            license=file:MIT
+            """)
+
+        setup_script = DALS("""
+            from setuptools import setup
+
+            setup()
+            """)
+
+        build_files({'setup.py': setup_script,
+                     'setup.cfg': setup_config})
+
+        # This command should fail with a ValueError, but because it's
+        # currently configured to use a subprocess, the actual traceback
+        # object is lost and we need to parse it from stderr
+        with pytest.raises(AssertionError) as exc:
+            self._run_egg_info_command(tmpdir_cwd, env)
+
+        # Hopefully this is not too fragile: the only argument to the
+        # assertion error should be a traceback, ending with:
+        #     ValueError: ....
+        #
+        #     assert not 1
+        tb = exc.value.args[0].split('\n')
+        assert tb[-3].lstrip().startswith('ValueError')
+
     def test_rebuilt(self, tmpdir_cwd, env):
         """Ensure timestamps are updated when the command is re-run."""
         self._create_project()
@@ -597,8 +629,8 @@ class TestEggInfo:
             data_stream=1,
             env=environ,
         )
-        if code:
-            raise AssertionError(data)
+        assert not code, data
+
         if output:
             assert output in data
 
@@ -620,3 +652,49 @@ class TestEggInfo:
 
     def test_get_pkg_info_revision_deprecated(self):
         pytest.warns(EggInfoDeprecationWarning, get_pkg_info_revision)
+
+    EGG_INFO_TESTS = (
+        # Check for issue #1136: invalid string type when
+        # reading declarative `setup.cfg` under Python 2.
+        {
+            'setup.py': DALS(
+                """
+                from setuptools import setup
+                setup(
+                    name="foo",
+                )
+                """),
+            'setup.cfg': DALS(
+                """
+                [options]
+                package_dir =
+                    = src
+                """),
+            'src': {},
+        },
+        # Check Unicode can be used in `setup.py` under Python 2.
+        {
+            'setup.py': DALS(
+                """
+                # -*- coding: utf-8 -*-
+                from __future__ import unicode_literals
+                from setuptools import setup, find_packages
+                setup(
+                    name="foo",
+                    package_dir={'': 'src'},
+                )
+                """),
+            'src': {},
+        }
+    )
+
+    @pytest.mark.parametrize('package_files', EGG_INFO_TESTS)
+    def test_egg_info(self, tmpdir_cwd, env, package_files):
+        """
+        """
+        build_files(package_files)
+        code, data = environment.run_setup_py(
+            cmd=['egg_info'],
+            data_stream=1,
+        )
+        assert not code, data
index b08f91c7d5a5879fa379a82d114482bc630e4f71..ab26b4f128beb1bafe14a77a4c2bef93ca6d9ffc 100644 (file)
@@ -12,10 +12,10 @@ from . import py3_only
 from setuptools.extern.six import PY3
 from setuptools import find_packages
 if PY3:
-  from setuptools import find_namespace_packages
+    from setuptools import find_namespace_packages
 
-# modeled after CPython's test.support.can_symlink
 
+# modeled after CPython's test.support.can_symlink
 def can_symlink():
     TESTFN = tempfile.mktemp()
     symlink_path = TESTFN + "can_symlink"
@@ -164,12 +164,14 @@ class TestFindPackages:
     def test_pep420_ns_package_no_includes(self):
         packages = find_namespace_packages(
             self.dist_dir, exclude=['pkg.subpkg.assets'])
-        self._assert_packages(packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg'])
+        self._assert_packages(
+            packages, ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg'])
 
     @py3_only
     def test_pep420_ns_package_no_includes_or_excludes(self):
         packages = find_namespace_packages(self.dist_dir)
-        expected = ['docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets']
+        expected = [
+            'docs', 'pkg', 'pkg.nspkg', 'pkg.subpkg', 'pkg.subpkg.assets']
         self._assert_packages(packages, expected)
 
     @py3_only
@@ -185,4 +187,3 @@ class TestFindPackages:
         shutil.rmtree(os.path.join(self.dist_dir, 'pkg/subpkg/assets'))
         packages = find_namespace_packages(self.dist_dir)
         self._assert_packages(packages, ['pkg', 'pkg.nspkg', 'pkg.subpkg'])
-
index 727ad65b13d15f4658025c70f3e93f9b668d6eb2..4338c792f126ab93454dda762df0033e298600a8 100644 (file)
@@ -64,7 +64,8 @@ class TestInstallScripts:
     @pytest.mark.skipif(sys.platform == 'win32', reason='non-Windows only')
     def test_executable_with_spaces_escaping_unix(self, tmpdir):
         """
-        Ensure that shebang on Unix is not quoted, even when a value with spaces
+        Ensure that shebang on Unix is not quoted, even when
+        a value with spaces
         is specified using --executable.
         """
         expected = '#!%s\n' % self.unix_spaces_exe
@@ -77,7 +78,8 @@ class TestInstallScripts:
     @pytest.mark.skipif(sys.platform != 'win32', reason='Windows only')
     def test_executable_arg_escaping_win32(self, tmpdir):
         """
-        Ensure that shebang on Windows is quoted when getting a path with spaces
+        Ensure that shebang on Windows is quoted when
+        getting a path with spaces
         from --executable, that is itself properly quoted.
         """
         expected = '#!"%s"\n' % self.win32_exe
index 3a9a6c50a1e45c0bc91df78c234b6bfb8e0fe21c..e54f3209aafd3cf57559ad244e58a42ec72a604d 100644 (file)
@@ -6,6 +6,11 @@ Try to install a few packages.
 import glob
 import os
 import sys
+import re
+import subprocess
+import functools
+import tarfile
+import zipfile
 
 from setuptools.extern.six.moves import urllib
 import pytest
@@ -114,15 +119,12 @@ def test_pyuri(install_context):
     assert os.path.exists(os.path.join(pyuri.location, 'pyuri', 'uri.regex'))
 
 
-import re
-import subprocess
-import functools
-import tarfile, zipfile
+build_deps = ['appdirs', 'packaging', 'pyparsing', 'six']
 
 
-build_deps = ['appdirs', 'packaging', 'pyparsing', 'six']
 @pytest.mark.parametrize("build_dep", build_deps)
-@pytest.mark.skipif(sys.version_info < (3, 6), reason='run only on late versions')
+@pytest.mark.skipif(
+    sys.version_info < (3, 6), reason='run only on late versions')
 def test_build_deps_on_distutils(request, tmpdir_factory, build_dep):
     """
     All setuptools build dependencies must build without
@@ -149,13 +151,16 @@ def install(pkg_dir, install_dir):
         breaker.write('raise ImportError()')
     cmd = [sys.executable, 'setup.py', 'install', '--prefix', install_dir]
     env = dict(os.environ, PYTHONPATH=pkg_dir)
-    output = subprocess.check_output(cmd, cwd=pkg_dir, env=env, stderr=subprocess.STDOUT)
+    output = subprocess.check_output(
+        cmd, cwd=pkg_dir, env=env, stderr=subprocess.STDOUT)
     return output.decode('utf-8')
 
 
 def download_and_extract(request, req, target):
-    cmd = [sys.executable, '-m', 'pip', 'download', '--no-deps',
-        '--no-binary', ':all:', req]
+    cmd = [
+        sys.executable, '-m', 'pip', 'download', '--no-deps',
+        '--no-binary', ':all:', req,
+    ]
     output = subprocess.check_output(cmd, encoding='utf-8')
     filename = re.search('Saved (.*)', output).group(1)
     request.addfinalizer(functools.partial(os.remove, filename))
index 5edfbea003487c91119e5b0030e0862e3e8720e3..2a0e9c8625147e355faeed54810882da76ff3b0b 100644 (file)
@@ -15,7 +15,6 @@ from setuptools.command.egg_info import FileList, egg_info, translate_pattern
 from setuptools.dist import Distribution
 from setuptools.extern import six
 from setuptools.tests.textwrap import DALS
-from . import py3_only
 
 import pytest
 
@@ -74,7 +73,9 @@ translate_specs = [
 
     # Glob matching
     ('*.txt', ['foo.txt', 'bar.txt'], ['foo/foo.txt']),
-    ('dir/*.txt', ['dir/foo.txt', 'dir/bar.txt', 'dir/.txt'], ['notdir/foo.txt']),
+    (
+        'dir/*.txt',
+        ['dir/foo.txt', 'dir/bar.txt', 'dir/.txt'], ['notdir/foo.txt']),
     ('*/*.py', ['bin/start.py'], []),
     ('docs/page-?.txt', ['docs/page-9.txt'], ['docs/page-10.txt']),
 
@@ -243,77 +244,77 @@ class TestManifestTest(TempDirTestCase):
 
     def test_exclude(self):
         """Include everything in app/ except the text files"""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest(
             """
             include app/*
             exclude app/*.txt
             """)
-        files = default_files | set([l('app/c.rst')])
+        files = default_files | set([ml('app/c.rst')])
         assert files == self.get_files()
 
     def test_include_multiple(self):
         """Include with multiple patterns."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest("include app/*.txt app/static/*")
         files = default_files | set([
-            l('app/a.txt'), l('app/b.txt'),
-            l('app/static/app.js'), l('app/static/app.js.map'),
-            l('app/static/app.css'), l('app/static/app.css.map')])
+            ml('app/a.txt'), ml('app/b.txt'),
+            ml('app/static/app.js'), ml('app/static/app.js.map'),
+            ml('app/static/app.css'), ml('app/static/app.css.map')])
         assert files == self.get_files()
 
     def test_graft(self):
         """Include the whole app/static/ directory."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest("graft app/static")
         files = default_files | set([
-            l('app/static/app.js'), l('app/static/app.js.map'),
-            l('app/static/app.css'), l('app/static/app.css.map')])
+            ml('app/static/app.js'), ml('app/static/app.js.map'),
+            ml('app/static/app.css'), ml('app/static/app.css.map')])
         assert files == self.get_files()
 
     def test_graft_glob_syntax(self):
         """Include the whole app/static/ directory."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest("graft */static")
         files = default_files | set([
-            l('app/static/app.js'), l('app/static/app.js.map'),
-            l('app/static/app.css'), l('app/static/app.css.map')])
+            ml('app/static/app.js'), ml('app/static/app.js.map'),
+            ml('app/static/app.css'), ml('app/static/app.css.map')])
         assert files == self.get_files()
 
     def test_graft_global_exclude(self):
         """Exclude all *.map files in the project."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest(
             """
             graft app/static
             global-exclude *.map
             """)
         files = default_files | set([
-            l('app/static/app.js'), l('app/static/app.css')])
+            ml('app/static/app.js'), ml('app/static/app.css')])
         assert files == self.get_files()
 
     def test_global_include(self):
         """Include all *.rst, *.js, and *.css files in the whole tree."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest(
             """
             global-include *.rst *.js *.css
             """)
         files = default_files | set([
-            '.hidden.rst', 'testing.rst', l('app/c.rst'),
-            l('app/static/app.js'), l('app/static/app.css')])
+            '.hidden.rst', 'testing.rst', ml('app/c.rst'),
+            ml('app/static/app.js'), ml('app/static/app.css')])
         assert files == self.get_files()
 
     def test_graft_prune(self):
         """Include all files in app/, except for the whole app/static/ dir."""
-        l = make_local_path
+        ml = make_local_path
         self.make_manifest(
             """
             graft app
             prune app/static
             """)
         files = default_files | set([
-            l('app/a.txt'), l('app/b.txt'), l('app/c.rst')])
+            ml('app/a.txt'), ml('app/b.txt'), ml('app/c.rst')])
         assert files == self.get_files()
 
 
@@ -369,7 +370,7 @@ class TestFileListTest(TempDirTestCase):
     def test_process_template_line(self):
         # testing  all MANIFEST.in template patterns
         file_list = FileList()
-        l = make_local_path
+        ml = make_local_path
 
         # simulated file list
         self.make_files([
@@ -377,16 +378,16 @@ class TestFileListTest(TempDirTestCase):
             'buildout.cfg',
             # filelist does not filter out VCS directories,
             # it's sdist that does
-            l('.hg/last-message.txt'),
-            l('global/one.txt'),
-            l('global/two.txt'),
-            l('global/files.x'),
-            l('global/here.tmp'),
-            l('f/o/f.oo'),
-            l('dir/graft-one'),
-            l('dir/dir2/graft2'),
-            l('dir3/ok'),
-            l('dir3/sub/ok.txt'),
+            ml('.hg/last-message.txt'),
+            ml('global/one.txt'),
+            ml('global/two.txt'),
+            ml('global/files.x'),
+            ml('global/here.tmp'),
+            ml('f/o/f.oo'),
+            ml('dir/graft-one'),
+            ml('dir/dir2/graft2'),
+            ml('dir3/ok'),
+            ml('dir3/sub/ok.txt'),
         ])
 
         MANIFEST_IN = DALS("""\
@@ -413,12 +414,12 @@ class TestFileListTest(TempDirTestCase):
             'buildout.cfg',
             'four.txt',
             'ok',
-            l('.hg/last-message.txt'),
-            l('dir/graft-one'),
-            l('dir/dir2/graft2'),
-            l('f/o/f.oo'),
-            l('global/one.txt'),
-            l('global/two.txt'),
+            ml('.hg/last-message.txt'),
+            ml('dir/graft-one'),
+            ml('dir/dir2/graft2'),
+            ml('f/o/f.oo'),
+            ml('global/one.txt'),
+            ml('global/two.txt'),
         ]
 
         file_list.sort()
@@ -475,10 +476,10 @@ class TestFileListTest(TempDirTestCase):
                 assert False, "Should have thrown an error"
 
     def test_include(self):
-        l = make_local_path
+        ml = make_local_path
         # include
         file_list = FileList()
-        self.make_files(['a.py', 'b.txt', l('d/c.py')])
+        self.make_files(['a.py', 'b.txt', ml('d/c.py')])
 
         file_list.process_template_line('include *.py')
         file_list.sort()
@@ -491,42 +492,42 @@ class TestFileListTest(TempDirTestCase):
         self.assertWarnings()
 
     def test_exclude(self):
-        l = make_local_path
+        ml = make_local_path
         # exclude
         file_list = FileList()
-        file_list.files = ['a.py', 'b.txt', l('d/c.py')]
+        file_list.files = ['a.py', 'b.txt', ml('d/c.py')]
 
         file_list.process_template_line('exclude *.py')
         file_list.sort()
-        assert file_list.files == ['b.txt', l('d/c.py')]
+        assert file_list.files == ['b.txt', ml('d/c.py')]
         self.assertNoWarnings()
 
         file_list.process_template_line('exclude *.rb')
         file_list.sort()
-        assert file_list.files == ['b.txt', l('d/c.py')]
+        assert file_list.files == ['b.txt', ml('d/c.py')]
         self.assertWarnings()
 
     def test_global_include(self):
-        l = make_local_path
+        ml = make_local_path
         # global-include
         file_list = FileList()
-        self.make_files(['a.py', 'b.txt', l('d/c.py')])
+        self.make_files(['a.py', 'b.txt', ml('d/c.py')])
 
         file_list.process_template_line('global-include *.py')
         file_list.sort()
-        assert file_list.files == ['a.py', l('d/c.py')]
+        assert file_list.files == ['a.py', ml('d/c.py')]
         self.assertNoWarnings()
 
         file_list.process_template_line('global-include *.rb')
         file_list.sort()
-        assert file_list.files == ['a.py', l('d/c.py')]
+        assert file_list.files == ['a.py', ml('d/c.py')]
         self.assertWarnings()
 
     def test_global_exclude(self):
-        l = make_local_path
+        ml = make_local_path
         # global-exclude
         file_list = FileList()
-        file_list.files = ['a.py', 'b.txt', l('d/c.py')]
+        file_list.files = ['a.py', 'b.txt', ml('d/c.py')]
 
         file_list.process_template_line('global-exclude *.py')
         file_list.sort()
@@ -539,65 +540,65 @@ class TestFileListTest(TempDirTestCase):
         self.assertWarnings()
 
     def test_recursive_include(self):
-        l = make_local_path
+        ml = make_local_path
         # recursive-include
         file_list = FileList()
-        self.make_files(['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')])
+        self.make_files(['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')])
 
         file_list.process_template_line('recursive-include d *.py')
         file_list.sort()
-        assert file_list.files == [l('d/b.py'), l('d/d/e.py')]
+        assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')]
         self.assertNoWarnings()
 
         file_list.process_template_line('recursive-include e *.py')
         file_list.sort()
-        assert file_list.files == [l('d/b.py'), l('d/d/e.py')]
+        assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')]
         self.assertWarnings()
 
     def test_recursive_exclude(self):
-        l = make_local_path
+        ml = make_local_path
         # recursive-exclude
         file_list = FileList()
-        file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')]
+        file_list.files = ['a.py', ml('d/b.py'), ml('d/c.txt'), ml('d/d/e.py')]
 
         file_list.process_template_line('recursive-exclude d *.py')
         file_list.sort()
-        assert file_list.files == ['a.py', l('d/c.txt')]
+        assert file_list.files == ['a.py', ml('d/c.txt')]
         self.assertNoWarnings()
 
         file_list.process_template_line('recursive-exclude e *.py')
         file_list.sort()
-        assert file_list.files == ['a.py', l('d/c.txt')]
+        assert file_list.files == ['a.py', ml('d/c.txt')]
         self.assertWarnings()
 
     def test_graft(self):
-        l = make_local_path
+        ml = make_local_path
         # graft
         file_list = FileList()
-        self.make_files(['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')])
+        self.make_files(['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')])
 
         file_list.process_template_line('graft d')
         file_list.sort()
-        assert file_list.files == [l('d/b.py'), l('d/d/e.py')]
+        assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')]
         self.assertNoWarnings()
 
         file_list.process_template_line('graft e')
         file_list.sort()
-        assert file_list.files == [l('d/b.py'), l('d/d/e.py')]
+        assert file_list.files == [ml('d/b.py'), ml('d/d/e.py')]
         self.assertWarnings()
 
     def test_prune(self):
-        l = make_local_path
+        ml = make_local_path
         # prune
         file_list = FileList()
-        file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')]
+        file_list.files = ['a.py', ml('d/b.py'), ml('d/d/e.py'), ml('f/f.py')]
 
         file_list.process_template_line('prune d')
         file_list.sort()
-        assert file_list.files == ['a.py', l('f/f.py')]
+        assert file_list.files == ['a.py', ml('f/f.py')]
         self.assertNoWarnings()
 
         file_list.process_template_line('prune e')
         file_list.sort()
-        assert file_list.files == ['a.py', l('f/f.py')]
+        assert file_list.files == ['a.py', ml('f/f.py')]
         self.assertWarnings()
index 32d7a907cfff809eaee3786f2e56fda53231803b..24e38ea88004578fc56ea5088a4e0e595daa532c 100644 (file)
@@ -49,7 +49,8 @@ def mock_reg(hkcu=None, hklm=None):
             for k in hive if k.startswith(key.lower())
         )
 
-    return mock.patch.multiple(distutils.msvc9compiler.Reg,
+    return mock.patch.multiple(
+        distutils.msvc9compiler.Reg,
         read_keys=read_keys, read_values=read_values)
 
 
@@ -61,7 +62,7 @@ class TestModulePatch:
     """
 
     key_32 = r'software\microsoft\devdiv\vcforpython\9.0\installdir'
-    key_64 = r'software\wow6432node\microsoft\devdiv\vcforpython\9.0\installdir'
+    key_64 = key_32.replace(r'\microsoft', r'\wow6432node\microsoft')
 
     def test_patched(self):
         "Test the module is actually patched"
index 670ccee915a02f312a8feba66ab00c93e446368c..f937d9818979f68063a0c03b790f5a766b85c57e 100644 (file)
@@ -1,6 +1,5 @@
 from __future__ import absolute_import, unicode_literals
 
-import os
 import sys
 import subprocess
 
index 63b9294610551446a58812a868a1b025c5f9471c..ab371884d9a8ea9447fc57c558997450037bccac 100644 (file)
@@ -6,6 +6,8 @@ import distutils.errors
 
 from setuptools.extern import six
 from setuptools.extern.six.moves import urllib, http_client
+import mock
+import pytest
 
 import pkg_resources
 import setuptools.package_index
@@ -42,7 +44,10 @@ class TestPackageIndex:
             hosts=('www.example.com',)
         )
 
-        url = 'url:%20https://svn.plone.org/svn/collective/inquant.contentmirror.plone/trunk'
+        url = (
+            'url:%20https://svn.plone.org/svn'
+            '/collective/inquant.contentmirror.plone/trunk'
+        )
         try:
             v = index.open_url(url)
         except Exception as v:
@@ -61,9 +66,9 @@ class TestPackageIndex:
         index.opener = _urlopen
         url = 'http://example.com'
         try:
-            v = index.open_url(url)
-        except Exception as v:
-            assert 'line' in str(v)
+            index.open_url(url)
+        except Exception as exc:
+            assert 'line' in str(exc)
         else:
             raise AssertionError('Should have raise here!')
 
@@ -81,7 +86,11 @@ class TestPackageIndex:
             index.open_url(url)
         except distutils.errors.DistutilsError as error:
             msg = six.text_type(error)
-            assert 'nonnumeric port' in msg or 'getaddrinfo failed' in msg or 'Name or service not known' in msg
+            assert (
+                'nonnumeric port' in msg
+                or 'getaddrinfo failed' in msg
+                or 'Name or service not known' in msg
+            )
             return
         raise RuntimeError("Did not raise")
 
@@ -223,6 +232,61 @@ class TestPackageIndex:
             assert dists[0].version == ''
             assert dists[1].version == vc
 
+    def test_download_git_with_rev(self, tmpdir):
+        url = 'git+https://github.example/group/project@master#egg=foo'
+        index = setuptools.package_index.PackageIndex()
+
+        with mock.patch("os.system") as os_system_mock:
+            result = index.download(url, str(tmpdir))
+
+        os_system_mock.assert_called()
+
+        expected_dir = str(tmpdir / 'project@master')
+        expected = (
+            'git clone --quiet '
+            'https://github.example/group/project {expected_dir}'
+        ).format(**locals())
+        first_call_args = os_system_mock.call_args_list[0][0]
+        assert first_call_args == (expected,)
+
+        tmpl = '(cd {expected_dir} && git checkout --quiet master)'
+        expected = tmpl.format(**locals())
+        assert os_system_mock.call_args_list[1][0] == (expected,)
+        assert result == expected_dir
+
+    def test_download_git_no_rev(self, tmpdir):
+        url = 'git+https://github.example/group/project#egg=foo'
+        index = setuptools.package_index.PackageIndex()
+
+        with mock.patch("os.system") as os_system_mock:
+            result = index.download(url, str(tmpdir))
+
+        os_system_mock.assert_called()
+
+        expected_dir = str(tmpdir / 'project')
+        expected = (
+            'git clone --quiet '
+            'https://github.example/group/project {expected_dir}'
+        ).format(**locals())
+        os_system_mock.assert_called_once_with(expected)
+
+    def test_download_svn(self, tmpdir):
+        url = 'svn+https://svn.example/project#egg=foo'
+        index = setuptools.package_index.PackageIndex()
+
+        with pytest.warns(UserWarning):
+            with mock.patch("os.system") as os_system_mock:
+                result = index.download(url, str(tmpdir))
+
+        os_system_mock.assert_called()
+
+        expected_dir = str(tmpdir / 'project')
+        expected = (
+            'svn checkout -q '
+            'svn+https://svn.example/project {expected_dir}'
+        ).format(**locals())
+        os_system_mock.assert_called_once_with(expected)
+
 
 class TestContentCheckers:
     def test_md5(self):
index f558a0d87c3705dfd17f62504d0c31bf5551b4b4..30afdec7d44e531ae4c0e9e1181d255cffac67c1 100644 (file)
@@ -32,7 +32,9 @@ class TestPEP425Tags:
         if sys.version_info < (3, 3):
             config_vars.update({'Py_UNICODE_SIZE': 2})
             mock_gcf = self.mock_get_config_var(**config_vars)
-            with patch('setuptools.pep425tags.sysconfig.get_config_var', mock_gcf):
+            with patch(
+                    'setuptools.pep425tags.sysconfig.get_config_var',
+                    mock_gcf):
                 abi_tag = pep425tags.get_abi_tag()
                 assert abi_tag == base + flags
 
index d8675422296e4bd5feff49222e41aecee9a3bee6..99398cdb93bf55dc2e19c80d00ae76c6229f9ccc 100644 (file)
@@ -26,7 +26,8 @@ class TestSandbox:
         """
         It should be possible to execute a setup.py with a Byte Order Mark
         """
-        target = pkg_resources.resource_filename(__name__,
+        target = pkg_resources.resource_filename(
+            __name__,
             'script-with-bom.py')
         namespace = types.ModuleType('namespace')
         setuptools.sandbox._execfile(target, vars(namespace))
index 7aae3a163a93e7bed0783a300ccc72c6cce63049..5896a69ae69d482b689768a9e5e3ee7da7b6d127 100644 (file)
@@ -77,7 +77,8 @@ class TestDepends:
         from json import __version__
         assert dep.get_module_constant('json', '__version__') == __version__
         assert dep.get_module_constant('sys', 'version') == sys.version
-        assert dep.get_module_constant('setuptools.tests.test_setuptools', '__doc__') == __doc__
+        assert dep.get_module_constant(
+            'setuptools.tests.test_setuptools', '__doc__') == __doc__
 
     @needs_bytecode
     def testRequire(self):
@@ -216,7 +217,8 @@ class TestFeatures:
         self.req = Require('Distutils', '1.0.3', 'distutils')
         self.dist = makeSetup(
             features={
-                'foo': Feature("foo", standard=True, require_features=['baz', self.req]),
+                'foo': Feature(
+                    "foo", standard=True, require_features=['baz', self.req]),
                 'bar': Feature("bar", standard=True, packages=['pkg.bar'],
                                py_modules=['bar_et'], remove=['bar.ext'],
                                ),
@@ -252,7 +254,8 @@ class TestFeatures:
             ('with-dwim', None, 'include DWIM') in dist.feature_options
         )
         assert (
-            ('without-dwim', None, 'exclude DWIM (default)') in dist.feature_options
+            ('without-dwim', None, 'exclude DWIM (default)')
+            in dist.feature_options
         )
         assert (
             ('with-bar', None, 'include bar (default)') in dist.feature_options
index 8d1425e187cd15540b9ffac67151a585e5d5d687..faaa6ba90a02df4041878990c4bb56efd82a6d9d 100644 (file)
@@ -4,7 +4,6 @@ from __future__ import unicode_literals
 
 from distutils import log
 import os
-import sys
 
 import pytest
 
index cc0e8a0d94204f6f7ff3ee4e3c6f48274778b3a6..320c6959da2756e578819e7fa379cc6c1fa49a78 100644 (file)
@@ -4,7 +4,6 @@ import re
 
 from distutils import log
 from distutils.errors import DistutilsError
-from distutils.version import StrictVersion
 
 import pytest
 
index 7b5fea1738187d197271836d05875b333cdace2a..3d5c84b067eb88e8c4b9900ed38b231c2fa4a1c0 100644 (file)
@@ -1,4 +1,3 @@
-import distutils.command
 import glob
 import os
 import sys
@@ -141,7 +140,7 @@ def test_no_missing_dependencies(bare_virtualenv):
     """
     Quick and dirty test to ensure all external dependencies are vendored.
     """
-    for command in ('upload',):#sorted(distutils.command.__all__):
+    for command in ('upload',):  # sorted(distutils.command.__all__):
         bare_virtualenv.run(' && '.join((
             'cd {source}',
             'python setup.py {command} -h',
index 6db5fa117e0ea35f721347134ed84426a60b20b5..e85a4a7e8dab4a51e286296e59d1e5d3b132505e 100644 (file)
@@ -63,6 +63,7 @@ WHEEL_INFO_TESTS = (
     }),
 )
 
+
 @pytest.mark.parametrize(
     ('filename', 'info'), WHEEL_INFO_TESTS,
     ids=[t[0] for t in WHEEL_INFO_TESTS]
@@ -487,6 +488,7 @@ WHEEL_INSTALL_TESTS = (
 
 )
 
+
 @pytest.mark.parametrize(
     'params', WHEEL_INSTALL_TESTS,
     ids=list(params['id'] for params in WHEEL_INSTALL_TESTS),
index d2871c0f4032d296dcf8ec52b03b5b4c69ed1562..2553394a26cd62ab370e7b8a9fc4e5487a4a3723 100644 (file)
@@ -97,7 +97,8 @@ class TestCLI(WrapperTester):
             'arg 4\\',
             'arg5 a\\\\b',
         ]
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
+        proc = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
         stdout, stderr = proc.communicate('hello\nworld\n'.encode('ascii'))
         actual = stdout.decode('ascii').replace('\r\n', '\n')
         expected = textwrap.dedent(r"""
@@ -134,7 +135,11 @@ class TestCLI(WrapperTester):
         with (tmpdir / 'foo-script.py').open('w') as f:
             f.write(self.prep_script(tmpl))
         cmd = [str(tmpdir / 'foo.exe')]
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
+        proc = subprocess.Popen(
+            cmd,
+            stdout=subprocess.PIPE,
+            stdin=subprocess.PIPE,
+            stderr=subprocess.STDOUT)
         stdout, stderr = proc.communicate()
         actual = stdout.decode('ascii').replace('\r\n', '\n')
         expected = textwrap.dedent(r"""
@@ -172,7 +177,9 @@ class TestGUI(WrapperTester):
             str(tmpdir / 'test_output.txt'),
             'Test Argument',
         ]
-        proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.STDOUT)
+        proc = subprocess.Popen(
+            cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE,
+            stderr=subprocess.STDOUT)
         stdout, stderr = proc.communicate()
         assert not stdout
         assert not stderr
index 7c63efd20b350358ab25c079166dbb00ef49f8d2..3b8179a870c00226aa944a0c3bb3f61b170cf4e2 100644 (file)
@@ -1,5 +1,6 @@
 import unicodedata
 import sys
+import re
 
 from setuptools.extern import six
 
@@ -42,3 +43,15 @@ def try_encode(string, enc):
         return string.encode(enc)
     except UnicodeEncodeError:
         return None
+
+
+CODING_RE = re.compile(br'^[ \t\f]*#.*?coding[:=][ \t]*([-\w.]+)')
+
+
+def detect_encoding(fp):
+    first_line = fp.readline()
+    fp.seek(0)
+    m = CODING_RE.match(first_line)
+    if m is None:
+        return None
+    return m.group(1).decode('ascii')
index 95a794a85361be66f1862ff3e8035cb807eb6503..e11f0a1d912860b0da241dee65f6f76d9c8d45e9 100644 (file)
@@ -8,10 +8,11 @@ import posixpath
 import re
 import zipfile
 
-from pkg_resources import Distribution, PathMetadata, parse_version
+import pkg_resources
+import setuptools
+from pkg_resources import parse_version
 from setuptools.extern.packaging.utils import canonicalize_name
 from setuptools.extern.six import PY3
-from setuptools import Distribution as SetuptoolsDistribution
 from setuptools import pep425tags
 from setuptools.command.egg_info import write_requirements
 
@@ -79,7 +80,7 @@ class Wheel:
         return next((True for t in self.tags() if t in supported_tags), False)
 
     def egg_name(self):
-        return Distribution(
+        return pkg_resources.Distribution(
             project_name=self.project_name, version=self.version,
             platform=(None if self.platform == 'any' else get_platform()),
         ).egg_name() + '.egg'
@@ -130,9 +131,9 @@ class Wheel:
         zf.extractall(destination_eggdir)
         # Convert metadata.
         dist_info = os.path.join(destination_eggdir, dist_info)
-        dist = Distribution.from_location(
+        dist = pkg_resources.Distribution.from_location(
             destination_eggdir, dist_info,
-            metadata=PathMetadata(destination_eggdir, dist_info),
+            metadata=pkg_resources.PathMetadata(destination_eggdir, dist_info),
         )
 
         # Note: Evaluate and strip markers now,
@@ -155,7 +156,7 @@ class Wheel:
             os.path.join(egg_info, 'METADATA'),
             os.path.join(egg_info, 'PKG-INFO'),
         )
-        setup_dist = SetuptoolsDistribution(
+        setup_dist = setuptools.Distribution(
             attrs=dict(
                 install_requires=install_requires,
                 extras_require=extras_require,
index 0c6c3e597c0ee3bf811c06702f19a94a62e5ebb4..f944df27404372a84cc9c6ac60d71a5ba98b6915 100644 (file)
@@ -3,9 +3,11 @@ pytest-flake8; python_version!="3.4"
 pytest-flake8<=1.0.0; python_version=="3.4"
 virtualenv>=13.0.0
 pytest-virtualenv>=1.2.7
-pytest>=3.0.2
+# pytest pinned to <4 due to #1638
+pytest>=3.7,<4
 wheel
 coverage>=4.5.1
 pytest-cov>=2.5.1
 paver; python_version>="3.6"
 futures; python_version=="2.7"
+pip==18.1  # Temporary workaround for #1644.
diff --git a/tox.ini b/tox.ini
index a2f850dff52a28e688a0fafe7f3bef2c0b0b7d8a..a31cb1c5352ecc6a7baed6f0c92e1747257aa551 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -12,7 +12,7 @@ deps=-rtests/requirements.txt
 # Changed from default (`python -m pip ...`)
 # to prevent the current working directory
 # from being added to `sys.path`.
-install_command={envbindir}/pip install {opts} {packages}
+install_command=python -c 'import sys; sys.path.remove(""); from pkg_resources import load_entry_point; load_entry_point("pip", "console_scripts", "pip")()' install {opts} {packages}
 # Same as above.
 list_dependencies_command={envbindir}/pip freeze
 setenv=COVERAGE_FILE={toxworkdir}/.coverage.{envname}