Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Report multiple LICENSE files #78

Closed
wants to merge 10 commits into from
83 changes: 61 additions & 22 deletions piplicenses.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,8 @@
'Name',
'Version',
'License',
'LicenseFile',
'LicenseText',
'LicenseFiles',
'LicenseTexts',
'NoticeFile',
'NoticeText',
'Author',
Expand Down Expand Up @@ -131,11 +131,14 @@ def get_packages(args):

def get_pkg_included_file(pkg, file_names):
"""
Attempt to find the package's included file on disk and return the
tuple (included_file_path, included_file_contents).
Attempt to find the package's included file(s) on disk and return the
tuple (included_file_paths, included_files_contents).

Note that packages can have more than one file that matches a given
glob (e.g. multiple LICENSE* files).
"""
included_file = LICENSE_UNKNOWN
included_text = LICENSE_UNKNOWN
included_files = []
included_texts = []
pkg_dirname = "{}-{}.dist-info".format(
pkg.project_name.replace("-", "_"), pkg.version)
patterns = []
Expand All @@ -145,30 +148,34 @@ def get_pkg_included_file(pkg, file_names):
for f in file_names]
for test_file in patterns:
if os.path.exists(test_file):
included_file = test_file
included_files.append(test_file)
with open(test_file, encoding='utf-8',
errors='backslashreplace') as included_file_handle:
included_text = included_file_handle.read()
break
return (included_file, included_text)
included_texts.append(included_file_handle.read())

if len(included_files) == 0:
included_files = [LICENSE_UNKNOWN]
included_texts = [LICENSE_UNKNOWN]

return included_files, included_texts

def get_pkg_info(pkg):
(license_file, license_text) = get_pkg_included_file(
(license_files, license_texts) = get_pkg_included_file(
pkg,
('LICENSE*', 'LICENCE*', 'COPYING*')
)
(notice_file, notice_text) = get_pkg_included_file(
(notice_files, notice_texts) = get_pkg_included_file(
pkg,
('NOTICE*',)
)
pkg_info = {
'name': pkg.project_name,
'version': pkg.version,
'namever': str(pkg),
'licensefile': license_file,
'licensetext': license_text,
'noticefile': notice_file,
'noticetext': notice_text,
'licensefiles': license_files,
'licensetexts': license_texts,
'noticefiles': notice_files,
'noticetexts': notice_texts,
}
metadata = None
if pkg.has_metadata('METADATA'):
Expand Down Expand Up @@ -312,6 +319,20 @@ def get_string(self, **kwargs):
class CSVPrettyTable(PrettyTable):
"""PrettyTable-like class exporting to CSV"""

@staticmethod
def flatten_single_entry_lists(rows):
"""Flatten a list of lists, if the inner lists are of length 1."""
new_rows = []
for row in rows:
new_columns = []
for column in row:
if isinstance(column, list) and len(column) == 1:
new_columns.append(column[0])
else:
new_columns.append(column)
new_rows.append(new_columns)
return new_rows

def get_string(self, **kwargs):

def esc_quotes(val):
Expand All @@ -328,6 +349,7 @@ def esc_quotes(val):

options = self._get_options(kwargs)
rows = self._get_rows(options)
rows = self.flatten_single_entry_lists(rows)
formatted_rows = self._format_rows(rows, options)

lines = []
Expand Down Expand Up @@ -356,7 +378,11 @@ def get_string(self, **kwargs):
output = ''
for row in rows:
for v in row:
output += '{}\n'.format(v)
if isinstance(v, list):
for item in v:
output += '{}\n'.format(item)
else:
output += '{}\n'.format(v)
output += '\n'

return output
Expand Down Expand Up @@ -436,9 +462,9 @@ def get_output_fields(args):

if args.with_license_file:
if not args.no_license_path:
output_fields.append('LicenseFile')
output_fields.append('LicenseFiles')

output_fields.append('LicenseText')
output_fields.append('LicenseTexts')

if args.with_notice_file:
output_fields.append('NoticeText')
Expand Down Expand Up @@ -481,7 +507,6 @@ def create_output_string(args):

def create_warn_string(args):
warn_messages = []
warn = partial(output_colored, '33')

if args.with_license_file and not args.format == 'json':
message = warn(('Due to the length of these fields, this option is '
Expand All @@ -506,6 +531,11 @@ def parse_args(self, args=None, namespace=None):
self._compatible_format_args(args)
self._check_code_page(args.filter_code_page)

if args.with_license_file:
print(warn('The option "--with-license-file" is deprecated. '
'Please migrate to "--with-license-files".'))
args.with_license_files = True

return args

@staticmethod
Expand Down Expand Up @@ -603,11 +633,17 @@ def create_parser():
action='store_true',
default=False,
help='dump with short package description')
parser.add_argument('-l', '--with-license-file',
parser.add_argument('-l', '--with-license-files',
action='store_true',
default=False,
help='dump with location of license file and '
help='dump with location of license files and '
'contents, most useful with JSON output')
parser.add_argument('--with-license-file',
action='store_true',
default=False,
help='[Deprecated] dump with location of license '
'files and contents, most useful with JSON '
'output')
parser.add_argument('--no-license-path',
action='store_true',
default=False,
Expand Down Expand Up @@ -666,6 +702,9 @@ def output_colored(code, text, is_bold=False):
return '\033[%sm%s\033[0m' % (code, text)


warn = partial(output_colored, '33')


def save_if_needs(output_file, output_string):
"""
Save to path given by args
Expand Down