Imported Upstream version 65.5.1 upstream/65.5.1
authorJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:57 +0000 (17:02 +0900)
committerJinWang An <jinwang.an@samsung.com>
Mon, 27 Mar 2023 08:02:57 +0000 (17:02 +0900)
16 files changed:
.bumpversion.cfg
.github/workflows/main.yml
CHANGES.rst
docs/userguide/quickstart.rst
pkg_resources/tests/test_markers.py
pkg_resources/tests/test_pkg_resources.py
pytest.ini
setup.cfg
setuptools/command/build.py
setuptools/package_index.py
setuptools/tests/test_bdist_deprecations.py
setuptools/tests/test_build_clib.py
setuptools/tests/test_easy_install.py
setuptools/tests/test_packageindex.py
setuptools/tests/test_register.py
setuptools/tests/test_upload.py

index 26370e6c396db52427140cff1a22bab02f48a00a..316cd4c71ed3bbf9a76770851260276205c03ae3 100644 (file)
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 65.5.0
+current_version = 65.5.1
 commit = True
 tag = True
 
index 7b4669aceeae8bce246ad28a17f7807e63fc6cdc..8892a6cde0bcd48196060008cb36595c7aeedde0 100644 (file)
@@ -64,7 +64,7 @@ jobs:
         run: pipx run coverage xml --ignore-errors
       - name: Publish coverage
         if: hashFiles('coverage.xml') != ''  # Rudimentary `file.exists()`
-        uses: codecov/codecov-action@v1
+        uses: codecov/codecov-action@v3
         with:
           flags: >-  # Mark which lines are covered by which envs
             ${{ runner.os }},
index ac84ffac539ad90b852573e7cc5c869d5468cabb..e2b8dbdc4fbbeec5c59ff8259602ff941a9e75f6 100644 (file)
@@ -1,3 +1,13 @@
+v65.5.1
+-------
+
+
+Misc
+^^^^
+* #3638: Drop a test dependency on the ``mock`` package, always use :external+python:py:mod:`unittest.mock` -- by :user:`hroncok`
+* #3659: Fixed REDoS vector in package_index.
+
+
 v65.5.0
 -------
 
index 13846e205102b7b9347cd6382ad486f89ff00528..fe5c5bc2ba74be4293ffad499bfb898e7b3e7e75 100644 (file)
@@ -299,7 +299,7 @@ Dependency management
 ---------------------
 Packages built with ``setuptools`` can specify dependencies to be automatically
 installed when the package itself is installed.
-The example below show how to configure this kind of dependencies:
+The example below shows how to configure this kind of dependencies:
 
 .. tab:: pyproject.toml
 
index 15a3b499a6038cef16024ee8519a342acd22ba9b..9306d5b3483e8913b4a04d702091fb5efbc3f444 100644 (file)
@@ -1,4 +1,4 @@
-import mock
+from unittest import mock
 
 from pkg_resources import evaluate_marker
 
index 6518820e6f311fcfce4763e490fe119b0615946c..684c977771c1feebc998d349cba70f8960e6cb54 100644 (file)
@@ -9,10 +9,7 @@ import stat
 import distutils.dist
 import distutils.command.install_egg_info
 
-try:
-    from unittest import mock
-except ImportError:
-    import mock
+from unittest import mock
 
 from pkg_resources import (
     DistInfoDistribution, Distribution, EggInfoDistribution,
index 50a7971b714b4010618da0f5105282832ae3d6a4..9d326e54c7cfd3fcce414430ae76e063a535a9fb 100644 (file)
@@ -64,3 +64,6 @@ filterwarnings=
 
        ignore:Support for .* in .pyproject.toml. is still .beta.
        ignore::setuptools.command.editable_wheel.InformationOnly
+
+       # https://github.com/pypa/setuptools/issues/3655
+       ignore:The --rsyncdir command line argument and rsyncdirs config variable are deprecated.:DeprecationWarning
index 201c260247804c1eb3e208faf35b675ce72c033d..054fd047fc818a8e627ce819e7c5f6ad7dc757a2 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,6 +1,6 @@
 [metadata]
 name = setuptools
-version = 65.5.0
+version = 65.5.1
 author = Python Packaging Authority
 author_email = distutils-sig@python.org
 description = Easily download, build, install, upgrade, and uninstall Python packages
@@ -59,7 +59,6 @@ testing =
        pytest-perf
 
        # local
-       mock
        flake8-2020
        virtualenv>=13.0.0
        wheel
@@ -72,6 +71,7 @@ testing =
        pip_run>=8.8
        ini2toml[lite]>=0.9
        tomli-w>=1.0.0
+       pytest-timeout
 
 testing-integration =
        pytest
index c0676d8e4b1a567969cf05c5825d49c3300284c9..fa3c99ef48b67fc3ba1001fd8f550df18b9299f7 100644 (file)
@@ -48,8 +48,8 @@ class SubCommand(Protocol):
           Subcommands **SHOULD** take advantage of ``editable_mode=True`` to adequate
           its behaviour or perform optimisations.
 
-          For example, if a subcommand don't need to generate any extra file and
-          everything it does is to copy a source file into the build directory,
+          For example, if a subcommand doesn't need to generate an extra file and
+          all it does is to copy a source file into the build directory,
           ``run()`` **SHOULD** simply "early return".
 
           Similarly, if the subcommand creates files that would be placed alongside
index 14881d2992273f3c76e8c6c8dca156abdeae5375..362e26f3e1aa1dad364a0ec3cbbe9213191b1067 100644 (file)
@@ -1,4 +1,5 @@
-"""PyPI and direct package downloading"""
+"""PyPI and direct package downloading."""
+
 import sys
 import os
 import re
@@ -19,9 +20,20 @@ from functools import wraps
 
 import setuptools
 from pkg_resources import (
-    CHECKOUT_DIST, Distribution, BINARY_DIST, normalize_path, SOURCE_DIST,
-    Environment, find_distributions, safe_name, safe_version,
-    to_filename, Requirement, DEVELOP_DIST, EGG_DIST, parse_version,
+    CHECKOUT_DIST,
+    Distribution,
+    BINARY_DIST,
+    normalize_path,
+    SOURCE_DIST,
+    Environment,
+    find_distributions,
+    safe_name,
+    safe_version,
+    to_filename,
+    Requirement,
+    DEVELOP_DIST,
+    EGG_DIST,
+    parse_version,
 )
 from distutils import log
 from distutils.errors import DistutilsError
@@ -40,7 +52,9 @@ URL_SCHEME = re.compile('([-+.a-z0-9]{2,}):', re.I).match
 EXTENSIONS = ".tar.gz .tar.bz2 .tar .zip .tgz".split()
 
 __all__ = [
-    'PackageIndex', 'distros_for_url', 'parse_bdist_wininst',
+    'PackageIndex',
+    'distros_for_url',
+    'parse_bdist_wininst',
     'interpret_distro_name',
 ]
 
@@ -48,7 +62,8 @@ _SOCKET_TIMEOUT = 15
 
 _tmpl = "setuptools/{setuptools.__version__} Python-urllib/{py_major}"
 user_agent = _tmpl.format(
-    py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools)
+    py_major='{}.{}'.format(*sys.version_info), setuptools=setuptools
+)
 
 
 def parse_requirement_arg(spec):
@@ -120,13 +135,15 @@ def distros_for_location(location, basename, metadata=None):
         wheel = Wheel(basename)
         if not wheel.is_compatible():
             return []
-        return [Distribution(
-            location=location,
-            project_name=wheel.project_name,
-            version=wheel.version,
-            # Increase priority over eggs.
-            precedence=EGG_DIST + 1,
-        )]
+        return [
+            Distribution(
+                location=location,
+                project_name=wheel.project_name,
+                version=wheel.version,
+                # Increase priority over eggs.
+                precedence=EGG_DIST + 1,
+            )
+        ]
     if basename.endswith('.exe'):
         win_base, py_ver, platform = parse_bdist_wininst(basename)
         if win_base is not None:
