Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle when you combine increments without a base equals #177

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Unreleased
- Add ``BUILDOUT_HOME`` as an alternate way to control how the user default
configuration is found.

- Fix edgecase where using multiple inheritance and increments can result in
some increments not being included in the final value
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Edge case" is two words.

Other changelog entries end with a final period, perhaps it would be good to follow that convention.

It would also be good to link to a bug report.


2.2.1 (2013-09-05)
==================

Expand Down
23 changes: 21 additions & 2 deletions src/zc/buildout/buildout.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,16 @@ def __init__(self, config_file, cloptions,
# apply command-line options
_update(data, cloptions)

# convert any remaining += to =
for sectionname in data:
section = data[sectionname]
s2 = section.copy()
for k, v in s2.items():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would avoid the s2 local variable and instead do

    for k, v in list(section.items()):

if k.endswith('+'):
key = k.rstrip(' +')
section[key] = v
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible for a section to already have a key here? Will this work sensibly (e.g. letting the random dictionary iteration order to pick which one overrides the other would be a bad thing)?

I think these two examples should have the same effect:

# a.cfg
[section]
param = 1
param = 2

and

# b.cfg
[section]
param += 1
param = 2

i.e. latter option overrides the former one.

The following config is subtly different:

# c.cfg
[section]
param = 1
param += 2

and I would expect it to produce the same as

# c1.cfg
[section]
param = 1
# c2.cfg
[buildout]
extends = c1.cfg
[section]
param += 2

i.e. section:param to be set to '1\n2'.

Do you agree? Does your PR implement this?

del section[k]

# Set up versions section, if necessary
if 'versions' not in data['buildout']:
data['buildout']['versions'] = ('versions', 'DEFAULT_VALUE')
Expand Down Expand Up @@ -1651,11 +1661,20 @@ def _update_section(s1, s2):
if k.endswith('+'):
key = k.rstrip(' +')
# Find v1 in s2 first; it may have been defined locally too.
v1, note1 = s2.get(key, s1.get(key, ("", "")))
v1, note1 = s2.get(key, s1.get(key, s1.get(k, ("", ""))))
if v1 == '':
# merging += in nothing. Keep as +=
key = key + " +"
elif k in s1:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if k is 'param +' but s1 has a key 'param+' or the other way around? For example:

# a.cfg
[section]
param += 1
# b.cfg
[buildout]
extends = a.cfg
[section]
# note lack of space in front of `+=`!
param+=2

should be handled the same way as

# a.cfg
[section]
param += 1
# b1.cfg
[buildout]
extends = a.cfg
[section]
param += 2

# merging += into +=. keep as +=
key = key + " +"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are key names normalized? If k is 'param+' and now we set key to 'param +', we'll end up with a duplicate in s2.

del s1[k]
else:
# merging += into =. convert to =
del s2[k]
newnote = ' [+] '.join((note1, note2)).strip()
s2[key] = "\n".join((v1).split('\n') +
v2.split('\n')), newnote
del s2[k]
elif k.endswith('-'):
key = k.rstrip(' -')
# Find v1 in s2 first; it may have been set by a += operation first
Expand Down
39 changes: 39 additions & 0 deletions src/zc/buildout/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2671,6 +2671,45 @@ def increment_buildout_with_multiple_extended_files_421022():
recipe='zc.buildout:debug'
"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP-8 recommends two blank lines between top-level functions.

def increment_buildout_with_multiple_extended_without_base_equals():
r"""

>>> write('buildout.cfg', '''
... [buildout]
... extends = base1.cfg base2.cfg
... parts += foo
... [foo]
... recipe = zc.buildout:debug
... [base1]
... recipe = zc.buildout:debug
... [base2]
... recipe = zc.buildout:debug
... ''')
>>> write('base1.cfg', '''
... [buildout]
... extends = base3.cfg
... parts += base1
... ''')
>>> write('base2.cfg', '''
... [buildout]
... extends = base3.cfg
... parts += base2
... ''')
>>> write('base3.cfg', '''
... [buildout]
... ''')

>>> print_(system(buildout), end='')
Installing base1.
recipe='zc.buildout:debug'
Installing base2.
recipe='zc.buildout:debug'
Installing foo.
recipe='zc.buildout:debug'
"""



Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PEP-8 recommends two blank lines between top-level functions.

I don't believe a single test case is sufficient. I outlined some other possible test cases in my other comments.

def increment_on_command_line():
r"""
>>> write('buildout.cfg', '''
Expand Down