Imported Upstream version 38.0.0 upstream/38.0.0
authorDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 14 Jan 2019 01:39:58 +0000 (10:39 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Mon, 14 Jan 2019 01:39:58 +0000 (10:39 +0900)
CHANGES.rst
setup.cfg
setup.py
setuptools/command/egg_info.py
setuptools/dist.py
setuptools/tests/test_egg_info.py

index f75bb62..c8a3ecc 100644 (file)
@@ -1,3 +1,10 @@
+v38.0.0
+-------
+
+* #458: In order to support deterministic builds, Setuptools no
+  longer allows packages to declare ``install_requires`` as
+  unordered sequences (sets or dicts).
+
 v37.0.0
 -------
 
index 04dcfbf..c06810d 100755 (executable)
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
 [bumpversion]
-current_version = 37.0.0
+current_version = 38.0.0
 commit = True
 tag = True
 
index 25d44de..845588d 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -89,7 +89,7 @@ def pypi_link(pkg_filename):
 
 setup_params = dict(
     name="setuptools",
-    version="37.0.0",
+    version="38.0.0",
     description="Easily download, build, install, upgrade, and uninstall "
         "Python packages",
     author="Python Packaging Authority",
index a1d41b2..103c5f2 100755 (executable)
@@ -640,7 +640,7 @@ def write_requirements(cmd, basename, filename):
 
 
 def write_setup_requirements(cmd, basename, filename):
-    data = StringIO()
+    data = io.StringIO()
     _write_requirements(data, cmd.distribution.setup_requires)
     cmd.write_or_delete_file("setup-requirements", filename, data.getvalue())
 
index aa30450..477f93d 100644 (file)
@@ -166,6 +166,8 @@ def check_requirements(dist, attr, value):
     """Verify that install_requires is a valid requirements list"""
     try:
         list(pkg_resources.parse_requirements(value))
+        if isinstance(value, (dict, set)):
+            raise TypeError("Unordered types are not allowed")
     except (TypeError, ValueError) as error:
         tmpl = (
             "{attr!r} must be a string or list of strings "
index a97d0c8..66ca916 100644 (file)
@@ -191,7 +191,8 @@ class TestEggInfo(object):
                 test_params = test.lstrip().split('\n\n', 3)
                 name_kwargs = test_params.pop(0).split('\n')
                 if len(name_kwargs) > 1:
-                    install_cmd_kwargs = ast.literal_eval(name_kwargs[1].strip())
+                    val = name_kwargs[1].strip()
+                    install_cmd_kwargs = ast.literal_eval(val)
                 else:
                     install_cmd_kwargs = {}
                 name = name_kwargs[0].strip()
@@ -211,9 +212,11 @@ class TestEggInfo(object):
                                                   expected_requires,
                                                   install_cmd_kwargs,
                                                   marks=marks))
-            return pytest.mark.parametrize('requires,use_setup_cfg,'
-                                           'expected_requires,install_cmd_kwargs',
-                                           argvalues, ids=idlist)
+            return pytest.mark.parametrize(
+                'requires,use_setup_cfg,'
+                'expected_requires,install_cmd_kwargs',
+                argvalues, ids=idlist,
+            )
 
     @RequiresTestHelper.parametrize(
         # Format of a test:
@@ -228,6 +231,20 @@ class TestEggInfo(object):
         # expected contents of requires.txt
 
         '''
+        install_requires_deterministic
+
+        install_requires=["fake-factory==0.5.2", "pytz"]
+
+        [options]
+        install_requires =
+            fake-factory==0.5.2
+            pytz
+
+        fake-factory==0.5.2
+        pytz
+        ''',
+
+        '''
         install_requires_with_marker
 
         install_requires=["barbazquux;{mismatch_marker}"],
@@ -361,9 +378,9 @@ class TestEggInfo(object):
         mismatch_marker=mismatch_marker,
         mismatch_marker_alternate=mismatch_marker_alternate,
     )
-    def test_requires(self, tmpdir_cwd, env,
-                      requires, use_setup_cfg,
-                      expected_requires, install_cmd_kwargs):
+    def test_requires(
+            self, tmpdir_cwd, env, requires, use_setup_cfg,
+            expected_requires, install_cmd_kwargs):
         self._setup_script_with_requires(requires, use_setup_cfg)
         self._run_install_command(tmpdir_cwd, env, **install_cmd_kwargs)
         egg_info_dir = os.path.join('.', 'foo.egg-info')
@@ -376,6 +393,17 @@ class TestEggInfo(object):
         assert install_requires.lstrip() == expected_requires
         assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
 
+    def test_install_requires_unordered_disallowed(self, tmpdir_cwd, env):
+        """
+        Packages that pass unordered install_requires sequences
+        should be rejected as they produce non-deterministic
+        builds. See #458.
+        """
+        req = 'install_requires={"fake-factory==0.5.2", "pytz"}'
+        self._setup_script_with_requires(req)
+        with pytest.raises(AssertionError):
+            self._run_install_command(tmpdir_cwd, env)
+
     def test_extras_require_with_invalid_marker(self, tmpdir_cwd, env):
         tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},'
         req = tmpl.format(marker=self.invalid_marker)