Skip to content

Commit 8f484c4

Browse files
committed
Release 2.0.2
* Handle input's encoding issues * Better error handling * Writing to and reading from Windows OS with paths > 255 chars
2 parents e24ce42 + b324135 commit 8f484c4

File tree

12 files changed

+161
-38
lines changed

12 files changed

+161
-38
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,4 @@ nosetests.xml
3131
# PyCharm project
3232
.idea/
3333

34-
/*.egg_info
34+
/*.egg-info/

.travis.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
language: python
22

33
python:
4-
- "2.6"
54
- "2.7"
65
install:
76
- source configure etc/conf

README.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,14 @@ And on Windows::
7171

7272
For instance on Linux the whole installation would be like this::
7373

74-
$ wget https://github.com/dejacode/about-code-tool/archive/v2.0.1.zip
75-
$ unzip v2.0.1.zip
76-
$ cd about-code-tool-2.0.1/
74+
$ wget https://github.com/dejacode/about-code-tool/archive/v2.0.2.zip
75+
$ unzip v2.0.2.zip
76+
$ cd about-code-tool-2.0.2/
7777
$ source configure
7878

7979
On Windows, the whole installation would be like this:
8080

81-
* Download and extract https://github.com/dejacode/about-code-tool/archive/v2.0.1.zip
81+
* Download and extract https://github.com/dejacode/about-code-tool/archive/v2.0.2.zip
8282
* open a command prompt and cd to the directory where the zip extraction directory
8383
* run configure
8484

about_code_tool/about.py

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf8 -*-
33

44
# ============================================================================
5-
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
5+
# Copyright (c) 2013-2015 nexB Inc. http://www.nexb.com/ - All rights reserved.
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
@@ -32,28 +32,31 @@
3232
import csv
3333
from datetime import datetime
3434
from email.parser import HeaderParser
35-
from os.path import basename, dirname, join, normpath, realpath
3635
import errno
3736
import httplib
3837
import logging
38+
import ntpath
3939
import optparse
4040
import os
41+
from os.path import basename, dirname, join, normpath, realpath
4142
import posixpath
4243
import socket
4344
import string
4445
import sys
4546
import urlparse
46-
import ntpath
4747

4848

49-
__version__ = '2.0.1'
49+
on_windows = 'win32' in sys.platform
50+
UNC_PREFIX = u'\\\\?\\'
51+
52+
__version__ = '2.0.2'
5053

5154
# See http://dejacode.org
5255
__about_spec_version__ = '1.0'
5356

5457

5558
__copyright__ = """
56-
Copyright (c) 2013-2014 nexB Inc. All rights reserved.
59+
Copyright (c) 2013-2015 nexB Inc. All rights reserved.
5760
5861
Licensed under the Apache License, Version 2.0 (the "License");
5962
you may not use this file except in compliance with the License.
@@ -554,7 +557,14 @@ class AboutFile(object):
554557
"""
555558
def __init__(self, location=None):
556559
self.about_resource = None
557-
self.location = location
560+
# The os.path.abspath(None) will cause error in linux system.
561+
# See https://bugs.python.org/issue22587
562+
# Note that the os.path.abspath is needed for windows when there
563+
# is long path/filename.
564+
if on_windows:
565+
self.location = os.path.abspath(location)
566+
else:
567+
self.location = location
558568

559569
self.parsed = None
560570
self.parsed_fields = None
@@ -1082,7 +1092,10 @@ def get_dje_license_name(self):
10821092
"""
10831093
Return the about object's dje_license_name.
10841094
"""
1085-
return self.parsed.get('dje_license_name', '')
1095+
try:
1096+
return self.parsed.get('dje_license_name', '')
1097+
except:
1098+
return ''
10861099

10871100
def check_invalid_chars(field_name, line):
10881101
"""
@@ -1104,6 +1117,8 @@ def check_invalid_chars(field_name, line):
11041117
warnings = Warn(IGNORED, field_name, line, msg)
11051118
return invalid_chars, warnings
11061119

1120+
def posix_unc_prefix():
1121+
return posix_path(u'\\\\?\\')
11071122

11081123
class Collector(object):
11091124
"""
@@ -1144,6 +1159,23 @@ def collect(location):
11441159
"""
11451160
# FIXME: we should not accept both a file and dir location as input
11461161
paths = []
1162+
1163+
if on_windows:
1164+
location = unicode(location)
1165+
"""
1166+
Convert a location to an absolute Window UNC path to support long paths
1167+
on Windows. Return the location unchanged if not on Windows.
1168+
See https://msdn.microsoft.com/en-us/library/aa365247.aspx
1169+
"""
1170+
if on_windows and not location.startswith(UNC_PREFIX):
1171+
location = UNC_PREFIX + os.path.abspath(location)
1172+
location = os.path.expanduser(location)
1173+
location = os.path.expandvars(location)
1174+
location = os.path.normpath(location)
1175+
location = os.path.abspath(location)
1176+
1177+
assert os.path.exists(location)
1178+
11471179
if location:
11481180
if os.path.isfile(location) and is_about_file(location):
11491181
paths.append(location)
@@ -1197,6 +1229,10 @@ def get_relative_path(self, location):
11971229
"""
11981230
user_loc = normpath(self.location)
11991231
if os.path.isdir(self.normalized_location):
1232+
# Making sure both are in posix path format before
1233+
# doing any string partition.
1234+
location = posix_path(location)
1235+
user_loc = posix_path(user_loc)
12001236
parent_name = basename(user_loc)
12011237
subpath = '/' + parent_name + location.partition(user_loc)[2]
12021238
if user_loc[-1] == '/':
@@ -1241,6 +1277,10 @@ def get_about_context(self, about_object):
12411277
if '\n' in about_object.get_dje_license_name():
12421278
msg = ('Multiple licenses is not supported. '
12431279
'Skipping License generation.')
1280+
if on_windows:
1281+
if (about_object.location.startswith(posix_unc_prefix())
1282+
or about_object.location.startswith(UNC_PREFIX)):
1283+
about_object.location = about_object.location.strip(posix_unc_prefix()).strip(UNC_PREFIX)
12441284
err = Error(GENATTRIB, 'dje_license',
12451285
about_object.location, msg)
12461286
self.genattrib_errors.append(err)
@@ -1258,6 +1298,10 @@ def get_about_context(self, about_object):
12581298
and not '\n' in about_object.get_dje_license_name():
12591299
msg = ('No license_text found. '
12601300
'Skipping License generation.')
1301+
if on_windows:
1302+
if (about_object.location.startswith(posix_unc_prefix())
1303+
or about_object.location.startswith(UNC_PREFIX)):
1304+
about_object.location = about_object.location.strip(posix_unc_prefix()).strip(UNC_PREFIX)
12611305
err = Error(GENATTRIB, 'license_text_file',
12621306
about_object.location, msg)
12631307
self.genattrib_errors.append(err)
@@ -1314,6 +1358,9 @@ def generate_attribution(self, template_path=None, limit_to=None, verification=N
13141358
break
13151359

13161360
if not component_exist:
1361+
if on_windows:
1362+
if self.location.startswith(posix_unc_prefix()):
1363+
self.location = self.location.strip(posix_unc_prefix())
13171364
loc = self.location + component
13181365
msg = ('The requested ABOUT file: %r does not exist. '
13191366
'No attribution generated for this file.' % loc)
@@ -1364,6 +1411,9 @@ def check_paths(self, paths):
13641411
for path in paths:
13651412
path = posix_path(path)
13661413
afp = join(self.location, path)
1414+
if on_windows:
1415+
if afp.startswith(posix_unc_prefix()):
1416+
afp = afp.strip(posix_unc_prefix())
13671417
msg = ('The requested ABOUT file: %(afp)r does not exist. '
13681418
'No attribution generated for this file.' % locals())
13691419
err = Error(GENATTRIB, 'about_file', path, msg)

about_code_tool/genabout.py

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf8 -*-
33

44
# ============================================================================
5-
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
5+
# Copyright (c) 2013-2015 nexB Inc. http://www.nexb.com/ - All rights reserved.
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
@@ -13,7 +13,6 @@
1313
# See the License for the specific language governing permissions and
1414
# limitations under the License.
1515
# ============================================================================
16-
1716
"""
1817
This is a tool to generate ABOUT files based on the input file.
1918
The input file should be a csv format which contains information about the
@@ -22,29 +21,29 @@
2221

2322
from __future__ import print_function
2423

24+
from collections import namedtuple
2525
import copy
2626
import csv
2727
import errno
2828
import json
2929
import logging
3030
import optparse
31+
from os import makedirs
3132
import os
33+
from os.path import exists, dirname, join, abspath, isdir, normpath, basename, expanduser
3234
import shutil
3335
import sys
3436
import urllib
3537
import urllib2
36-
37-
from collections import namedtuple
3838
from urlparse import urljoin, urlparse
39-
from os import makedirs
40-
from os.path import exists, dirname, join, abspath, isdir, normpath, basename, expanduser
4139

4240
import about
4341

44-
__version__ = '2.0.0'
42+
43+
__version__ = '2.0.2'
4544

4645
__copyright__ = """
47-
Copyright (c) 2013-2014 nexB Inc. All rights reserved.
46+
Copyright (c) 2013-2015 nexB Inc. All rights reserved.
4847
4948
Licensed under the Apache License, Version 2.0 (the "License");
5049
you may not use this file except in compliance with the License.
@@ -135,7 +134,7 @@ def get_input_list(input_file):
135134
for row in csvfile:
136135
row_dict = {}
137136
for key in row:
138-
row_dict[key.lower()] = row[key]
137+
row_dict[key.lower()] = row[key].rstrip()
139138
input_list.append(row_dict)
140139
return input_list
141140

@@ -455,11 +454,12 @@ def pre_process_and_dje_license_dict(self, input_list, api_url, api_username, ap
455454
if not lic in license_dict:
456455
detail_list = []
457456
detail = self.get_license_details_from_api(api_url, api_username, api_key, lic)
458-
license_dict[lic] = detail[0]
459-
line['dje_license_name'] = detail[0]
460457
dje_key = detail[1]
458+
line['dje_license_key'] = dje_key
459+
license_dict[dje_key] = detail[0]
460+
line['dje_license_name'] = detail[0]
461461
license_context = detail [2]
462-
line['dje_license_url'] = dje_lic_urn + lic
462+
line['dje_license_url'] = dje_lic_urn + dje_key
463463
detail_list.append(dje_key)
464464
detail_list.append(license_context)
465465
key_text_dict[detail[0]] = detail_list
@@ -515,7 +515,14 @@ def pre_generation(self, gen_location, input_list, action_num):
515515
about_file_location = join(gen_location, file_location)
516516
about_file_dir = dirname(about_file_location)
517517
if not os.path.exists(about_file_dir):
518-
makedirs(about_file_dir)
518+
# Check for invalid file path
519+
try:
520+
makedirs(about_file_dir)
521+
except:
522+
msg = 'Invalid ABOUT file path.'
523+
self.errors.append(Error(VALUE, 'about_file_path',
524+
about_file_dir, msg))
525+
continue
519526
about_file_exist = _exists(about_file_location)
520527
if about_file_exist:
521528
if action_num == ACTION_DO_NOTHING_IF_ABOUT_FILE_EXIST:
@@ -604,6 +611,11 @@ def format_output(input_list):
604611
value = about_dict_list[item].replace('\n', '\n ')
605612
if (value or item in about.MANDATORY_FIELDS) and not item\
606613
in about.ERROR_WARN_FIELDS and not item == 'about_resource':
614+
# It will cause error if value has different coding
615+
try:
616+
value = unicode(value, errors='ignore')
617+
except:
618+
pass
607619
context += item + ': ' + value + '\n'
608620

609621
component.append(about_file_location)
@@ -614,6 +626,8 @@ def format_output(input_list):
614626
@staticmethod
615627
def write_output(output):
616628
for about_file_location, context in output:
629+
if about.on_windows:
630+
about_file_location = about.UNC_PREFIX + os.path.abspath(about_file_location)
617631
if _exists(about_file_location):
618632
os.remove(about_file_location)
619633
with open(about_file_location, 'wb') as output_file:
@@ -855,6 +869,10 @@ def main(parser, options, args):
855869
sys.exit(errno.EINVAL)
856870

857871
if gen_license:
872+
# Strip the ' and " for api_url, api_username and api_key from input
873+
api_url = api_url.strip("'").strip("\"")
874+
api_username = api_username.strip("'").strip("\"")
875+
api_key = api_key.strip("'").strip("\"")
858876
dje_license_dict = gen.pre_process_and_dje_license_dict(input_list,
859877
api_url,
860878
api_username,

about_code_tool/genattrib.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# -*- coding: utf8 -*-
33

44
# ============================================================================
5-
# Copyright (c) 2014 nexB Inc. http://www.nexb.com/ - All rights reserved.
5+
# Copyright (c) 2013-2015 nexB Inc. http://www.nexb.com/ - All rights reserved.
66
# Licensed under the Apache License, Version 2.0 (the "License");
77
# you may not use this file except in compliance with the License.
88
# You may obtain a copy of the License at
@@ -43,12 +43,12 @@
4343
logger.addHandler(handler)
4444
file_logger = logging.getLogger(__name__ + '_file')
4545

46-
__version__ = '2.0.0'
46+
__version__ = '2.0.2'
4747

4848
__about_spec_version__ = '1.0.0' # See http://dejacode.org
4949

5050
__copyright__ = """
51-
Copyright (c) 2013-2014 nexB Inc. All rights reserved.
51+
Copyright (c) 2013-2015 nexB Inc. All rights reserved.
5252
5353
Licensed under the Apache License, Version 2.0 (the "License");
5454
you may not use this file except in compliance with the License.

0 commit comments

Comments
 (0)