@@ -137,7 +154,7 @@ def distros_for_location(location, basename, metadata=None):
     #
     for ext in EXTENSIONS:
         if basename.endswith(ext):
-            basename = basename[:-len(ext)]
+            basename = basename[: -len(ext)]
             return interpret_distro_name(location, basename, metadata)
     return []  # no extension matched
 
@@ -150,8 +167,7 @@ def distros_for_filename(filename, metadata=None):
 
 
 def interpret_distro_name(
-        location, basename, metadata, py_version=None, precedence=SOURCE_DIST,
-        platform=None
+    location, basename, metadata, py_version=None, precedence=SOURCE_DIST, platform=None
 ):
     """Generate alternative interpretations of a source distro name
 
@@ -178,9 +194,13 @@ def interpret_distro_name(
 
     for p in range(1, len(parts) + 1):
         yield Distribution(
-            location, metadata, '-'.join(parts[:p]), '-'.join(parts[p:]),
-            py_version=py_version, precedence=precedence,
-            platform=platform
+            location,
+            metadata,
+            '-'.join(parts[:p]),
+            '-'.join(parts[p:]),
+            py_version=py_version,
+            precedence=precedence,
+            platform=platform,
         )
 
 
@@ -197,8 +217,10 @@ def unique_values(func):
     return wrapper
 
 
-REL = re.compile(r"""<([^>]*\srel\s*=\s*['"]?([^'">]+)[^>]*)>""", re.I)
-# this line is here to fix emacs' cruddy broken syntax highlighting
+REL = re.compile(r"""<([^>]*\srel\s{0,10}=\s{0,10}['"]?([^'" >]+)[^>]*)>""", re.I)
+"""
+Regex for an HTML tag with 'rel="val"' attributes.
+"""
 
 
 @unique_values
@@ -282,11 +304,16 @@ class PackageIndex(Environment):
     """A distribution index that scans web pages for download URLs"""
 
     def __init__(
-            self, index_url="https://pypi.org/simple/", hosts=('*',),
-            ca_bundle=None, verify_ssl=True, *args, **kw
+        self,
+        index_url="https://pypi.org/simple/",
+        hosts=('*',),
+        ca_bundle=None,
+        verify_ssl=True,
+        *args,
+        **kw
     ):
         super().__init__(*args, **kw)
-        self.index_url = index_url + "/" [:not index_url.endswith('/')]
+        self.index_url = index_url + "/"[: not index_url.endswith('/')]
         self.scanned_urls = {}
         self.fetched_urls = {}
         self.package_pages = {}
@@ -379,7 +406,8 @@ class PackageIndex(Environment):
             return True
         msg = (
             "\nNote: Bypassing %s (disallowed host; see "
-            "http://bit.ly/2hrImnY for details).\n")
+            "http://bit.ly/2hrImnY for details).\n"
+        )
         if fatal:
             raise DistutilsError(msg % url)
         else:
@@ -417,9 +445,7 @@ class PackageIndex(Environment):
         if not link.startswith(self.index_url):
             return NO_MATCH_SENTINEL
 
-        parts = list(map(
-            urllib.parse.unquote, link[len(self.index_url):].split('/')
-        ))
+        parts = list(map(urllib.parse.unquote, link[len(self.index_url) :].split('/')))
         if len(parts) != 2 or '#' in parts[1]:
             return NO_MATCH_SENTINEL
 
@@ -461,16 +487,15 @@ class PackageIndex(Environment):
     def need_version_info(self, url):
         self.scan_all(
             "Page at %s links to .py file(s) without version info; an index "
-            "scan is required.", url
+            "scan is required.",
+            url,
         )
 
     def scan_all(self, msg=None, *args):
         if self.index_url not in self.fetched_urls:
             if msg:
                 self.warn(msg, *args)
-            self.info(
-                "Scanning index of all packages (this may take a while)"
-            )
+            self.info("Scanning index of all packages (this may take a while)")
         self.scan_url(self.index_url)
 
     def find_packages(self, requirement):
@@ -501,9 +526,7 @@ class PackageIndex(Environment):
         """
         checker is a ContentChecker
         """
-        checker.report(
-            self.debug,
-            "Validating %%s checksum for %s" % filename)
+        checker.report(self.debug, "Validating %%s checksum for %s" % filename)
         if not checker.is_valid():
             tfp.close()
             os.unlink(filename)
@@ -540,7 +563,8 @@ class PackageIndex(Environment):
         else:  # no distros seen for this name, might be misspelled
             meth, msg = (
                 self.warn,
-                "Couldn't find index page for %r (maybe misspelled?)")
+                "Couldn't find index page for %r (maybe misspelled?)",
+            )
         meth(msg, requirement.unsafe_name)
         self.scan_all()
 
@@ -579,8 +603,14 @@ class PackageIndex(Environment):
         return getattr(self.fetch_distribution(spec, tmpdir), 'location', None)
 
     def fetch_distribution(  # noqa: C901  # is too complex (14)  # FIXME
-            self, requirement, tmpdir, force_scan=False, source=False,
-            develop_ok=False, local_index=None):
+        self,
+        requirement,
+        tmpdir,
+        force_scan=False,
+        source=False,
+        develop_ok=False,
+        local_index=None,
+    ):
         """Obtain a distribution suitable for fulfilling `requirement`
 
         `requirement` must be a ``pkg_resources.Requirement`` instance.
@@ -612,15 +642,13 @@ class PackageIndex(Environment):
                 if dist.precedence == DEVELOP_DIST and not develop_ok:
                     if dist not in skipped:
                         self.warn(
-                            "Skipping development or system egg: %s", dist,
+                            "Skipping development or system egg: %s",
+                            dist,
                         )
                         skipped[dist] = 1
                     continue
 
-                test = (
-                    dist in req
-                    and (dist.precedence <= SOURCE_DIST or not source)
-                )
+                test = dist in req and (dist.precedence <= SOURCE_DIST or not source)
                 if test:
                     loc = self.download(dist.location, tmpdir)
                     dist.download_location = loc
@@ -669,10 +697,15 @@ class PackageIndex(Environment):
 
     def gen_setup(self, filename, fragment, tmpdir):
         match = EGG_FRAGMENT.match(fragment)
-        dists = match and [
-            d for d in
-            interpret_distro_name(filename, match.group(1), None) if d.version
-        ] or []
+        dists = (
+            match
+            and [
+                d
+                for d in interpret_distro_name(filename, match.group(1), None)
+                if d.version
+            ]
+            or []
+        )
 
         if len(dists) == 1:  # unambiguous ``#egg`` fragment
             basename = os.path.basename(filename)
@@ -689,8 +722,9 @@ class PackageIndex(Environment):
                     "from setuptools import setup\n"
                     "setup(name=%r, version=%r, py_modules=[%r])\n"
                     % (
-                        dists[0].project_name, dists[0].version,
-                        os.path.splitext(basename)[0]
+                        dists[0].project_name,
+                        dists[0].version,
+                        os.path.splitext(basename)[0],
                     )
                 )
             return filename
@@ -766,23 +800,22 @@ class PackageIndex(Environment):
             if warning:
                 self.warn(warning, v.reason)
             else:
-                raise DistutilsError("Download error for %s: %s"
-                                     % (url, v.reason)) from v
+                raise DistutilsError(
+                    "Download error for %s: %s" % (url, v.reason)
+                ) from v
         except http.client.BadStatusLine as v:
             if warning:
                 self.warn(warning, v.line)
             else:
                 raise DistutilsError(
                     '%s returned a bad status line. The server might be '
-                    'down, %s' %
-                    (url, v.line)
+                    'down, %s' % (url, v.line)
                 ) from v
         except (http.client.HTTPException, socket.error) as v:
             if warning:
                 self.warn(warning, v)
             else:
-                raise DistutilsError("Download error for %s: %s"
-                                     % (url, v)) from v
+                raise DistutilsError("Download error for %s: %s" % (url, v)) from v
 
     def _download_url(self, scheme, url, tmpdir):
         # Determine download filename
@@ -887,10 +920,13 @@ class PackageIndex(Environment):
 
         if rev is not None:
             self.info("Checking out %s", rev)
-            os.system("git -C %s checkout --quiet %s" % (
-                filename,
-                rev,
-            ))
+            os.system(
+                "git -C %s checkout --quiet %s"
+                % (
+                    filename,
+                    rev,
+                )
+            )
 
         return filename
 
@@ -903,10 +939,13 @@ class PackageIndex(Environment):
 
         if rev is not None:
             self.info("Updating to %s", rev)
-            os.system("hg --cwd %s up -C -r %s -q" % (
-                filename,
-                rev,
-            ))
+            os.system(
+                "hg --cwd %s up -C -r %s -q"
+                % (
+                    filename,
+                    rev,
+                )
+            )
 
         return filename
 
@@ -1010,7 +1049,8 @@ class PyPIConfig(configparser.RawConfigParser):
     @property
     def creds_by_repository(self):
         sections_with_repositories = [
-            section for section in self.sections()
+            section
+            for section in self.sections()
             if self.get(section, 'repository').strip()
         ]
 
@@ -1114,8 +1154,8 @@ def local_open(url):
             files.append('<a href="{name}">{name}</a>'.format(name=f))
         else:
             tmpl = (
-                "<html><head><title>{url}</title>"
-                "</head><body>{files}</body></html>")
+                "<html><head><title>{url}</title>" "</head><body>{files}</body></html>"
+            )
             body = tmpl.format(url=url, files='\n'.join(files))
         status, message = 200, "OK"
     else:
index 1a900c6766b75362bc5a9b82b2d9ba9605641e31..1b69c41858f665bd716a5d961999a636269549ac 100644 (file)
@@ -1,7 +1,7 @@
 """develop tests
 """
-import mock
 import sys
+from unittest import mock
 
 import pytest
 
index 48bea2b43d0c0c9f3e3b797fe2c43114dbded139..af9e7c6dc397d052f603c8a86aace6551d624ba3 100644 (file)
@@ -1,6 +1,7 @@
+from unittest import mock
+
 import pytest
 
-import mock
 from distutils.errors import DistutilsSetupError
 from setuptools.command.build_clib import build_clib
 from setuptools.dist import Distribution
index d102e586b46d28d93ec0133613a56f665cebb606..bca86066adb25c8d0b0857c5da741f9161a67246 100644 (file)
@@ -12,7 +12,6 @@ import itertools
 import distutils.errors
 import io
 import zipfile
-import mock
 import time
 import re
 import subprocess
@@ -20,6 +19,7 @@ import pathlib
 import warnings
 from collections import namedtuple
 from pathlib import Path
+from unittest import mock
 
 import pytest
 from jaraco import path
index 8e9435efef0595250ecf066d851b3f5460de0445..7b0bf1124dc891c3e2628ee385f9f41a35fa6984 100644 (file)
@@ -5,8 +5,8 @@ import platform
 import urllib.request
 import urllib.error
 import http.client
+from unittest import mock
 
-import mock
 import pytest
 
 import setuptools.package_index
@@ -21,7 +21,9 @@ class TestPackageIndex:
             <a href="http://some_url">Name</a>
             (<a title="MD5 hash"
             href="{hash_url}">md5</a>)
-        """.lstrip().format(**locals())
+        """.lstrip().format(
+            **locals()
+        )
         assert setuptools.package_index.PYPI_MD5.match(doc)
 
     def test_bad_url_bad_port(self):
@@ -38,9 +40,7 @@ class TestPackageIndex:
         # issue 16
         # easy_install inquant.contentmirror.plone breaks because of a typo
         # in its home URL
-        index = setuptools.package_index.PackageIndex(
-            hosts=('www.example.com',)
-        )
+        index = setuptools.package_index.PackageIndex(hosts=('www.example.com',))
 
         url = (
             'url:%20https://svn.plone.org/svn'
@@ -54,9 +54,7 @@ class TestPackageIndex:
             assert isinstance(v, urllib.error.HTTPError)
 
     def test_bad_url_bad_status_line(self):
-        index = setuptools.package_index.PackageIndex(
-            hosts=('www.example.com',)
-        )
+        index = setuptools.package_index.PackageIndex(hosts=('www.example.com',))
 
         def _urlopen(*args):
             raise http.client.BadStatusLine('line')
@@ -74,9 +72,7 @@ class TestPackageIndex:
         """
         A bad URL with a double scheme should raise a DistutilsError.
         """
-        index = setuptools.package_index.PackageIndex(
-            hosts=('www.example.com',)
-        )
+        index = setuptools.package_index.PackageIndex(hosts=('www.example.com',))
 
         # issue 20
         url = 'http://http://svn.pythonpaste.org/Paste/wphp/trunk'
@@ -93,22 +89,17 @@ class TestPackageIndex:
         raise RuntimeError("Did not raise")
 
     def test_bad_url_screwy_href(self):
-        index = setuptools.package_index.PackageIndex(
-            hosts=('www.example.com',)
-        )
+        index = setuptools.package_index.PackageIndex(hosts=('www.example.com',))
 
         # issue #160
         if sys.version_info[0] == 2 and sys.version_info[1] == 7:
             # this should not fail
             url = 'http://example.com'
-            page = ('<a href="http://www.famfamfam.com]('
-                    'http://www.famfamfam.com/">')
+            page = '<a href="http://www.famfamfam.com](' 'http://www.famfamfam.com/">'
             index.process_index(url, page)
 
     def test_url_ok(self):
-        index = setuptools.package_index.PackageIndex(
-            hosts=('www.example.com',)
-        )
+        index = setuptools.package_index.PackageIndex(hosts=('www.example.com',))
         url = 'file:///tmp/test_package_index'
         assert index.url_ok(url, True)
 
@@ -169,9 +160,7 @@ class TestPackageIndex:
             'b0',
             'rc0',
         ]
-        post = [
-            '.post0'
-        ]
+        post = ['.post0']
         dev = [
             '.dev0',
         ]
@@ -186,10 +175,14 @@ class TestPackageIndex:
             for e in epoch
             for r in releases
             for p in sum([pre, post, dev], [''])
-            for locs in local]
+            for locs in local
+        ]
         for v, vc in versions:
-            dists = list(setuptools.package_index.distros_for_url(
-                'http://example.com/example.zip#egg=example-' + v))
+            dists = list(
+                setuptools.package_index.distros_for_url(
+                    'http://example.com/example.zip#egg=example-' + v
+                )
+            )
             assert dists[0].version == ''
             assert dists[1].version == vc
 
@@ -204,8 +197,7 @@ class TestPackageIndex:
 
         expected_dir = str(tmpdir / 'project@master')
         expected = (
-            'git clone --quiet '
-            'https://github.example/group/project {expected_dir}'
+            '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,)
@@ -226,8 +218,7 @@ class TestPackageIndex:
 
         expected_dir = str(tmpdir / 'project')
         expected = (
-            'git clone --quiet '
-            'https://github.example/group/project {expected_dir}'
+            'git clone --quiet ' 'https://github.example/group/project {expected_dir}'
         ).format(**locals())
         os_system_mock.assert_called_once_with(expected)
 
@@ -243,8 +234,7 @@ class TestPackageIndex:
 
         expected_dir = str(tmpdir / 'project')
         expected = (
-            'svn checkout -q '
-            'svn+https://svn.example/project {expected_dir}'
+            'svn checkout -q ' 'svn+https://svn.example/project {expected_dir}'
         ).format(**locals())
         os_system_mock.assert_called_once_with(expected)
 
@@ -252,7 +242,8 @@ class TestPackageIndex:
 class TestContentCheckers:
     def test_md5(self):
         checker = setuptools.package_index.HashChecker.from_url(
-            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478')
+            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478'
+        )
         checker.feed('You should probably not be using MD5'.encode('ascii'))
         assert checker.hash.hexdigest() == 'f12895fdffbd45007040d2e44df98478'
         assert checker.is_valid()
@@ -260,25 +251,27 @@ class TestContentCheckers:
     def test_other_fragment(self):
         "Content checks should succeed silently if no hash is present"
         checker = setuptools.package_index.HashChecker.from_url(
-            'http://foo/bar#something%20completely%20different')
+            'http://foo/bar#something%20completely%20different'
+        )
         checker.feed('anything'.encode('ascii'))
         assert checker.is_valid()
 
     def test_blank_md5(self):
         "Content checks should succeed if a hash is empty"
-        checker = setuptools.package_index.HashChecker.from_url(
-            'http://foo/bar#md5=')
+        checker = setuptools.package_index.HashChecker.from_url('http://foo/bar#md5=')
         checker.feed('anything'.encode('ascii'))
         assert checker.is_valid()
 
     def test_get_hash_name_md5(self):
         checker = setuptools.package_index.HashChecker.from_url(
-            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478')
+            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478'
+        )
         assert checker.hash_name == 'md5'
 
     def test_report(self):
         checker = setuptools.package_index.HashChecker.from_url(
-            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478')
+            'http://foo/bar#md5=f12895fdffbd45007040d2e44df98478'
+        )
         rep = checker.report(lambda x: x, 'My message about %s')
         assert rep == 'My message about md5'
 
@@ -287,8 +280,8 @@ class TestContentCheckers:
 def temp_home(tmpdir, monkeypatch):
     key = (
         'USERPROFILE'
-        if platform.system() == 'Windows' and sys.version_info > (3, 8) else
-        'HOME'
+        if platform.system() == 'Windows' and sys.version_info > (3, 8)
+        else 'HOME'
     )
 
     monkeypatch.setitem(os.environ, key, str(tmpdir))
@@ -298,13 +291,25 @@ def temp_home(tmpdir, monkeypatch):
 class TestPyPIConfig:
     def test_percent_in_password(self, temp_home):
         pypirc = temp_home / '.pypirc'
-        pypirc.write(DALS("""
+        pypirc.write(
+            DALS(
+                """
             [pypi]
             repository=https://pypi.org
             username=jaraco
             password=pity%
-        """))
+        """
+            )
+        )
         cfg = setuptools.package_index.PyPIConfig()
         cred = cfg.creds_by_repository['https://pypi.org']
         assert cred.username == 'jaraco'
         assert cred.password == 'pity%'
+
+
+@pytest.mark.timeout(1)
+def test_REL_DoS():
+    """
+    REL should not hang on a contrived attack string.
+    """
+    setuptools.package_index.REL.search('< rel=' + ' ' * 2**12)
index 986058067bed4a9104a2481845a841eae760d53d..ed85e9bbd3d82e55871f77565bd1700d152c030e 100644 (file)
@@ -2,10 +2,7 @@ from setuptools.command.register import register
 from setuptools.dist import Distribution
 from setuptools.errors import RemovedCommandError
 
-try:
-    from unittest import mock
-except ImportError:
-    import mock
+from unittest import mock
 
 import pytest
 
index 7586cb262d4787193ab78d012ba0f75f1adc2e1c..4ed59bc24d4298f81aa4f39fdebd12c523a46241 100644 (file)
@@ -2,10 +2,7 @@ from setuptools.command.upload import upload
 from setuptools.dist import Distribution
 from setuptools.errors import RemovedCommandError
 
-try:
-    from unittest import mock
-except ImportError:
-    import mock
+from unittest import mock
 
 import pytest