From 050808db513e54c12190d64d5ba0a1f6e8ad9590 Mon Sep 17 00:00:00 2001 From: Benoit Pierre Date: Mon, 10 Jul 2017 02:54:29 +0200 Subject: [PATCH] fix handling of environment markers in `install_requires` --- setuptools/dist.py | 34 ++++++++++++++++++++++++++++++- setuptools/tests/test_egg_info.py | 15 +++++++++----- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/setuptools/dist.py b/setuptools/dist.py index 6b97ed33c5..cb887ea612 100644 --- a/setuptools/dist.py +++ b/setuptools/dist.py @@ -8,6 +8,7 @@ import distutils.core import distutils.cmd import distutils.dist +from collections import defaultdict from distutils.errors import (DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError) from distutils.util import rfc822_escape @@ -134,7 +135,13 @@ def check_extras(dist, attr, value): k, m = k.split(':', 1) if pkg_resources.invalid_marker(m): raise DistutilsSetupError("Invalid environment marker: " + m) - list(pkg_resources.parse_requirements(v)) + for r in pkg_resources.parse_requirements(v): + if r.marker: + tmpl = ( + "'extras_require' requirements cannot include " + "environment markers, in {section!r}: '{req!s}'" + ) + raise DistutilsSetupError(tmpl.format(section=k, req=r)) except (TypeError, ValueError, AttributeError): raise DistutilsSetupError( "'extras_require' must be a dictionary whose values are " @@ -346,6 +353,31 @@ def __init__(self, attrs=None): ) if getattr(self, 'python_requires', None): self.metadata.python_requires = self.python_requires + self._finalize_requires() + + def _finalize_requires(self): + """Move requirements in `install_requires` that + are using environment markers to `extras_require`. + """ + if not self.install_requires: + return + extras_require = defaultdict(list, ( + (k, list(pkg_resources.parse_requirements(v))) + for k, v in (self.extras_require or {}).items() + )) + install_requires = [] + for r in pkg_resources.parse_requirements(self.install_requires): + marker = r.marker + if not marker: + install_requires.append(r) + continue + r.marker = None + extras_require[':'+str(marker)].append(r) + self.extras_require = dict( + (k, [str(r) for r in v]) + for k, v in extras_require.items() + ) + self.install_requires = [str(r) for r in install_requires] def parse_config_files(self, filenames=None): """Parses configuration files from various levels diff --git a/setuptools/tests/test_egg_info.py b/setuptools/tests/test_egg_info.py index a32b981d67..55df448974 100644 --- a/setuptools/tests/test_egg_info.py +++ b/setuptools/tests/test_egg_info.py @@ -185,8 +185,12 @@ def test_install_requires_with_markers(self, tmpdir_cwd, env): self._run_install_command(tmpdir_cwd, env) egg_info_dir = self._find_egg_info_files(env.paths['lib']).base requires_txt = os.path.join(egg_info_dir, 'requires.txt') - assert "barbazquux;python_version<'2'" in open( - requires_txt).read().split('\n') + with open(requires_txt) as fp: + install_requires = fp.read() + assert install_requires.lstrip() == DALS(''' + [:python_version < "2"] + barbazquux + ''') assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_setup_requires_with_markers(self, tmpdir_cwd, env): @@ -202,10 +206,11 @@ def test_tests_require_with_markers(self, tmpdir_cwd, env): tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in") assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] - def test_extra_requires_with_markers(self, tmpdir_cwd, env): + def test_extras_require_with_markers(self, tmpdir_cwd, env): self._setup_script_with_requires( - """extra_requires={":python_version<'2'": ["barbazquux"]},""") - self._run_install_command(tmpdir_cwd, env) + """extras_require={"extra": ["barbazquux; python_version<'2'"]},""") + with pytest.raises(AssertionError): + self._run_install_command(tmpdir_cwd, env) assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] def test_python_requires_egg_info(self, tmpdir_cwd, env):