From 0d5afb32a9c66452b391380f931bd87af5ce6226 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 19:39:50 -0400 Subject: [PATCH 1/9] Collect all LICENSE and NOTICE files when parsing package info --- piplicenses.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index 29ab8f6..f6b968f 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -134,8 +134,8 @@ 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). """ - included_file = LICENSE_UNKNOWN - included_text = LICENSE_UNKNOWN + included_files = [] + included_texts = [] pkg_dirname = "{}-{}.dist-info".format( pkg.project_name.replace("-", "_"), pkg.version) patterns = [] @@ -145,19 +145,23 @@ 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*',) ) @@ -165,10 +169,10 @@ def get_pkg_info(pkg): 'name': pkg.project_name, 'version': pkg.version, 'namever': str(pkg), - 'licensefile': license_file, - 'licensetext': license_text, - 'noticefile': notice_file, - 'noticetext': notice_text, + 'licensefile': license_files, + 'licensetext': license_texts, + 'noticefile': notice_files, + 'noticetext': notice_texts, } metadata = None if pkg.has_metadata('METADATA'): From 84c4e1c3471f40a4f9181da2fbb5fdda9c017908 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 19:46:51 -0400 Subject: [PATCH 2/9] Add multiple license file support for PlainVertical format --- piplicenses.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index f6b968f..9514bcd 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -131,8 +131,11 @@ 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_files = [] included_texts = [] @@ -360,7 +363,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 From c5dff13737ee1e8d90a8eb1ce9d66a264f7837e7 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 20:06:45 -0400 Subject: [PATCH 3/9] Update field names --- piplicenses.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index 9514bcd..551a873 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -73,8 +73,8 @@ 'Name', 'Version', 'License', - 'LicenseFile', - 'LicenseText', + 'LicenseFiles', + 'LicenseTexts', 'NoticeFile', 'NoticeText', 'Author', @@ -172,10 +172,10 @@ def get_pkg_info(pkg): 'name': pkg.project_name, 'version': pkg.version, 'namever': str(pkg), - 'licensefile': license_files, - 'licensetext': license_texts, - 'noticefile': notice_files, - 'noticetext': notice_texts, + 'licensefiles': license_files, + 'licensetexts': license_texts, + 'noticefiles': notice_files, + 'noticetexts': notice_texts, } metadata = None if pkg.has_metadata('METADATA'): @@ -447,9 +447,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') From f05d5a46ac91d547a7f61d9bb785e289e1526f3e Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 19:39:50 -0400 Subject: [PATCH 4/9] Collect all LICENSE and NOTICE files when parsing package info --- piplicenses.py | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index 9df973d..297b857 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -134,8 +134,8 @@ 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). """ - included_file = LICENSE_UNKNOWN - included_text = LICENSE_UNKNOWN + included_files = [] + included_texts = [] pkg_dirname = "{}-{}.dist-info".format( pkg.project_name.replace("-", "_"), pkg.version) patterns = [] @@ -145,19 +145,23 @@ 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*',) ) @@ -165,10 +169,10 @@ def get_pkg_info(pkg): 'name': pkg.project_name, 'version': pkg.version, 'namever': str(pkg), - 'licensefile': license_file, - 'licensetext': license_text, - 'noticefile': notice_file, - 'noticetext': notice_text, + 'licensefile': license_files, + 'licensetext': license_texts, + 'noticefile': notice_files, + 'noticetext': notice_texts, } metadata = None if pkg.has_metadata('METADATA'): From 1c17372a8ac73fd5c9392c6104dedaa52d6d9357 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 19:46:51 -0400 Subject: [PATCH 5/9] Add multiple license file support for PlainVertical format --- piplicenses.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index 297b857..b9ac264 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -131,8 +131,11 @@ 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_files = [] included_texts = [] @@ -360,7 +363,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 From 734c39307a7c1c6f9f7ef18117fd1642076884ed Mon Sep 17 00:00:00 2001 From: johnthagen Date: Sun, 30 Aug 2020 20:06:45 -0400 Subject: [PATCH 6/9] Update field names --- piplicenses.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index b9ac264..1183630 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -73,8 +73,8 @@ 'Name', 'Version', 'License', - 'LicenseFile', - 'LicenseText', + 'LicenseFiles', + 'LicenseTexts', 'NoticeFile', 'NoticeText', 'Author', @@ -172,10 +172,10 @@ def get_pkg_info(pkg): 'name': pkg.project_name, 'version': pkg.version, 'namever': str(pkg), - 'licensefile': license_files, - 'licensetext': license_texts, - 'noticefile': notice_files, - 'noticetext': notice_texts, + 'licensefiles': license_files, + 'licensetexts': license_texts, + 'noticefiles': notice_files, + 'noticetexts': notice_texts, } metadata = None if pkg.has_metadata('METADATA'): @@ -447,9 +447,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') From d304b471efb8ff36e54b5b14111cd200ce1d8c93 Mon Sep 17 00:00:00 2001 From: johnthagen Date: Mon, 31 Aug 2020 19:19:48 -0400 Subject: [PATCH 7/9] Add deprecation warning for --with-license-file and add new, better named --with-license-files argument --- piplicenses.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index 1183630..7dc5448 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -492,7 +492,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 ' @@ -517,6 +516,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 @@ -614,11 +618,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, @@ -677,6 +687,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 From ebabf11d479b97d17ad654ed500ded7d275625cc Mon Sep 17 00:00:00 2001 From: johnthagen Date: Mon, 31 Aug 2020 19:39:14 -0400 Subject: [PATCH 8/9] Flatten CSV entries if there are only 1 license file returned --- piplicenses.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/piplicenses.py b/piplicenses.py index 7dc5448..e8e3e50 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -254,6 +254,20 @@ def create_summary_table(args): return table +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 + + class JsonPrettyTable(PrettyTable): """PrettyTable-like class exporting to JSON""" @@ -335,6 +349,7 @@ def esc_quotes(val): options = self._get_options(kwargs) rows = self._get_rows(options) + rows = flatten_single_entry_lists(rows) formatted_rows = self._format_rows(rows, options) lines = [] From c147c72ba2028af43df12e9eb5dcf4ec94d4c30e Mon Sep 17 00:00:00 2001 From: johnthagen Date: Tue, 1 Sep 2020 06:56:09 -0400 Subject: [PATCH 9/9] Move flatten_single_entry_lists into CSVPrettyTable static method --- piplicenses.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/piplicenses.py b/piplicenses.py index e8e3e50..5d5d871 100644 --- a/piplicenses.py +++ b/piplicenses.py @@ -254,20 +254,6 @@ def create_summary_table(args): return table -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 - - class JsonPrettyTable(PrettyTable): """PrettyTable-like class exporting to JSON""" @@ -333,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): @@ -349,7 +349,7 @@ def esc_quotes(val): options = self._get_options(kwargs) rows = self._get_rows(options) - rows = flatten_single_entry_lists(rows) + rows = self.flatten_single_entry_lists(rows) formatted_rows = self._format_rows(rows, options) lines = []