Skip to content

Commit 265f05d

Browse files
committed
Improve error string handling. Make test pass on Py2 #280
Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent acb88ea commit 265f05d

File tree

5 files changed

+69
-26
lines changed

5 files changed

+69
-26
lines changed

src/attributecode/__init__.py

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,45 @@
4040
limitations under the License.
4141
"""
4242

43-
Error = namedtuple('Error', ['severity', 'message'])
43+
class Error(namedtuple('Error', ['severity', 'message'])):
44+
"""
45+
An Error data with a severity and message.
46+
"""
47+
def __new__(self, severity, message):
48+
if message:
49+
if isinstance(message, basestring):
50+
message = clean_string(message)
51+
else:
52+
message = clean_string(repr(message))
4453

45-
def error_repr(self):
46-
sev = severities[self.severity]
47-
msg = self.message
48-
return 'Error(%(sev)s, %(msg)r)' % locals()
54+
return super(Error, self).__new__(
55+
Error, severity, message)
4956

50-
Error.__repr__ = error_repr
57+
def __repr__(self, *args, **kwargs):
58+
sev = severities[self.severity]
59+
msg = clean_string(repr(self.message))
60+
return 'Error(%(sev)s, %(msg)s)' % locals()
5161

5262

63+
def clean_string(s):
64+
"""
65+
Return a cleaned string for `s`, stripping eventual "u" prefixes
66+
from unicode representations.
67+
"""
68+
if not s:
69+
return s
70+
if s.startswith(('u"', "u'")):
71+
msg = s.lstrip('u')
72+
msg = s.replace('[u"', '["')
73+
msg = s.replace("[u'", "['")
74+
msg = s.replace("(u'", "('")
75+
msg = s.replace("(u'", "('")
76+
msg = s.replace("{u'", "{'")
77+
msg = s.replace("{u'", "{'")
78+
msg = s.replace(" u'", " '")
79+
msg = s.replace(" u'", " '")
80+
return msg
81+
5382
# modeled after the logging levels
5483
CRITICAL = 50
5584
ERROR = 40

src/attributecode/attrib.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ def generate_and_save(abouts, output_location, mapping, template_loc=None,
166166
about_list = attributecode.util.get_about_file_path(mapping, inventory_location)
167167
except Exception:
168168
# 'about_file_path' key/column doesn't exist
169-
msg = (u"The required key: 'about_file_path' does not exist. Generation halted.")
169+
msg = u"The required key: 'about_file_path' does not exist. Generation halted."
170170
errors.append(Error(ERROR, msg))
171171
return errors
172172
else:
173-
msg = (u'Only .csv and .json are supported for the "INVENTORY_LOCATOIN". Generation halted.')
173+
msg = u'Only .csv and .json are supported for the "INVENTORY_LOCATOIN". Generation halted.'
174174
errors.append(Error(ERROR, msg))
175175
return errors
176176

@@ -191,12 +191,12 @@ def generate_and_save(abouts, output_location, mapping, template_loc=None,
191191

192192
if not_match_path:
193193
if len(not_match_path) == len(about_files_list):
194-
msg = ("None of the paths in the provided 'inventory_location' match with the 'LOCATION'.")
194+
msg = "None of the paths in the provided 'inventory_location' match with the 'LOCATION'."
195195
errors.append(Error(ERROR, msg))
196196
return errors
197197
else:
198198
for path in not_match_path:
199-
msg = ('Path: ' + path + ' cannot be found.')
199+
msg = 'Path: ' + path + ' cannot be found.'
200200
errors.append(Error(ERROR, msg))
201201

202202
for about in abouts:

src/attributecode/gen.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,12 @@
2222
from collections import OrderedDict
2323
import logging
2424
import posixpath
25+
import sys
2526

26-
import unicodecsv
27+
if sys.version_info >= (3, 0):
28+
from unicodecsv.py3 import UnicodeReader
29+
else:
30+
from unicodecsv.py2 import UnicodeReader
2731

2832
from attributecode import ERROR
2933
from attributecode import CRITICAL
@@ -51,7 +55,7 @@ def check_duplicated_columns(location):
5155
"""
5256
location = add_unc(location)
5357
with codecs.open(location, 'rb', encoding='utf-8', errors='ignore') as csvfile:
54-
reader = unicodecsv.UnicodeReader(csvfile)
58+
reader = UnicodeReader(csvfile)
5559
columns = reader.next()
5660
columns = [col for col in columns]
5761

@@ -120,7 +124,7 @@ def load_inventory(mapping, location, base_dir, license_notice_text_location=Non
120124
errors.extend(dup_about_paths_err)
121125
return errors, abouts
122126
except:
123-
msg = ("The essential field 'about_file_path' is not found.")
127+
msg = "The essential field 'about_file_path' is not found."
124128
errors.append(Error(CRITICAL, msg))
125129
return errors, abouts
126130

src/attributecode/model.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -181,9 +181,10 @@ def serialize(self):
181181
value = u' '.join(value)
182182
else:
183183
value = u''.join(value)
184-
serialized = u'%(name)s: %(value)s' % locals()
185-
else:
186-
serialized = u'%(name)s:' % locals()
184+
185+
serialized = u'%(name)s:' % locals()
186+
if value:
187+
serialized += ' ' + '%(value)s' % locals()
187188
return serialized
188189

189190
def serialized_value(self):
@@ -600,7 +601,7 @@ def has_content(self):
600601

601602
def _serialized_value(self):
602603
# default normalized values for serialization
603-
if self.value is True:
604+
if self.value:
604605
return u'yes'
605606
elif self.value is False:
606607
return u'no'
@@ -921,7 +922,8 @@ def hydrate(self, fields):
921922
errors.append(Error(INFO, msg % locals()))
922923
return errors
923924

924-
def process(self, fields, about_file_path, running_inventory=False, base_dir=None, license_notice_text_location=None):
925+
def process(self, fields, about_file_path, running_inventory=False,
926+
base_dir=None, license_notice_text_location=None):
925927
"""
926928
Hydrate and validate a sequence of field name/value tuples from an
927929
ABOUT file. Return a list of errors.
@@ -935,15 +937,19 @@ def process(self, fields, about_file_path, running_inventory=False, base_dir=Non
935937

936938
# We want to copy the license_files before the validation
937939
if license_notice_text_location:
938-
copy_license_notice_files(fields, base_dir, license_notice_text_location, afp)
940+
copy_license_notice_files(
941+
fields, base_dir, license_notice_text_location, afp)
939942

940943
# we validate all fields, not only these hydrated
941944
all_fields = self.all_fields()
942-
validation_errors = validate_fields(all_fields, about_file_path, running_inventory, self.base_dir, self.license_notice_text_location)
945+
validation_errors = validate_fields(
946+
all_fields, about_file_path, running_inventory,
947+
self.base_dir, self.license_notice_text_location)
943948
errors.extend(validation_errors)
944949

945-
# do not forget to resolve about resource paths
946-
# The 'about_resource' field is now a ListField and those do not need to resolve
950+
# do not forget to resolve about resource paths The
951+
# 'about_resource' field is now a ListField and those do not
952+
# need to resolve
947953
# self.about_resource.resolve(self.about_file_path)
948954
return errors
949955

tests/test_util.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -212,13 +212,17 @@ def test_check_file_names_with_no_invalid_char_return_no_error(self):
212212
assert expected == result
213213

214214
def test_check_file_names_with_invalid_chars_return_errors(self):
215-
paths = ['locations/file',
216-
'locations/file with space',
217-
'locations/dir1/dir2/file1',
218-
'locations/dir2/file1']
215+
paths = [
216+
'locations/file',
217+
'locations/file with space',
218+
'locations/dir1/dir2/file1',
219+
'locations/dir2/file1'
220+
]
219221

220222
expected = [Error(CRITICAL, "Invalid characters ' ' in file name at: 'locations/file with space'")]
221223
result = util.check_file_names(paths)
224+
225+
assert expected[0].message == result[0].message
222226
assert expected == result
223227

224228
def test_get_about_locations(self):

0 commit comments

Comments
 (0)