Commit 2328be3c authored by Benoit Pierre's avatar Benoit Pierre

fix `extras_require` handling

Allow requirements of the form `"extra": ["barbazquux; {marker}"]`
by internally converting them to `"extra:{marker}": ["barbazquux"]`.
parent 7c2df64c
...@@ -9,7 +9,6 @@ import distutils.core ...@@ -9,7 +9,6 @@ import distutils.core
import distutils.cmd import distutils.cmd
import distutils.dist import distutils.dist
import itertools import itertools
import operator
from collections import defaultdict from collections import defaultdict
from distutils.errors import ( from distutils.errors import (
DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError, DistutilsOptionError, DistutilsPlatformError, DistutilsSetupError,
...@@ -17,7 +16,7 @@ from distutils.errors import ( ...@@ -17,7 +16,7 @@ from distutils.errors import (
from distutils.util import rfc822_escape from distutils.util import rfc822_escape
from setuptools.extern import six from setuptools.extern import six
from setuptools.extern.six.moves import map, filter from setuptools.extern.six.moves import map
from pkg_resources.extern import packaging from pkg_resources.extern import packaging
from setuptools.depends import Require from setuptools.depends import Require
...@@ -146,17 +145,7 @@ def _check_extra(extra, reqs): ...@@ -146,17 +145,7 @@ def _check_extra(extra, reqs):
name, sep, marker = extra.partition(':') name, sep, marker = extra.partition(':')
if marker and pkg_resources.invalid_marker(marker): if marker and pkg_resources.invalid_marker(marker):
raise DistutilsSetupError("Invalid environment marker: " + marker) raise DistutilsSetupError("Invalid environment marker: " + marker)
list(pkg_resources.parse_requirements(reqs))
# extras requirements cannot themselves have markers
parsed = pkg_resources.parse_requirements(reqs)
marked_reqs = filter(operator.attrgetter('marker'), parsed)
bad_req = next(marked_reqs, None)
if bad_req:
tmpl = (
"'extras_require' requirements cannot include "
"environment markers, in {name!r}: '{bad_req!s}'"
)
raise DistutilsSetupError(tmpl.format(**locals()))
def assert_bool(dist, attr, value): def assert_bool(dist, attr, value):
...@@ -366,18 +355,29 @@ class Distribution(Distribution_parse_config_files, _Distribution): ...@@ -366,18 +355,29 @@ class Distribution(Distribution_parse_config_files, _Distribution):
def _finalize_requires(self): def _finalize_requires(self):
""" """
Move requirements in `install_requires` that Fix environment markers in `install_requires` and `extras_require`.
are using environment markers to `extras_require`.
- move requirements in `install_requires` that are using environment
markers to `extras_require`.
- convert requirements in `extras_require` of the form
`"extra": ["barbazquux; {marker}"]` to
`"extra:{marker}": ["barbazquux"]`.
""" """
if not getattr(self, 'install_requires', None): extras_require = defaultdict(list)
return for k, v in (
extras_require = getattr(self, 'extras_require', None) getattr(self, 'extras_require', None) or {}
extras_require = defaultdict(list, ( ).items():
(k, list(pkg_resources.parse_requirements(v))) for r in pkg_resources.parse_requirements(v):
for k, v in (extras_require or {}).items() marker = r.marker
)) if marker:
r.marker = None
extras_require[k + ':' + str(marker)].append(r)
else:
extras_require[k].append(r)
install_requires = [] install_requires = []
for r in pkg_resources.parse_requirements(self.install_requires): for r in pkg_resources.parse_requirements(
getattr(self, 'install_requires', None) or ()
):
marker = r.marker marker = r.marker
if not marker: if not marker:
install_requires.append(r) install_requires.append(r)
......
...@@ -182,6 +182,11 @@ class TestEggInfo(object): ...@@ -182,6 +182,11 @@ class TestEggInfo(object):
mismatch_marker = "python_version<'{this_ver}'".format( mismatch_marker = "python_version<'{this_ver}'".format(
this_ver=sys.version_info[0], this_ver=sys.version_info[0],
) )
# Alternate equivalent syntax.
mismatch_marker_alternate = 'python_version < "{this_ver}"'.format(
this_ver=sys.version_info[0],
)
invalid_marker = "<=>++"
def test_install_requires_with_markers(self, tmpdir_cwd, env): def test_install_requires_with_markers(self, tmpdir_cwd, env):
tmpl = 'install_requires=["barbazquux;{marker}"],' tmpl = 'install_requires=["barbazquux;{marker}"],'
...@@ -193,9 +198,9 @@ class TestEggInfo(object): ...@@ -193,9 +198,9 @@ class TestEggInfo(object):
with open(requires_txt) as fp: with open(requires_txt) as fp:
install_requires = fp.read() install_requires = fp.read()
expected_requires = DALS(''' expected_requires = DALS('''
[:python_version < "{sys.version_info[0]}"] [:{marker}]
barbazquux barbazquux
''').format(sys=sys) ''').format(marker=self.mismatch_marker_alternate)
assert install_requires.lstrip() == expected_requires assert install_requires.lstrip() == expected_requires
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
...@@ -214,19 +219,53 @@ class TestEggInfo(object): ...@@ -214,19 +219,53 @@ class TestEggInfo(object):
tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in") tmpdir_cwd, env, cmd=['test'], output="Ran 0 tests in")
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
def test_extras_require_with_markers(self, tmpdir_cwd, env): def test_extras_require_with_marker(self, tmpdir_cwd, env):
tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},' tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},'
req = tmpl.format(marker=self.mismatch_marker) req = tmpl.format(marker=self.mismatch_marker)
self._setup_script_with_requires(req) self._setup_script_with_requires(req)
self._run_install_command(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')
with open(requires_txt) as fp:
install_requires = fp.read()
expected_requires = DALS('''
[:{marker}]
barbazquux
''').format(marker=self.mismatch_marker)
assert install_requires.lstrip() == expected_requires
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == [] assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
def test_extras_require_with_markers_in_req(self, tmpdir_cwd, env): def test_extras_require_with_marker_in_req(self, tmpdir_cwd, env):
tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},' tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},'
req = tmpl.format(marker=self.mismatch_marker) req = tmpl.format(marker=self.mismatch_marker)
self._setup_script_with_requires(req) self._setup_script_with_requires(req)
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')
with open(requires_txt) as fp:
install_requires = fp.read()
expected_requires = DALS('''
[extra:{marker}]
barbazquux
''').format(marker=self.mismatch_marker_alternate)
assert install_requires.lstrip() == expected_requires
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
def test_extras_require_with_invalid_marker(self, tmpdir_cwd, env):
tmpl = 'extras_require={{":{marker}": ["barbazquux"]}},'
req = tmpl.format(marker=self.invalid_marker)
self._setup_script_with_requires(req)
with pytest.raises(AssertionError): with pytest.raises(AssertionError):
self._run_install_command(tmpdir_cwd, env) self._run_install_command(tmpdir_cwd, env)
assert glob.glob(os.path.join(env.paths['lib'], 'barbazquux*')) == []
def test_extras_require_with_invalid_marker_in_req(self, tmpdir_cwd, env):
tmpl = 'extras_require={{"extra": ["barbazquux; {marker}"]}},'
req = tmpl.format(marker=self.invalid_marker)
self._setup_script_with_requires(req)
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): def test_python_requires_egg_info(self, tmpdir_cwd, env):
self._setup_script_with_requires( self._setup_script_with_requires(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment