From 9fe659d6259d2efa5b63ccfb28aaa571ee0356fe Mon Sep 17 00:00:00 2001 From: ReinerBRO <593493640@qq.com> Date: Thu, 26 Mar 2026 14:14:11 +0800 Subject: [PATCH] Handle pip show files sections --- jc/parsers/pip_show.py | 41 ++++++++++++++++++++++----------- tests/test_pip_show.py | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 13 deletions(-) diff --git a/jc/parsers/pip_show.py b/jc/parsers/pip_show.py index 4ce9a7a7..7c372f60 100644 --- a/jc/parsers/pip_show.py +++ b/jc/parsers/pip_show.py @@ -26,7 +26,8 @@ Schema: "license": string, "location": string, "requires": string, - "required_by": string + "required_by": string, + "files": list } ] @@ -60,7 +61,7 @@ Examples: } ] """ -from typing import List, Dict, Optional +from typing import List, Dict import jc.utils @@ -120,6 +121,22 @@ def parse( last_key: str = '' last_key_data: List = [] + def flush_last_key_data() -> None: + """Append buffered continuation lines to the previous field.""" + nonlocal last_key_data + + if not last_key_data: + return + + if last_key == 'files': + package[last_key].extend(last_key_data) + else: + if not isinstance(package[last_key], str): + package[last_key] = '' + package[last_key] = package[last_key] + '\n' + '\n'.join(last_key_data) + + last_key_data = [] + # Clear any blank lines cleandata = list(filter(None, data.splitlines())) @@ -127,8 +144,7 @@ def parse( for row in cleandata: if row.startswith('---'): - if last_key_data: - package[last_key] = package[last_key] + '\n' + '\n'.join(last_key_data) + flush_last_key_data() raw_output.append(package) package = {} @@ -137,17 +153,17 @@ def parse( continue if not row.startswith(' '): - item_key = row.split(': ', maxsplit=1)[0].lower().replace('-', '_') - item_value: Optional[str] = row.split(': ', maxsplit=1)[1] + item_key, item_value = row.split(':', maxsplit=1) + item_key = item_key.lower().replace('-', '_') + item_value = item_value.lstrip() - if item_value == '': + if item_key == 'files': + item_value = [] + elif item_value == '': item_value = None if last_key_data and last_key != item_key: - if not isinstance(package[last_key], str): - package[last_key] = '' - package[last_key] = package[last_key] + '\n' + '\n'.join(last_key_data) - last_key_data = [] + flush_last_key_data() package[item_key] = item_value last_key = item_key @@ -158,8 +174,7 @@ def parse( continue if package: - if last_key_data: - package[last_key] = package[last_key] + '\n' + '\n'.join(last_key_data) + flush_last_key_data() raw_output.append(package) diff --git a/tests/test_pip_show.py b/tests/test_pip_show.py index f5607f2b..67c4fd01 100644 --- a/tests/test_pip_show.py +++ b/tests/test_pip_show.py @@ -89,6 +89,58 @@ class MyTests(unittest.TestCase): """ self.assertEqual(jc.parsers.pip_show.parse(self.generic_pip_show_multiline_license_first_blank, quiet=True), self.generic_pip_show_multiline_license_first_blank_json) + def test_pip_show_files_section(self): + """ + Test 'pip show -f' output with a files section + """ + data = """\ +Name: jc +Version: 1.25.4 +Summary: Converts the output of popular command-line tools and file-types to JSON. +Home-page: https://github.com/kellyjonbrazil/jc +Author: Kelly Brazil +Author-email: kelly@gmail.com +License: MIT +Location: /home/pi/.local/lib/python3.11/site-packages +Requires: Pygments, ruamel.yaml, xmltodict +Required-by: pypiwifi +Files: + ../../../bin/jc + jc-1.25.4.dist-info/RECORD +""" + expected = [{ + 'name': 'jc', + 'version': '1.25.4', + 'summary': 'Converts the output of popular command-line tools and file-types to JSON.', + 'home_page': 'https://github.com/kellyjonbrazil/jc', + 'author': 'Kelly Brazil', + 'author_email': 'kelly@gmail.com', + 'license': 'MIT', + 'location': '/home/pi/.local/lib/python3.11/site-packages', + 'requires': 'Pygments, ruamel.yaml, xmltodict', + 'required_by': 'pypiwifi', + 'files': ['../../../bin/jc', 'jc-1.25.4.dist-info/RECORD'] + }] + self.assertEqual(jc.parsers.pip_show.parse(data, quiet=True), expected) + + def test_pip_show_files_section_with_following_field(self): + """ + Test 'pip show -f' output when the files section is followed by a new field + """ + data = """\ +Name: jc +Files: + ../../../bin/jc + jc-1.25.4.dist-info/RECORD +Foo: bar +""" + expected = [{ + 'name': 'jc', + 'files': ['../../../bin/jc', 'jc-1.25.4.dist-info/RECORD'], + 'foo': 'bar' + }] + self.assertEqual(jc.parsers.pip_show.parse(data, quiet=True), expected) + if __name__ == '__main__': unittest.main()