Skip to content

Commit 64cd61d

Browse files
committed
Cosmetic. All tests passing now
1 parent 6164fa0 commit 64cd61d

File tree

9 files changed

+225
-142
lines changed

9 files changed

+225
-142
lines changed

about_code_tool/__init__.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# =============================================================================
2-
# Copyright (c) 2013 by nexB, Inc. http://www.nexb.com/ - All rights reserved.
1+
# ============================================================================
2+
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
55
# You may obtain a copy of the License at
@@ -9,4 +9,4 @@
99
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1010
# See the License for the specific language governing permissions and
1111
# limitations under the License.
12-
# =============================================================================
12+
# ============================================================================

about_code_tool/about.py

Lines changed: 142 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
#!/usr/bin/env python
22
# -*- coding: utf8 -*-
33

4+
# ============================================================================
5+
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# ============================================================================
16+
417
"""
518
AboutCode is a tool to process ABOUT files. ABOUT files are small text files
619
that document the provenance (aka. the origin and license) of software
@@ -503,6 +516,7 @@ def check_network_connection():
503516
else:
504517
return True
505518

519+
506520
has_network_connectivity = check_network_connection()
507521

508522

@@ -528,6 +542,9 @@ def __init__(self, location=None):
528542
if self.location:
529543
self.parse()
530544

545+
def __repr__(self):
546+
return repr((self.parsed, self.parsed_fields, self.validated_fields,))
547+
531548
def parse(self):
532549
"""
533550
Parse and validate a the file at self.location object in an ABOUT
@@ -1039,56 +1056,55 @@ def get_about_name(self):
10391056
"""
10401057
return self.parsed.get('name', '')
10411058

1042-
class AboutCollector(object):
1043-
"""
1044-
A collection of AboutFile instances.
10451059

1046-
Collects the About files in the given path on initialization.
1047-
Creates one AboutFile instance per file.
1048-
Summarize all the issues from each instance.
1060+
class Collector(object):
10491061
"""
1050-
def __init__(self, input_path):
1051-
if os.path.isdir(input_path) and not input_path.endswith('/'):
1052-
input_path = input_path + '/'
1053-
self.user_provided_path = input_path
1054-
self.absolute_path = os.path.abspath(input_path)
1055-
assert os.path.exists(self.absolute_path)
1062+
Collect ABOUT files.
1063+
"""
1064+
def __init__(self, location):
1065+
"""
1066+
Collect ABOUT files at location and create one AboutFile instance per
1067+
file.
1068+
"""
1069+
assert location
1070+
self.location = location
1071+
normed_loc = os.path.expanduser(location)
1072+
normed_loc = os.path.normpath(normed_loc)
1073+
normed_loc = os.path.abspath(normed_loc)
1074+
normed_loc = posix_path(normed_loc)
1075+
assert os.path.exists(normed_loc)
1076+
self.normalized_location = normed_loc
1077+
self.abouts = [AboutFile(f) for f in self.collect(normed_loc)]
10561078

10571079
self._errors = []
10581080
self._warnings = []
1059-
10601081
self.genattrib_errors = []
1061-
1062-
self.abouts = [AboutFile(f) for f
1063-
in self._collect_about_files(self.absolute_path)]
1064-
1065-
# self.create_about_objects_from_files()
1066-
# self.extract_about_data_from_objects()
1067-
10681082
self.summarize_issues()
10691083

10701084
def __iter__(self):
10711085
"""
1072-
Yield the collected about instances.
1086+
Iterate collected AboutFile.
10731087
"""
10741088
return iter(self.abouts)
10751089

10761090
@staticmethod
1077-
def _collect_about_files(location):
1091+
def collect(location):
10781092
"""
1079-
Given the location of a file or directory, return a list of
1080-
locations of valid .ABOUT files.
1093+
Return a list of locations of *.ABOUT files given the location of an
1094+
ABOUT file or a directory tree containing ABOUT files.
1095+
Locations are normalized using posix path separators.
10811096
"""
1097+
# FIXME: we should not accept both a file and dir location as input
10821098
paths = []
10831099
if location:
10841100
if os.path.isfile(location) and is_about_file(location):
1085-
paths = [location]
1101+
paths.append(location)
10861102
else:
10871103
for root, _, files in os.walk(location):
10881104
for name in files:
10891105
if is_about_file(name):
10901106
paths.append(os.path.join(root, name))
1091-
# normalize the paths to use os path seps
1107+
# normalize the paths to use posix path separators
10921108
paths = [posix_path(p)for p in paths]
10931109
return paths
10941110

@@ -1098,18 +1114,20 @@ def errors(self):
10981114
"""
10991115
Return a list of about.errors for every about instances.
11001116
"""
1117+
# FIXME: this function is not needed.
11011118
return self._errors
11021119

11031120
@property
11041121
def warnings(self):
11051122
"""
11061123
Return a list of about.warnings for every about instances.
11071124
"""
1125+
# FIXME: this function is not needed.
11081126
return self._warnings
11091127

11101128
def summarize_issues(self):
11111129
"""
1112-
Summarize and log, errors and warnings.
1130+
Summarize and log errors and warnings.
11131131
"""
11141132
for about_object in self:
11151133
relative_path = self.get_relative_path(about_object.location)
@@ -1125,7 +1143,23 @@ def summarize_issues(self):
11251143
self._warnings.extend(about_object.warnings)
11261144
logger.warning(about_object.warnings)
11271145

1128-
def get_relative_path(self, about_object_location):
1146+
def get_relative_path(self, location):
1147+
"""
1148+
Return a path for a given ABOUT file location relative to and based on
1149+
the provided collector normalized location.
1150+
"""
1151+
user_loc = self.location
1152+
if os.path.isdir(self.normalized_location):
1153+
subpath = location.partition(os.path.basename(os.path.normpath(user_loc)))[2]
1154+
if user_loc[-1] == '/':
1155+
user_loc = user_loc.rpartition('/')[0]
1156+
if user_loc[-1] == '\\':
1157+
user_loc = user_loc.rpartition('\\')[0]
1158+
return (user_loc + subpath).replace('\\', '/')
1159+
else:
1160+
return user_loc.replace('\\', '/')
1161+
1162+
def get_relative_path2(self, about_object_location):
11291163
"""
11301164
Return a relative path as provided by the user for an about_object.
11311165
@@ -1134,7 +1168,8 @@ def get_relative_path(self, about_object_location):
11341168
"hardcode" to add/append the path.
11351169
"""
11361170
# FIXME: we should use correct path manipulation, not our own cooking
1137-
user_provided_path = self.user_provided_path
1171+
# this is too complex
1172+
user_provided_path = self.location
11381173
if os.path.isdir(self.absolute_path):
11391174
subpath = about_object_location.partition(
11401175
os.path.basename(os.path.normpath(user_provided_path)))[2]
@@ -1160,95 +1195,113 @@ def write_to_csv(self, output_path):
11601195
row_data = about_object.get_row_data(relative_path)
11611196
csv_writer.writerow(row_data)
11621197

1163-
def generate_attribution(self, template_path=None, limit_to=None):
1198+
def generate_attribution(self, template_path='templates/default.html',
1199+
limit_to=None):
11641200
"""
11651201
Generate an attribution file from the current list of ABOUT objects.
11661202
The optional `limit_to` parameter allows to restrict the generated
11671203
attribution to a specific list of component names.
11681204
"""
1169-
if not limit_to:
1170-
limit_to = []
1171-
1172-
if not template_path:
1173-
template_path = 'templates/default.html'
11741205

11751206
try:
1176-
import jinja2
1207+
import jinja2 as j2
11771208
except ImportError, e:
11781209
print('The Jinja2 templating library is required to generate '
1179-
'attribution texts. You can install it with using:'
1180-
'pip install -r requirements.txt')
1210+
'attribution texts. You can install it by running:'
1211+
'configure')
11811212
return
11821213

1214+
# FIXME: the template dir should be outside the code tree
11831215
template_dir = os.path.dirname(template_path)
1184-
template_name = os.path.basename(template_path)
1185-
env = jinja2.Environment(loader=jinja2.FileSystemLoader(template_dir))
1216+
template_file_name = os.path.basename(template_path)
1217+
1218+
jinja_env = j2.Environment(loader=j2.FileSystemLoader(template_dir))
11861219

11871220
try:
1188-
template = env.get_template(template_name)
1189-
except jinja2.TemplateNotFound:
1190-
print('Template: %(template_name)s not found' % locals())
1221+
template = jinja_env.get_template(template_file_name)
1222+
except j2.TemplateNotFound, e:
1223+
print('Template: %(template_file_name)s not found' % locals())
11911224
return
11921225

1226+
limit_to = limit_to or []
1227+
limit_to = set(limit_to)
1228+
11931229
about_object_fields = []
11941230
about_content_dict = {}
1195-
not_exist_components = list(limit_to)
11961231

1197-
# FIXME: this loop and conditional is too complex.
11981232
for about_object in self:
1199-
about_relative_path = ('/' +
1200-
about_object.location.partition(
1201-
self.user_provided_path)[2])
1202-
1203-
# Check is there any components in the 'limit_to' list that
1204-
# does not exist in the code base.
1205-
if limit_to:
1206-
try:
1207-
not_exist_components.remove(about_relative_path)
1208-
except Exception as e:
1209-
continue
1210-
1211-
if not limit_to or about_relative_path in limit_to:
1212-
about_content_dict = about_object.validated_fields
1213-
# Add information in the dictionary where it does not
1214-
# present in the ABOUT file
1215-
about_content_dict['license_text'] = unicode(about_object.license_text(),
1216-
errors='replace')
1217-
about_content_dict['notice_text'] = about_object.notice_text()
1218-
1219-
# FIXME: The following is a tmp code to handle multiple
1220-
# 'license_text_file' in the input
1221-
for k in about_content_dict:
1222-
if '\n' in about_content_dict[k] and k == 'license_text_file':
1223-
about_content_dict['license_text'] = unicode(about_object.tmp_get_license_text(),
1224-
errors='replace')
1225-
1226-
# Raise error if no license_text is found
1227-
if not about_content_dict['license_text']:
1228-
msg = ('No license_text is found. '
1229-
'License generation is skipped.')
1230-
err = Error(GENATTRIB, 'name',
1231-
about_object.get_about_name(), msg)
1232-
self.genattrib_errors.append(err)
1233+
# FIXME: what is the meaning of this partition?
1234+
# PO created the var some_path to provide some clarity
1235+
# but what does the second element means?
1236+
some_path = about_object.location.partition(self.location)[2]
1237+
about_relative_path = '/' + some_path
1238+
print('some_path:', some_path)
1239+
1240+
if limit_to and about_relative_path in limit_to:
1241+
continue
1242+
1243+
about_content_dict = about_object.validated_fields
1244+
print('about_content_dict:', about_content_dict)
1245+
# Add information in the dictionary where it does not
1246+
# present in the ABOUT file
1247+
lic_text = unicode(about_object.license_text(),
1248+
errors='replace')
1249+
about_content_dict['license_text'] = lic_text
1250+
notice_text = about_object.notice_text()
1251+
about_content_dict['notice_text'] = notice_text
1252+
1253+
# FIXME: The following is a tmp code to handle multiple
1254+
# 'license_text_file' in the input
1255+
print('about_content_dict:', about_content_dict)
1256+
for k in about_content_dict:
1257+
if ('\n' in about_content_dict[k]
1258+
and k == 'license_text_file'):
1259+
lic_text = unicode(about_object.tmp_get_license_text(),
1260+
errors='replace')
1261+
about_content_dict['license_text'] = lic_text
1262+
1263+
# Raise error if no license_text is found
1264+
if 'license_text' not in about_content_dict:
1265+
msg = ('No license_text found. '
1266+
'skipping License generation.')
1267+
err = Error(GENATTRIB, 'name',
1268+
about_object.get_about_name(), msg)
1269+
self.genattrib_errors.append(err)
12331270

12341271
about_object_fields.append(about_content_dict)
12351272

1236-
if not_exist_components:
1237-
for component in not_exist_components:
1238-
afp = self.user_provided_path + component.replace('//', '/')
1239-
msg = ('about file: %s - file does not exist. '
1240-
'No attribution is generated for this component.'
1241-
% afp)
1242-
err = Error(GENATTRIB, 'about_file', component, msg)
1243-
self.genattrib_errors.append(err)
1273+
# find paths requested in the limit_to paths arg that do not point to
1274+
# a corresponding ABOUT file
1275+
for path in limit_to:
1276+
path = posix_path(path)
1277+
1278+
afp = join(self.location, path)
1279+
msg = ('The requested ABOUT file: %(afp)r does not exist. '
1280+
'No attribution generated for this file.' % locals())
1281+
err = Error(GENATTRIB, 'about_file', path, msg)
1282+
self.genattrib_errors.append(err)
12441283

12451284
# TODO: Handle the grouping and ordering later
12461285
"""# We want to display common_licenses in alphabetical order
12471286
for key in sorted(common_license_dict.keys()):
12481287
license_key.append(key)
12491288
license_text_list.append(common_license_dict[key])"""
12501289

1251-
return template.render(about_objects=about_object_fields)
1290+
rendered = template.render(about_objects=about_object_fields)
1291+
return rendered
1292+
1293+
def check_paths(self, paths):
1294+
"""
1295+
Check if each path in a list of ABOUT file paths exist in the
1296+
collected ABOUT files. Add errors if it does not.
1297+
"""
1298+
for path in paths:
1299+
path = posix_path(path)
1300+
afp = join(self.location, path)
1301+
msg = ('The requested ABOUT file: %(afp)r does not exist. '
1302+
'No attribution generated for this file.' % locals())
1303+
err = Error(GENATTRIB, 'about_file', path, msg)
1304+
self.genattrib_errors.append(err)
12521305

12531306
def get_genattrib_errors(self):
12541307
return self.genattrib_errors
@@ -1320,7 +1373,7 @@ def main(parser, options, args):
13201373

13211374
if (not os.path.exists(output_path)
13221375
or (os.path.exists(output_path) and overwrite)):
1323-
collector = AboutCollector(input_path)
1376+
collector = Collector(input_path)
13241377
collector.write_to_csv(output_path)
13251378
if collector.errors:
13261379
print('%d errors detected.' % len(collector.errors))

about_code_tool/genabout.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,19 @@
11
#!/usr/bin/env python
22
# -*- coding: utf8 -*-
33

4+
# ============================================================================
5+
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
# ============================================================================
16+
417
"""
518
This is a tool to generate ABOUT files based on the input file.
619
The input file should be a csv format which contains information about the

0 commit comments

Comments
 (0)