Allow maintaining extras from project

A project needs to be able to specify that it depends on a project
including extras for that project and still have it's version maintained
by global-requirements.

Currently if the requirements are seen to mismatch then the string from
global requirements is dropped in place of the project string, but this
drops any extras specified by project.

In the case that extras are different and the version needs updating add
a new combined requirement to the project with the original extras and
the new version.

Closes-Bug: #1567809
Change-Id: Ife48b7963a5e6706289f1b9a47cb95fae7f0bc22
This commit is contained in:
Jamie Lennox 2016-04-08 17:37:01 +10:00
parent 6410265574
commit 5033a0f716
3 changed files with 53 additions and 9 deletions

View File

@ -138,8 +138,24 @@ def _sync_requirements_file(
# 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]))
# NOTE(jamielennox): extras are allowed to be specified in
# a project's requirements and the version be updated and
# extras maintained. Create a new ref object the same as
# the original but with the req's extras.
merged_ref = requirement.Requirement(ref[0].package,
ref[0].location,
ref[0].specifiers,
ref[0].markers,
ref[0].comment,
req[0].extras)
ref = (merged_ref, merged_ref.to_line())
if req[0] != ref[0]:
# A change on this entry
changes.append(Change(req[0].package, req[1], ref[1]))
if ref:
output_requirements.append(ref[0])
elif softupdate:

View File

@ -40,6 +40,20 @@ class Requirement(collections.namedtuple('Requirement',
cls, package, location, specifiers, markers, comment,
frozenset(extras or ()))
def to_line(self, marker_sep=';', line_prefix=''):
comment_p = ' ' if self.package else ''
comment = (comment_p + self.comment if self.comment else '')
marker = marker_sep + self.markers if self.markers else ''
package = line_prefix + self.package if self.package else ''
location = self.location + '#egg=' if self.location else ''
extras = '[%s]' % ",".join(sorted(self.extras)) if self.extras else ''
return '%s%s%s%s%s%s\n' % (location,
package,
extras,
self.specifiers,
marker,
comment)
Requirements = collections.namedtuple('Requirements', ['reqs'])
@ -135,13 +149,7 @@ def to_content(reqs, marker_sep=';', line_prefix='', prefix=True):
if prefix:
lines += _REQS_HEADER
for req in reqs.reqs:
comment_p = ' ' if req.package else ''
comment = (comment_p + req.comment if req.comment else '')
marker = marker_sep + req.markers if req.markers else ''
package = line_prefix + req.package if req.package else ''
location = req.location + '#egg=' if req.location else ''
lines.append('%s%s%s%s%s\n' % (
location, package, req.specifiers, marker, comment))
lines.append(req.to_line(marker_sep, line_prefix))
return u''.join(lines)

View File

@ -384,6 +384,26 @@ class TestSyncRequirementsFile(testtools.TestCase):
requirement.Requirement('', '', '', '', n)]),
reqs)
def test_extras_kept(self):
global_content = textwrap.dedent("""\
oslo.db>1.4.1
""")
project_content = textwrap.dedent("""\
oslo.db[fixture,mysql]>1.3
""")
global_reqs = requirement.parse(global_content)
project_reqs = list(requirement.to_reqs(project_content))
actions, reqs = update._sync_requirements_file(
global_reqs, project_reqs, 'f', False, False, False)
self.assertEqual(requirement.Requirements([
requirement.Requirement(
'oslo.db', '', '>1.4.1', '', '', ['fixture', 'mysql'])]),
reqs)
self.assertThat(actions, matchers.HasLength(3))
self.assertEqual(project.StdOut(
" oslo.db[fixture,mysql]>1.3 -> "
"oslo.db[fixture,mysql]>1.4.1\n"), actions[2])
class TestCopyRequires(testtools.TestCase):