Skip to content

Commit 6eed786

Browse files
committed
#566 - Working in progess to support multiple about_resource
Signed-off-by: Chin Yeung Li <[email protected]>
1 parent f4c4db4 commit 6eed786

File tree

8 files changed

+157
-74
lines changed

8 files changed

+157
-74
lines changed

src/attributecode/gen.py

Lines changed: 109 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,12 @@ def check_newline_in_file_field(component):
9494
if k in file_fields:
9595
try:
9696
if '\n' in component[k]:
97-
if k == u'about_resource':
98-
msg = (
99-
"Multiple lines detected in 'about_resource' for '%s' which is not supported.") % component['about_resource']
100-
else:
101-
msg = ("New line character detected in '%s' for '%s' which is not supported."
102-
"\nPlease use ',' to declare multiple files.") % (k, component['about_resource'])
97+
# if k == u'about_resource':
98+
# msg = (
99+
# "Multiple lines detected in 'about_resource' for '%s' which is not supported.") % component['about_resource']
100+
# else:
101+
msg = ("New line character detected in '%s' for '%s' which is not supported."
102+
"\nPlease use ',' to declare multiple files.") % (k, component['about_resource'])
103103
errors.append(Error(CRITICAL, msg))
104104
except:
105105
pass
@@ -123,9 +123,6 @@ def load_inventory(location, from_attrib=False, base_dir=None, scancode=False, r
123123
Load the inventory file at `location` for ABOUT and LICENSE files stored in
124124
the `base_dir`. Return a list of errors and a list of About objects
125125
validated against the `base_dir`.
126-
127-
Optionally use `reference_dir` as the directory location of extra reference
128-
license and notice files to reuse.
129126
"""
130127
errors = []
131128
abouts = []
@@ -164,21 +161,37 @@ def load_inventory(location, from_attrib=False, base_dir=None, scancode=False, r
164161
for component in stripped_inv:
165162
if not from_attrib:
166163
if 'about_resource' in component:
167-
arp = component['about_resource']
168-
dup_err = check_duplicated_about_resource(arp, arp_list)
169-
if dup_err:
170-
if not dup_err in errors:
171-
errors.append(dup_err)
172-
else:
173-
arp_list.append(arp)
174-
175-
invalid_about_filename = check_about_resource_filename(arp)
176-
if invalid_about_filename and not invalid_about_filename in errors:
177-
errors.append(invalid_about_filename)
164+
if isinstance(component['about_resource'], str):
165+
arp = component['about_resource']
166+
dup_err = check_duplicated_about_resource(arp, arp_list)
167+
if dup_err:
168+
if dup_err not in errors:
169+
errors.append(dup_err)
170+
else:
171+
arp_list.append(arp)
178172

173+
invalid_about_filename = check_about_resource_filename(arp)
174+
if invalid_about_filename and invalid_about_filename not in errors:
175+
errors.append(invalid_about_filename)
176+
else:
177+
for arp in component['about_resource']:
178+
dup_err = check_duplicated_about_resource(
179+
arp, arp_list)
180+
if dup_err:
181+
if dup_err not in errors:
182+
errors.append(dup_err)
183+
else:
184+
arp_list.append(arp)
185+
186+
invalid_about_filename = check_about_resource_filename(
187+
arp)
188+
if invalid_about_filename and invalid_about_filename not in errors:
189+
errors.append(invalid_about_filename)
190+
"""
179191
newline_in_file_err = check_newline_in_file_field(component)
180192
if newline_in_file_err:
181193
errors.extend(newline_in_file_err)
194+
"""
182195

183196
if errors:
184197
return errors, abouts
@@ -197,50 +210,27 @@ def load_inventory(location, from_attrib=False, base_dir=None, scancode=False, r
197210
)
198211
errors.append(Error(CRITICAL, msg))
199212
return errors, abouts
213+
200214
# Set about file path to '' if no 'about_resource' is provided from
201215
# the input
202216
if 'about_resource' not in fields:
203217
afp = ''
218+
about, custom_fields_list, process_errors = process_inventory(afp, fields,
219+
from_attrib, base_dir, scancode, reference_dir)
220+
abouts.append(about)
204221
else:
205-
afp = fields.get(model.About.ABOUT_RESOURCE_ATTR)
206-
207-
afp = util.to_posix(afp)
208-
if base_dir:
209-
loc = join(base_dir, afp)
210-
else:
211-
loc = afp
212-
about = model.About(about_file_path=afp)
213-
about.location = loc
214-
215-
# Update value for 'about_resource'
216-
# keep only the filename or '.' if it's a directory
217-
if 'about_resource' in fields:
218-
updated_resource_value = u''
219-
resource_path = fields['about_resource']
220-
if resource_path.endswith(u'/'):
221-
updated_resource_value = u'.'
222-
else:
223-
updated_resource_value = basename(resource_path)
224-
fields['about_resource'] = updated_resource_value
225-
226-
ld_errors = about.load_dict(
227-
fields,
228-
base_dir,
229-
scancode=scancode,
230-
from_attrib=from_attrib,
231-
running_inventory=False,
232-
reference_dir=reference_dir,
233-
)
234-
235-
for severity, message in ld_errors:
236-
if 'Custom Field' in message:
237-
field_name = message.replace('Custom Field: ', '').strip()
238-
if not field_name in custom_fields_list:
239-
custom_fields_list.append(field_name)
222+
if scancode:
223+
afp_list = [fields.get(model.About.ABOUT_RESOURCE_ATTR)]
240224
else:
241-
errors.append(Error(severity, message))
225+
afp_list = fields.get(model.About.ABOUT_RESOURCE_ATTR)
226+
for afp in afp_list:
227+
about, custom_fields_list, process_errors = process_inventory(afp, fields,
228+
from_attrib, base_dir, scancode, reference_dir)
229+
abouts.append(about)
230+
231+
for err in process_errors:
232+
errors.append(err)
242233

243-
abouts.append(about)
244234
if custom_fields_list:
245235
custom_fields_err_msg = 'Field ' + \
246236
str(custom_fields_list) + ' is a custom field.'
@@ -249,6 +239,66 @@ def load_inventory(location, from_attrib=False, base_dir=None, scancode=False, r
249239
return errors, abouts
250240

251241

242+
def process_inventory(about_file_path, fields, from_attrib, base_dir, scancode, reference_dir):
243+
"""
244+
Return About object, a list of custom fields and a list of errors and
245+
validated against the `base_dir`.
246+
247+
Optionally use `reference_dir` as the directory location of extra reference
248+
license and notice files to reuse.
249+
"""
250+
custom_fields_list = []
251+
errors = []
252+
afp = util.to_posix(about_file_path)
253+
if base_dir:
254+
loc = join(base_dir, afp)
255+
else:
256+
loc = afp
257+
about = model.About(about_file_path=afp)
258+
about.location = loc
259+
260+
"""
261+
# Update value for 'about_resource'
262+
# keep only the filename or '.' if it's a directory
263+
if 'about_resource' in fields:
264+
updated_resource_list = []
265+
resource_path_list = fields['about_resource']
266+
for resource_path in resource_path_list:
267+
if resource_path.endswith(u'/'):
268+
updated_resource_list.append('.')
269+
else:
270+
updated_resource_list.append(basename(resource_path))
271+
fields['about_resource'] = updated_resource_list
272+
"""
273+
if 'about_resource' in fields:
274+
updated_resource_value = u''
275+
resource_path = about.about_file_path
276+
if resource_path.endswith(u'/'):
277+
updated_resource_value = u'.'
278+
else:
279+
updated_resource_value = basename(resource_path)
280+
fields['about_resource'] = updated_resource_value
281+
282+
ld_errors = about.load_dict(
283+
fields,
284+
base_dir,
285+
scancode=scancode,
286+
from_attrib=from_attrib,
287+
running_inventory=False,
288+
reference_dir=reference_dir,
289+
)
290+
291+
for severity, message in ld_errors:
292+
if 'Custom Field' in message:
293+
field_name = message.replace('Custom Field: ', '').strip()
294+
if field_name not in custom_fields_list:
295+
custom_fields_list.append(field_name)
296+
else:
297+
errors.append(Error(severity, message))
298+
299+
return about, custom_fields_list, errors
300+
301+
252302
def update_about_resource(self):
253303
pass
254304

@@ -283,6 +333,7 @@ def generate(location, base_dir, android=None, reference_dir=None, fetch_license
283333
scancode=scancode,
284334
worksheet=worksheet
285335
)
336+
286337
if gen_license:
287338
license_dict, err = model.pre_process_and_fetch_license_dict(
288339
abouts, api_url=api_url, api_key=api_key)
@@ -297,7 +348,7 @@ def generate(location, base_dir, android=None, reference_dir=None, fetch_license
297348
about.about_file_path = about.about_file_path.strip()
298349
if about.about_file_path.startswith('/'):
299350
about.about_file_path = about.about_file_path.lstrip('/')
300-
# Use the name as the ABOUT file name if about_resource is empty
351+
# Use the name as the ABOUT file name if about_file_path field is empty
301352
if not about.about_file_path:
302353
about.about_file_path = about.name.value
303354
dump_loc = join(bdir, about.about_file_path.lstrip('/'))
@@ -319,7 +370,6 @@ def generate(location, base_dir, android=None, reference_dir=None, fetch_license
319370
continue
320371

321372
try:
322-
323373
licenses_dict = {}
324374
if gen_license:
325375
# Write generated LICENSE file
@@ -344,9 +394,7 @@ def generate(location, base_dir, android=None, reference_dir=None, fetch_license
344394
about.license_url.present = True
345395
if about.spdx_license_key.value:
346396
about.spdx_license_key.present = True
347-
348397
about.dump(dump_loc, licenses_dict)
349-
350398
if android:
351399
"""
352400
Create MODULE_LICENSE_XXX and get context to create NOTICE file

src/attributecode/model.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1830,14 +1830,17 @@ def about_object_to_list_of_dictionary(abouts):
18301830
# from the output location
18311831
if 'about_resource' in ad.keys():
18321832
about_resource = ad['about_resource']
1833+
about_resource_dict = {}
18331834
for resource in about_resource:
18341835
updated_about_resource = posixpath.normpath(
18351836
posixpath.join(afp_parent, resource))
18361837
if resource == u'.':
18371838
if not updated_about_resource == '/':
18381839
updated_about_resource = updated_about_resource + '/'
1839-
ad['about_resource'] = dict(
1840-
[(updated_about_resource, None)])
1840+
about_resource_dict[updated_about_resource] = None
1841+
# about_resource_list.append(updated_about_resource)
1842+
# ad['about_resource'] = dict([(updated_about_resource, None)])
1843+
ad['about_resource'] = about_resource_dict
18411844
del ad['about_file_path']
18421845
serialized.append(ad)
18431846
return serialized

src/attributecode/util.py

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,13 @@ def load_csv(location):
309309
with open(location, mode='r', encoding='utf-8-sig',
310310
errors='replace') as csvfile:
311311
for row in csv.DictReader(csvfile):
312-
# convert all the column keys to lower case
313-
updated_row = {key.lower().strip(): value for key,
314-
value in row.items()}
312+
updated_row = {}
313+
for key, value in row.items():
314+
formatted_key = key.lower().strip()
315+
if formatted_key in file_fields:
316+
updated_row[formatted_key] = value.splitlines()
317+
else:
318+
updated_row[formatted_key] = value
315319
results.append(updated_row)
316320
return results
317321

@@ -545,8 +549,10 @@ def ungroup_licenses(licenses):
545549
return lic_key, lic_name, lic_file, lic_url, spdx_lic_key, lic_score, lic_matched_text
546550

547551

548-
# FIXME: add docstring
549552
def format_about_dict_output(about_dictionary_list):
553+
"""
554+
Format the dictionary list to be able to write to a CSV output
555+
"""
550556
formatted_list = []
551557
for element in about_dictionary_list:
552558
row_list = dict()
@@ -562,8 +568,10 @@ def format_about_dict_output(about_dictionary_list):
562568
return formatted_list
563569

564570

565-
# FIXME: add docstring
566571
def format_about_dict_for_json_output(about_dictionary_list):
572+
"""
573+
Format the dictionary list to be able to write to a JSON output
574+
"""
567575
licenses = ['license_key', 'license_name', 'license_file', 'license_url']
568576
json_formatted_list = []
569577
for element in about_dictionary_list:
@@ -812,7 +820,10 @@ def strip_inventory_value(inventory):
812820
for component in inventory:
813821
comp_dict = {}
814822
for key in component:
815-
comp_dict[key] = str(component[key]).strip()
823+
if isinstance(component[key], str):
824+
comp_dict[key] = component[key].strip()
825+
else:
826+
comp_dict[key] = component[key]
816827
stripped_inventory.append(comp_dict)
817828
return stripped_inventory
818829

tests/test_attrib.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,6 @@ def test_scancode_input_dup_lic_match(self):
245245
test_file = get_test_loc(
246246
'test_attrib/scancode_input/sc-dup-lic-match.json')
247247
errors, abouts = gen.load_inventory(test_file, scancode=True)
248-
print("############################")
249-
print(errors)
250248
# Check if there is error's level > INFO
251249
result = [(level, e) for level, e in errors if level > INFO]
252250
assert result == []
@@ -272,6 +270,10 @@ def test_scancode_input_dup_lic_match(self):
272270
# expected doesn't work well, it works after removed all the newline and spaces
273271
# assert expected == result
274272
# assert expected.splitlines(False) == result.splitlines(False)
273+
with open("C:\\Users\\thoma\\Desktop\\tmp\\AbcTK\\566\\about\\result.html", 'w') as result_file:
274+
result_file.write(result)
275+
with open("C:\\Users\\thoma\\Desktop\\tmp\\AbcTK\\566\\about\\expected.html", 'w') as expected_file:
276+
expected_file.write(expected)
275277
assert expected.replace('\n', '').replace(' ', '').replace(
276278
'\t', '') == result.replace('\n', '').replace(' ', '').replace('\t', '')
277279

tests/test_model.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,18 @@ def test_write_output_csv_with_multiple_files(self):
10531053
expected = get_test_loc('test_model/multiple_files_expected.csv')
10541054
check_csv(expected, result)
10551055

1056+
def test_write_output_csv_with_multiple_about_resource(self):
1057+
path = 'test_model/multiple_about_resource.ABOUT'
1058+
test_file = get_test_loc(path)
1059+
abouts = model.About(location=test_file, about_file_path=path)
1060+
1061+
result = get_temp_file()
1062+
model.write_output([abouts], result, format='csv')
1063+
1064+
expected = get_test_loc(
1065+
'test_model/multiple_about_resource_expected.csv')
1066+
check_csv(expected, result)
1067+
10561068
def test_write_output_json(self):
10571069
path = 'test_model/this.ABOUT'
10581070
test_file = get_test_loc(path)
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
[
22
{
3-
"about_resource": "/test_model/",
4-
"name": "AboutCode",
3+
"about_resource": "/test_model/",
4+
"name": "AboutCode",
55
"version": "0.11.0"
66
}
7-
]
7+
]
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
about_resource:
2+
- .
3+
- multiple_files_expected.csv
4+
name: multiple_about_resource
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
about_resource,name
2+
"/test_model/
3+
/test_model/multiple_files_expected.csv",multiple_about_resource

0 commit comments

Comments
 (0)