Skip to content

Commit ae9cc57

Browse files
committed
Fixed #324
* Prompts error and stop generating the output if multiple keys are detected. Signed-off-by: Chin Yeung Li <[email protected]>
1 parent 1f364f0 commit ae9cc57

File tree

4 files changed

+78
-15
lines changed

4 files changed

+78
-15
lines changed

src/attributecode/cmd.py

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,22 @@ def inventory(location, output, mapping, mapping_file, quiet, format, verbose):
157157

158158
errors, abouts = model.collect_inventory(location, use_mapping=mapping, mapping_file=mapping_file)
159159

160-
write_errors = model.write_output(abouts, output, format)
161-
for err in write_errors:
162-
errors.append(err)
160+
# Do not write the output if one of the ABOUT files has duplicated key names
161+
dup_error_msg = u'Duplicated key name(s)'
162+
halt_output = False
163+
for err in errors:
164+
if dup_error_msg in err.message:
165+
halt_output = True
166+
break
167+
168+
if not halt_output:
169+
write_errors = model.write_output(abouts, output, format)
170+
for err in write_errors:
171+
errors.append(err)
172+
else:
173+
msg = u'Duplicated key names are not supported.\n' + \
174+
'Please correct and re-run.'
175+
print(msg)
163176

164177
finalized_errors = update_severity_level_about_resource_path_not_exist_error(errors)
165178

src/attributecode/model.py

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060
from attributecode import util
6161
from attributecode.util import add_unc
6262
from attributecode.util import copy_license_notice_files
63+
from attributecode.util import check_duplicate_keys_about_file
6364
from attributecode.util import on_windows
6465
from attributecode.util import UNC_PREFIX
6566
from attributecode.util import UNC_PREFIX_POSIX
@@ -983,18 +984,23 @@ def load(self, location, use_mapping=False, mapping_file=None):
983984
loc = add_unc(loc)
984985
with codecs.open(loc, encoding='utf-8') as txt:
985986
input_text = txt.read()
986-
"""
987-
The running_inventory defines if the current process is 'inventory' or not.
988-
This is used for the validation of the about_resource_path.
989-
In the 'inventory' command, the code will use the parent of the about_file_path
990-
location and join with the 'about_resource_path' for the validation.
991-
On the other hand, in the 'gen' command, the code will use the
992-
generated location (aka base_dir) along with the parent of the about_file_path
993-
and then join with the 'about_resource_path'
994-
"""
995-
running_inventory = True
996-
errs = self.load_dict(saneyaml.load(input_text), base_dir, running_inventory, use_mapping, mapping_file)
997-
errors.extend(errs)
987+
dup_keys = check_duplicate_keys_about_file(input_text)
988+
if dup_keys:
989+
msg = ('Duplicated key name(s): %(dup_keys)s' % locals())
990+
errors.append(Error(ERROR, msg % locals()))
991+
else:
992+
"""
993+
The running_inventory defines if the current process is 'inventory' or not.
994+
This is used for the validation of the about_resource_path.
995+
In the 'inventory' command, the code will use the parent of the about_file_path
996+
location and join with the 'about_resource_path' for the validation.
997+
On the other hand, in the 'gen' command, the code will use the
998+
generated location (aka base_dir) along with the parent of the about_file_path
999+
and then join with the 'about_resource_path'
1000+
"""
1001+
running_inventory = True
1002+
errs = self.load_dict(saneyaml.load(input_text), base_dir, running_inventory, use_mapping, mapping_file)
1003+
errors.extend(errs)
9981004
except Exception as e:
9991005
msg = 'Cannot load invalid ABOUT file: %(location)r: %(e)r\n' + str(e)
10001006
errors.append(Error(CRITICAL, msg % locals()))

src/attributecode/util.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,23 @@ def check_file_names(paths):
124124
return errors
125125

126126

127+
def check_duplicate_keys_about_file(context):
128+
keys = []
129+
dup_keys = []
130+
for line in context.splitlines():
131+
"""
132+
Ignore all the continuation string, string block and empty line
133+
"""
134+
if not line.startswith(' ') and not len(line.strip()) == 0 :
135+
# Get the key name
136+
key = line.partition(':')[0]
137+
if key in keys:
138+
dup_keys.append(key)
139+
else:
140+
keys.append(key)
141+
return dup_keys
142+
143+
127144
def get_absolute(location):
128145
"""
129146
Return an absolute normalized location.

tests/test_util.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -444,3 +444,30 @@ def test_update_severity_level_about_resource_path_not_exist_error(self):
444444
input_err = [Error(ERROR, 'Field about_resource_path: test.tar.gz does not exist')]
445445
expected_err = [Error(INFO, 'Field about_resource_path: test.tar.gz does not exist')]
446446
assert util.update_severity_level_about_resource_path_not_exist_error(input_err) == expected_err
447+
448+
def test_check_duplicate_keys_about_file(self):
449+
test = '''
450+
name: test
451+
notes: some notes
452+
453+
license_expression: mit
454+
notes: dup key here
455+
'''
456+
expected = ['notes']
457+
assert expected == util.check_duplicate_keys_about_file(test)
458+
459+
def test_check_duplicate_keys_about_file_with_multiline(self):
460+
test = '''
461+
name: test
462+
owner: test
463+
notes: |
464+
String block here
465+
license_expression: mit
466+
owner: test1
467+
notes: continuation
468+
line
469+
description: sample
470+
'''
471+
expected = ['owner', 'notes']
472+
assert expected == util.check_duplicate_keys_about_file(test)
473+

0 commit comments

Comments
 (0)