Support multiple marked reqs for the same package
This is needed when a single specifier set is insufficient to handle both Python2.7 and 3.x. We're not currently dealing with this, but I have observed it in other projects and we can be forearmed quite easily. Change-Id: If1493625d0d2ebf750159ccb8ed0fd22699766a1
This commit is contained in:
parent
32fed9acdb
commit
a3a3f7f57a
|
@ -17,6 +17,7 @@ from __future__ import print_function
|
|||
import io
|
||||
import StringIO
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import fixtures
|
||||
import pkg_resources
|
||||
|
@ -348,3 +349,128 @@ class TestParseRequirementFailures(testtools.TestCase):
|
|||
def test_does_not_parse(self):
|
||||
with testtools.ExpectedException(pkg_resources.RequirementParseError):
|
||||
update._parse_requirement(self.line)
|
||||
|
||||
|
||||
class TestSyncRequirementsFile(testtools.TestCase):
|
||||
|
||||
def test_multiple_lines_in_global_one_in_project(self):
|
||||
global_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
project_content = textwrap.dedent("""\
|
||||
foo
|
||||
""")
|
||||
global_reqs = update._parse_reqs(global_content)
|
||||
actions = update._sync_requirements_file(
|
||||
global_reqs, project_content, 'f', False, False, 'f', False)
|
||||
self.assertEqual(update.File('f', textwrap.dedent("""\
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")), actions[1])
|
||||
self.assertEqual(update.StdOut(
|
||||
" foo "
|
||||
"-> foo<2;python_version=='2.7'\n"), actions[4])
|
||||
self.assertEqual(update.StdOut(
|
||||
" "
|
||||
"-> foo>1;python_version!='2.7'\n"), actions[5])
|
||||
self.assertThat(actions, matchers.HasLength(6))
|
||||
|
||||
def test_multiple_lines_separated_in_project_nochange(self):
|
||||
global_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
project_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
# mumbo gumbo
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
global_reqs = update._parse_reqs(global_content)
|
||||
actions = update._sync_requirements_file(
|
||||
global_reqs, project_content, 'f', False, False, 'f', False)
|
||||
self.assertEqual(update.File('f', textwrap.dedent("""\
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
# mumbo gumbo
|
||||
""")), actions[1])
|
||||
self.assertThat(actions, matchers.HasLength(2))
|
||||
|
||||
def test_multiple_lines_separated_in_project(self):
|
||||
global_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
project_content = textwrap.dedent("""\
|
||||
foo<1.8;python_version=='2.7'
|
||||
# mumbo gumbo
|
||||
foo>0.9;python_version!='2.7'
|
||||
""")
|
||||
global_reqs = update._parse_reqs(global_content)
|
||||
actions = update._sync_requirements_file(
|
||||
global_reqs, project_content, 'f', False, False, 'f', False)
|
||||
self.assertEqual(update.File('f', textwrap.dedent("""\
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
# mumbo gumbo
|
||||
""")), actions[1])
|
||||
self.assertEqual(update.StdOut(
|
||||
" foo<1.8;python_version=='2.7' -> "
|
||||
"foo<2;python_version=='2.7'\n"), actions[4])
|
||||
self.assertEqual(update.StdOut(
|
||||
" foo>0.9;python_version!='2.7' -> "
|
||||
"foo>1;python_version!='2.7'\n"), actions[5])
|
||||
self.assertThat(actions, matchers.HasLength(6))
|
||||
|
||||
def test_multiple_lines_nochange(self):
|
||||
global_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
project_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
global_reqs = update._parse_reqs(global_content)
|
||||
actions = update._sync_requirements_file(
|
||||
global_reqs, project_content, 'f', False, False, 'f', False)
|
||||
self.assertEqual(update.File('f', textwrap.dedent("""\
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")), actions[1])
|
||||
self.assertThat(actions, matchers.HasLength(2))
|
||||
|
||||
def test_single_global_multiple_in_project(self):
|
||||
global_content = textwrap.dedent("""\
|
||||
foo>1
|
||||
""")
|
||||
project_content = textwrap.dedent("""\
|
||||
foo<2;python_version=='2.7'
|
||||
foo>1;python_version!='2.7'
|
||||
""")
|
||||
global_reqs = update._parse_reqs(global_content)
|
||||
actions = update._sync_requirements_file(
|
||||
global_reqs, project_content, 'f', False, False, 'f', False)
|
||||
self.assertEqual(update.File('f', textwrap.dedent("""\
|
||||
# The order of packages is significant, because pip processes them in the order
|
||||
# of appearance. Changing the order has an impact on the overall integration
|
||||
# process, which may cause wedges in the gate later.
|
||||
foo>1
|
||||
""")), actions[1])
|
||||
self.assertEqual(update.StdOut(
|
||||
" foo<2;python_version=='2.7' -> foo>1\n"), actions[4])
|
||||
self.assertEqual(update.StdOut(
|
||||
" foo>1;python_version!='2.7' -> \n"), actions[5])
|
||||
self.assertThat(actions, matchers.HasLength(6))
|
||||
|
|
|
@ -161,27 +161,31 @@ def _sync_requirements_file(
|
|||
source_reqs, content, dest_path, softupdate, hacking, dest_name,
|
||||
non_std_reqs):
|
||||
actions = []
|
||||
dest_reqs = list(_content_to_reqs(content))
|
||||
dest_sequence = list(_content_to_reqs(content))
|
||||
dest_reqs = _parse_reqs(content)
|
||||
changes = []
|
||||
actions.append(Verbose("Syncing %s" % dest_path))
|
||||
content_lines = []
|
||||
processed_packages = set()
|
||||
|
||||
# Check the instructions header
|
||||
if dest_reqs[:len(_REQS_HEADER)] != zip(
|
||||
if dest_sequence[:len(_REQS_HEADER)] != zip(
|
||||
itertools.repeat(None), _REQS_HEADER):
|
||||
content_lines.extend(_REQS_HEADER)
|
||||
|
||||
for req, req_line in dest_reqs:
|
||||
for req, req_line in dest_sequence:
|
||||
if req is None:
|
||||
# Unparsable lines.
|
||||
content_lines.append(req_line)
|
||||
continue
|
||||
|
||||
if not req.package:
|
||||
elif not req.package:
|
||||
# Comment-only lines
|
||||
content_lines.append(req_line)
|
||||
continue
|
||||
elif req.package.lower() in processed_packages:
|
||||
continue
|
||||
|
||||
processed_packages.add(req.package.lower())
|
||||
# Special cases:
|
||||
# projects need to align hacking version on their own time
|
||||
if req.package == "hacking" and not hacking:
|
||||
|
@ -190,9 +194,19 @@ def _sync_requirements_file(
|
|||
|
||||
reference = source_reqs.get(req.package.lower())
|
||||
if reference:
|
||||
if reference[0] != req:
|
||||
changes.append(Change(req.package, req_line, reference[1]))
|
||||
content_lines.append(reference[1])
|
||||
actual = dest_reqs.get(req.package.lower())
|
||||
for req, ref in itertools.izip_longest(actual, reference):
|
||||
if not req:
|
||||
# More in globals
|
||||
changes.append(Change(ref[0].package, '', ref[1]))
|
||||
elif not ref:
|
||||
# less in globals
|
||||
changes.append(Change(req[0].package, req[1], ''))
|
||||
elif req[0] != ref[0]:
|
||||
# A change on this entry
|
||||
changes.append(Change(req[0].package, req[1], ref[1]))
|
||||
if ref:
|
||||
content_lines.append(ref[1])
|
||||
elif softupdate:
|
||||
# under softupdate we pass through anything unknown packages,
|
||||
# this is intended for ecosystem projects that want to stay in
|
||||
|
@ -272,7 +286,7 @@ def _parse_reqs(content):
|
|||
req_lines = _content_to_reqs(content)
|
||||
for req, req_line in req_lines:
|
||||
if req is not None:
|
||||
reqs[req.package.lower()] = (req, req_line)
|
||||
reqs.setdefault(req.package.lower(), []).append((req, req_line))
|
||||
return reqs
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue