diff --git a/packagedb/tests/test_purlcli.py b/packagedb/tests/test_purlcli.py index f8f04845..3781e71e 100644 --- a/packagedb/tests/test_purlcli.py +++ b/packagedb/tests/test_purlcli.py @@ -1,29 +1,38 @@ import json import os -from click.testing import CliRunner from commoncode.testcase import FileBasedTesting from django.test import TestCase -from fetchcode.package_versions import PackageVersion, router, versions import purlcli from packagedb.models import Package -class TestPURLCLI(FileBasedTesting): +class TestPURLCLI_validate(FileBasedTesting): test_data_dir = os.path.join(os.path.dirname(__file__), "data") - # QUESTION: These four validate tests call validate_purls(), which queries the validate endpoint. Is that what we want here? I think we might be able to mock the API if we pass the API to the validate_purls() function instead of defining the API inside the function as we do now. - def test_validate_purl(self): test_purls = [ + "pkg:pypi/fetchcode@0.2.0", + "pkg:pypi/fetchcode@10.2.0", "pkg:nginx/nginx@0.8.9?os=windows", "pkg:maven/com.fasterxml.jackson.core/jackson-databind@2.14.0-rc1", ] - validated_purls = purlcli.validate_purls(test_purls) expected_results = [ + { + "valid": True, + "exists": True, + "message": "The provided Package URL is valid, and the package exists in the upstream repo.", + "purl": "pkg:pypi/fetchcode@0.2.0", + }, + { + "valid": True, + "exists": False, + "message": "The provided PackageURL is valid, but does not exist in the upstream repo.", + "purl": "pkg:pypi/fetchcode@10.2.0", + }, { "valid": True, "exists": None, @@ -42,7 +51,6 @@ def test_validate_purl(self): def test_validate_purl_empty(self): test_purls = [] - validated_purls = purlcli.validate_purls(test_purls) expected_results = [] @@ -53,7 +61,6 @@ def test_validate_purl_invalid(self): test_purls = [ "foo", ] - validated_purls = purlcli.validate_purls(test_purls) expected_results = [ @@ -73,7 +80,6 @@ def test_validate_purl_strip(self): " pkg:nginx/nginx@0.8.9?os=windows", "pkg:nginx/nginx@0.8.9?os=windows ", ] - validated_purls = purlcli.validate_purls(test_purls) expected_results = [ @@ -99,31 +105,54 @@ def test_validate_purl_strip(self): self.assertEqual(validated_purls, expected_results) - # def test_versions(self): - # purls = ["pkg:pypi/fetchcode"] - # abc = purlcli.list_versions(purls) - # print(f"\nabc = {abc}") - # for purl_list in abc: - # for p in purl_list: - # # print(PackageVersion.to_dict(p)) - # p_dict = PackageVersion.to_dict(p) - # print(f"\np_dict = {p_dict}") - # print(f"\ntype(p_dict) = {type(p_dict)}") +class TestPURLCLI_versions(FileBasedTesting): + # TODO: can we test the terminal warnings, e.g., `There was an error with your 'zzzzz' query -- the Package URL you provided is not valid.`? + def test_versions(self): + purls1 = ["pkg:pypi/fetchcode"] + purls2 = ["pkg:pypi/zzzzz"] + purls3 = ["zzzzz"] + purl_versions1 = purlcli.list_versions(purls1) + purl_versions2 = purlcli.list_versions(purls2) + purl_versions3 = purlcli.list_versions(purls3) + + expected_results1 = [ + { + "purl": "pkg:pypi/fetchcode", + "versions": [ + { + "purl": "pkg:pypi/fetchcode@0.1.0", + "version": "0.1.0", + "release_date": "2021-08-25T15:15:15.265015+00:00", + }, + { + "purl": "pkg:pypi/fetchcode@0.2.0", + "version": "0.2.0", + "release_date": "2022-09-14T16:36:02.242182+00:00", + }, + { + "purl": "pkg:pypi/fetchcode@0.3.0", + "version": "0.3.0", + "release_date": "2023-12-18T20:49:45.840364+00:00", + }, + ], + }, + ] + expected_results2 = [] + expected_results3 = [] - # json_p_dict = json.dumps(p_dict) - # print(f"\njson_p_dict = {json_p_dict}") - # print(f"\ntype(json_p_dict) = {type(json_p_dict)}") + self.assertEqual(purl_versions1, expected_results1) + self.assertEqual(purl_versions2, expected_results2) + self.assertEqual(purl_versions3, expected_results3) -# 2024-01-08 Monday 17:55:15. Based on test_api.py's class PurlValidateApiTestCase(TestCase). -class TestPURLCLI_API(TestCase): +class TestPURLAPI_validate(TestCase): def setUp(self): self.package_data = { "type": "npm", "namespace": "", - "name": "foobar", - "version": "1,1.0", + "name": "nosuchpackage", + "version": "1.1.0", "qualifiers": "", "subpath": "", "download_url": "", @@ -135,29 +164,14 @@ def setUp(self): self.package = Package.objects.create(**self.package_data) self.package.refresh_from_db() - def test_api_purl_validation(self): + def test_api_validate_purl(self): data1 = { - # "purl": "pkg:npm/foobar@1.1.0", "purl": "pkg:pypi/packagedb@2.0.0", "check_existence": True, } response1 = self.client.get(f"/api/validate/", data=data1) - print(f"\nresponse1 = {response1}") - print(f"\nresponse1.data = {response1.data}") - print(f"") - - data2 = { - # "purl": "pkg:npm/?foobar@1.1.0", - "purl": "pkg:pypi/?packagedb@2.0.0", - "check_existence": True, - } - response2 = self.client.get(f"/api/validate/", data=data2) - - print(f"\nresponse2 = {response2}") - print(f"\nresponse2.data = {response2.data}") - print(f"") - + self.assertEqual(response1.status_code, 200) self.assertEqual(True, response1.data["valid"]) self.assertEqual(True, response1.data["exists"]) self.assertEqual( @@ -165,46 +179,71 @@ def test_api_purl_validation(self): response1.data["message"], ) + data2 = { + "purl": "pkg:pypi/?packagedb@2.0.0", + "check_existence": True, + } + response2 = self.client.get(f"/api/validate/", data=data2) + + self.assertEqual(response2.status_code, 200) self.assertEqual(False, response2.data["valid"]) + self.assertEqual(None, response2.data["exists"]) self.assertEqual( "The provided PackageURL is not valid.", response2.data["message"] ) - # ZZZ: 2024-01-08 Monday 18:54:51. Some exploring: - data3 = { - # "purl": "pkg:npm/ogdendunes", - # "purl": "pkg:pypi/ogdendunes", "purl": "pkg:pypi/zzzzz@2.0.0", "check_existence": True, } response3 = self.client.get(f"/api/validate/", data=data3) - print(f"\nresponse3 = {response3}") - print(f"\nresponse3.data = {response3.data}") - print(f"") - + self.assertEqual(response3.status_code, 200) self.assertEqual(True, response3.data["valid"]) self.assertEqual(False, response3.data["exists"]) self.assertEqual( - "The provided PackageURL is valid but does not exist in the upstream repo.", + "The provided PackageURL is valid, but does not exist in the upstream repo.", response3.data["message"], ) data4 = { - # "purl": "pkg:nginx/nginx@0.8.9?os=windows", "purl": "pkg:nginx/nginx@0.8.9", "check_existence": True, } response4 = self.client.get(f"/api/validate/", data=data4) - print(f"\nresponse4 = {response4}") - print(f"\nresponse4.data = {response4.data}") - print(f"") - + self.assertEqual(response4.status_code, 200) self.assertEqual(True, response4.data["valid"]) - self.assertEqual(False, response4.data["exists"]) + self.assertEqual(None, response4.data["exists"]) self.assertEqual( - "The provided PackageURL is valid but does not exist in the upstream repo.", + "The provided PackageURL is valid, but `check_existence` is not supported for this package type.", response4.data["message"], ) + + data5 = { + "purl": "pkg:npm/nosuchpackage@1.1.0", + "check_existence": True, + } + response5 = self.client.get(f"/api/validate/", data=data5) + + self.assertEqual(response5.status_code, 200) + self.assertEqual(True, response5.data["valid"]) + self.assertEqual(True, response5.data["exists"]) + self.assertEqual( + "The provided Package URL is valid, and the package exists in the upstream repo.", + response5.data["message"], + ) + + data6 = { + "purl": "pkg:npm/nosuchpackage@1.1.1", + "check_existence": True, + } + response6 = self.client.get(f"/api/validate/", data=data6) + + self.assertEqual(response6.status_code, 200) + self.assertEqual(True, response6.data["valid"]) + self.assertEqual(False, response6.data["exists"]) + self.assertEqual( + "The provided PackageURL is valid, but does not exist in the upstream repo.", + response6.data["message"], + ) diff --git a/purlcli.py b/purlcli.py index 407cf1b1..bac7a41a 100644 --- a/purlcli.py +++ b/purlcli.py @@ -1,7 +1,3 @@ -""" -2024-01-09 Tuesday 11:59:11. This began the day as a cleaned-up version of purlcli-03.py. -""" - import json import click @@ -41,14 +37,16 @@ def validate(purls, output, file): """ Check the syntax of one or more PURLs. """ - if (purls and file) or not (purls or file): + if purls and file: raise click.UsageError("Use either purls or file but not both.") + if not (purls or file): + raise click.UsageError("Use either purls or file.") + if file: purls = file.read().splitlines(False) validated_purls = validate_purls(purls) - json.dump(validated_purls, output, indent=4) @@ -80,7 +78,7 @@ def validate_purls(purls): type=click.File(mode="w", encoding="utf-8"), required=True, default="-", - help="Write validation output as JSON to FILE.", + help="Write versions output as JSON to FILE.", ) @click.option( "--file", @@ -90,10 +88,8 @@ def validate_purls(purls): ) def get_versions(purls, output, file): """ - Check the syntax of one or more PURLs. + Given one or more PURLs, return a list of all known versions for each PURL. """ - # if (purls and file) or not (purls or file): - # raise click.UsageError("Use either purls or file but not both.") if purls and file: raise click.UsageError("Use either purls or file but not both.") @@ -104,77 +100,34 @@ def get_versions(purls, output, file): purls = file.read().splitlines(False) purl_versions = list_versions(purls) - - purl_versions - # print(f"\nlen(purl_versions) = {len(purl_versions)}") + json.dump(purl_versions, output, indent=4) def list_versions(purls): - # print(f"\nlen(purls) = {len(purls)}") - # print(f"\ntype(purls) = {type(purls)}") - # print(f"\npurls = {purls}") - purl_versions = [] + packageversion_objects = [] - list_of_dict_outputs = [] - # and this will hold the dict_outputs converted with json.dumps(): + purl_versions = [] list_of_dict_output_json_dumps = [] for purl in purls: dict_output = {} - # print(f"purl = {purl}") dict_output["purl"] = purl dict_output["versions"] = [] - # YO: Do we want to check for version data and if found remove it? Or allow? + purl = purl.strip() if not purl: continue - # YO: try/except -- 2024-01-07 Sunday 21:14:37. I tried this but the error thrown by fetchcode does not trigger the try/except. - # https://github.com/nexB/fetchcode/blob/d0a3fa9bb56dc3a77f7d3d7bd5b8d0e40c7a8612/src/fetchcode/package_versions.py#L512-L524 - # try: - # some_variable = list(router.process(purl)) - - # 2024-01-07 Sunday 21:58:44. TEST to detect fetchcode error. - # result = os.system("python other_script.py") - # if 0 == result: - # YO: 2024-01-07 Sunday 22:25:38. This DOES catch an error -- empty list means that, for some unidentified reason, there is no responsive data. - # results = list(router.process(purl)) - # if results != []: - # print(" Command executed successfully") - # print(f"results = {results}") - # else: - # print(" Command didn't execute successfully") - # print(f"results = {results}") - - # 2024-01-08 Monday 15:28:11. Can this alert us to a problem w/o that pseudo error message? - # test_variable01 = list(router.process(purl)) - # print(f"\ntest_variable01 = {test_variable01}") - - # YO: 2024-01-08 Monday 15:25:28. Each of these throws a pseudo error for pkg:pypi/ogdendunes but not for pkg:pypi/foobar -- Error while fetching 'https://pypi.org/pypi/ogdendunes/json': 404 -- but each produces an empty []. - # results = list(versions(purl)) - # results = list(versions(purl)) - # results = list(router.process(purl)) - - # test01 = versions(purl) - # test02 = router.process(purl) - # print(f"results = {results}") - # print(f"test01 = {test01}") - # print(f"test02 = {test02}") - # ZAP: If we have multiple inputs and some are valid, I assume we DO want to return data for those. - # if results == []: - # print( - # f"\nThere was an error with your '{purl}' query. Make sure that '{purl}' actually exists in the relevant repository." - # ) - # continue - - # ZAP: 2024-01-08 Monday 17:07:34. Rather than test results as above, we'll use 'validate'. - api_query = "https://public.purldb.io/api/validate/" purl = purl.strip() request_body = {"purl": purl, "check_existence": True} response = requests.get(api_query, params=request_body) results = response.json() - # print(f"\n\nresults = {results}") + if results["valid"] == False: + print( + f"\nThere was an error with your '{purl}' query -- the Package URL you provided is not valid." + ) + continue if results["exists"] != True: print( @@ -182,136 +135,27 @@ def list_versions(purls): ) continue - # This works: this is a list of PackageVersion objects - # print(f"\n\n* ABOUT TO PROCESS A PURL") - results = list(router.process(purl)) - # print(f"* JUST PROCESSED A PURL") - - # print(f"\n\nrouter.process(purl) = {router.process(purl)}") - - # print(f"\n\n* ABOUT TO PROCESS A PURL") - # print(f"\nlist(router.process(purl)) = {list(router.process(purl))}") - # print(f"* JUST PROCESSED A PURL") - - # versions(purl) is a generator object - # print(f"\nversions(purl) = {versions(purl)}") - - # Another test -- this is a list of PackageVersion objects - # print(f"\n\n* ABOUT TO PROCESS A PURL") - results_versions = list(versions(purl)) - # print(f"* JUST PROCESSED A PURL") - # print(f"\nresults_versions = {results_versions}") - - purl_versions.append(results) - - # Test: list of strings - # print(f"\n\n* ABOUT TO PROCESS A PURL") - results_values = [v.value for v in router.process(purl)] - # print(f"* JUST PROCESSED A PURL") - # print(f"\nresults_values = {results_values}") + # results = list(router.process(purl)) + # results_versions = list(versions(purl)) + # packageversion_objects.append(results) + # results_values = [v.value for v in router.process(purl)] - # 2024-01-05 Friday 17:25:41. Iterate through PackageVersion() objects - # YO: rename the variable! - # print(f"\n\n* ABOUT TO PROCESS A PURL") for package_version_object in list(versions(purl)): - # YO This print statement is not reached when the PURL query returns a 404. - # print(f"* JUST PROCESSED A PURL") - # print(f"\n*** package_version_object = {package_version_object}") - - # print( - # f"\n*** package_version_object.to_dict() = {package_version_object.to_dict()}" - # ) - - # print( - # f"\n*** package_version_object.to_dict()['value'] = {package_version_object.to_dict()['value']}" - # ) - - # print( - # f"\n*** package_version_object.to_dict()['release_date'] = {package_version_object.to_dict()['release_date']}" - # ) - - # Here, too, create dict which we'll convert to JSON with json.dumps(). nested_dict = {} - # print(f"type(nested_dict) = {type(nested_dict)}") - nested_purl = purl + "@" + f'{package_version_object.to_dict()["value"]}' - # nested_purl = "TEST" - # dict_output["versions"].append({"purl": nested_purl}) nested_dict["purl"] = nested_purl nested_dict["version"] = f'{package_version_object.to_dict()["value"]}' nested_dict[ "release_date" ] = f'{package_version_object.to_dict()["release_date"]}' + dict_output["versions"].append(nested_dict) - # # print(f"\nnested_dict = {nested_dict}") - # print( - # f"\nnested_dict = {json.dumps(nested_dict, indent=4, sort_keys=False)}" - # ) - - # dict - # print(f"\n==> dict_output = {dict_output}") - # print(f"\n==> type(dict_output) = {type(dict_output)}") - # add to list - list_of_dict_outputs.append(dict_output) - # 2024-01-05 Friday 20:53:18. Does this format? - # list_of_dict_outputs.append(json.dumps(dict_output, indent=2, sort_keys=False)) - - # 2024-01-05 Friday 18:31:54. Let's convert dict_output with json.loads() and json.dumps() -- but that errors. Omit json.loads()??? - # json.dumps() works but the .json file is not formatted, i.e., no indents - dict_output_json_dumps = json.dumps(dict_output, indent=2, sort_keys=False) - - # no the next 2 fail. - # dict_output_json_loads = json.loads(dict_output) - # dict_output_json_dumps = json.dumps( - # dict_output_json_loads, indent=2, sort_keys=False - # ) - - # YO: 2024-01-08 Monday 15:13:07. This is a list of the versions -- each nested_dict -- above. - # print(f"\n==> dict_output_json_dumps = {dict_output_json_dumps}") - # and add to the separate list - list_of_dict_output_json_dumps.append(dict_output_json_dumps) - - # ZAP: 2024-01-08 Monday 13:37:29. We don't want these to rpint for a PURL that threw an error, so need to revise. - # print(f"\npurl_versions = {purl_versions}") - # print(f"\n==> list_of_dict_outputs = {list_of_dict_outputs}") - # print(f"\n==> list_of_dict_output_json_dumps = {list_of_dict_output_json_dumps}") - - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-03-list_of_dict_outputs-2024-01-08.json", - "w", - ) as f: - json.dump(list_of_dict_outputs, f) - - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-03-list_of_dict_output_json_dumps-2024-01-08.json", - "w", - ) as f: - json.dump(list_of_dict_output_json_dumps, f) - - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-03-formatted_list_of_dict_output_json_dumps-2024-01-08.json", - "w", - ) as f: - # json.dump(dict_output_json_dumps, f) - # json.dump(dict_output_json_dumps, f, indent=4) - # json.dump(list_of_dict_outputs, f) - - # 2024-01-05 Friday 20:54:27. This creates formatted JSON in the output file -- not in a list - # json.dump(dict_output, f, indent=4) - - # json.dump(list_of_dict_outputs, f, indent=4) - - # json.dump([obj for obj in list_of_dict_outputs], f) - - # 2024-01-05 Friday 21:11:21. THIS NOW WORKS! OUTPUT IS A LIST OF FORMATTED JSON OBJECTS! - json.dump([obj for obj in list_of_dict_outputs], f, indent=4) - - # print(json.dumps(dict_output, indent=4, sort_keys=False)) - # print("===") - print( - f"\n\nlist_of_dict_outputs = {json.dumps(list_of_dict_outputs, indent=4, sort_keys=False)}" - ) + + purl_versions.append(dict_output) + + # dict_output_json_dumps = json.dumps(dict_output, indent=2, sort_keys=False) + # list_of_dict_output_json_dumps.append(dict_output_json_dumps) return purl_versions diff --git a/purlcli_SAVED.py b/purlcli_SAVED.py deleted file mode 100644 index 50bedfa4..00000000 --- a/purlcli_SAVED.py +++ /dev/null @@ -1,239 +0,0 @@ -import json - -import click -import requests -from fetchcode.package_versions import PackageVersion, router, versions - - -@click.group() -def purlcli(): - """ - Return information from a PURL. - """ - - -@purlcli.command(name="validate") -@click.option( - "--purl", - "purls", - multiple=True, - required=False, - help="PackageURL or PURL.", -) -@click.option( - "--output", - type=click.File(mode="w", encoding="utf-8"), - required=True, - default="-", - help="Write validation output as JSON to FILE.", -) -@click.option( - "--file", - type=click.File(mode="r", encoding="utf-8"), - required=False, - help="Read a list of PURLs from a FILE, one per line.", -) -def validate(purls, output, file): - """ - Check the syntax of one or more PURLs. - """ - if (purls and file) or not (purls or file): - raise click.UsageError("Use either purls or file but not both.") - - if file: - purls = file.read().splitlines(False) - - # api_query = "https://public.purldb.io/api/validate/" - - validated_purls = validate_purls(purls) - # validated_purls = validate_purls(purls, api_query) - - json.dump(validated_purls, output, indent=4) - - -def validate_purls(purls): - # def validate_purls(purls, api_query): - api_query = "https://public.purldb.io/api/validate/" - validated_purls = [] - for purl in purls: - purl = purl.strip() - if not purl: - continue - request_body = {"purl": purl, "check_existence": True} - response = requests.get(api_query, params=request_body) - results = response.json() - validated_purls.append(results) - - return validated_purls - - -@purlcli.command(name="versions") -@click.option( - "--purl", - "purls", - multiple=True, - required=False, - help="PackageURL or PURL.", -) -@click.option( - "--output", - type=click.File(mode="w", encoding="utf-8"), - required=True, - default="-", - help="Write validation output as JSON to FILE.", -) -@click.option( - "--file", - type=click.File(mode="r", encoding="utf-8"), - required=False, - help="Read a list of PURLs from a FILE, one per line.", -) -def get_versions(purls, output, file): - """ - Check the syntax of one or more PURLs. - """ - if (purls and file) or not (purls or file): - raise click.UsageError("Use either purls or file but not both.") - - if file: - purls = file.read().splitlines(False) - - purl_versions = list_versions(purls) - - purl_versions - # print(f"\nlen(purl_versions) = {len(purl_versions)}") - - -def list_versions(purls): - print(f"\nlen(purls) = {len(purls)}") - print(f"\ntype(purls) = {type(purls)}") - print(f"\npurls = {purls}") - purl_versions = [] - - list_of_dict_outputs = [] - # and this will hold the dict_outputs converted with json.dumps(): - list_of_dict_output_json_dumps = [] - for purl in purls: - dict_output = {} - print(f"\n==> purl = {purl}") - dict_output["purl"] = purl - dict_output["versions"] = [] - purl = purl.strip() - if not purl: - continue - - # This works: this is a list of PackageVersion objects - results = list(router.process(purl)) - - print(f"\n\nrouter.process(purl) = {router.process(purl)}") - - print(f"\nlist(router.process(purl)) = {list(router.process(purl))}") - - # versions(purl) is a generator object - print(f"\nversions(purl) = {versions(purl)}") - - # Another test -- this is a list of PackageVersion objects - results_versions = list(versions(purl)) - print(f"\nresults_versions = {results_versions}") - - purl_versions.append(results) - - # Test: list of strings - results_values = [v.value for v in router.process(purl)] - print(f"\nresults_values = {results_values}") - - # 2024-01-05 Friday 17:25:41. Iterate through PackageVersion() objects - for package_version_object in list(versions(purl)): - print(f"\n*** package_version_object = {package_version_object}") - - print( - f"\n*** package_version_object.to_dict() = {package_version_object.to_dict()}" - ) - - print( - f"\n*** package_version_object.to_dict()['value'] = {package_version_object.to_dict()['value']}" - ) - - print( - f"\n*** package_version_object.to_dict()['release_date'] = {package_version_object.to_dict()['release_date']}" - ) - - # Here, too, create dict which we'll convert to JSON with json.dumps(). - nested_dict = {} - print(f"type(nested_dict) = {type(nested_dict)}") - - nested_purl = purl + "@" + f'{package_version_object.to_dict()["value"]}' - # nested_purl = "TEST" - - # dict_output["versions"].append({"purl": nested_purl}) - nested_dict["purl"] = nested_purl - nested_dict["version"] = f'{package_version_object.to_dict()["value"]}' - nested_dict[ - "release_date" - ] = f'{package_version_object.to_dict()["release_date"]}' - dict_output["versions"].append(nested_dict) - - # dict - print(f"\n==> dict_output = {dict_output}") - print(f"\n==> type(dict_output) = {type(dict_output)}") - # add to list - list_of_dict_outputs.append(dict_output) - # 2024-01-05 Friday 20:53:18. Does this format? - # list_of_dict_outputs.append(json.dumps(dict_output, indent=2, sort_keys=False)) - - # 2024-01-05 Friday 18:31:54. Let's convert dict_output with json.loads() and json.dumps() -- but that errors. Omit json.loads()??? - # json.dumps() works but the .json file is not formatted, i.e., no indents - dict_output_json_dumps = json.dumps(dict_output, indent=2, sort_keys=False) - - # no the next 2 fail. - # dict_output_json_loads = json.loads(dict_output) - # dict_output_json_dumps = json.dumps( - # dict_output_json_loads, indent=2, sort_keys=False - # ) - - print(f"\n==> dict_output_json_dumps = {dict_output_json_dumps}") - # and add to the separate list - list_of_dict_output_json_dumps.append(dict_output_json_dumps) - - print(f"\npurl_versions = {purl_versions}") - - print(f"\n==> list_of_dict_outputs = {list_of_dict_outputs}") - print(f"\n==> list_of_dict_output_json_dumps = {list_of_dict_output_json_dumps}") - - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-list_of_dict_outputs-2024-01-08.json", - "w", - ) as f: - json.dump(list_of_dict_outputs, f) - - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-list_of_dict_output_json_dumps-2024-01-08.json", - "w", - ) as f: - json.dump(list_of_dict_output_json_dumps, f) - - # try just one json.dumps() object -- dict_output_json_dumps -- NO: this also looks like this: - # "{\n \"purl\": \"pkg:pypi/minecode\",\n \"versions\": [\n {\n \"purl\": - with open( - "/mnt/c/nexb/purldb-testing/2024-01-08-testing/json-output/purlcli-single_dict_output_json_dumps-2024-01-08.json", - "w", - ) as f: - # json.dump(dict_output_json_dumps, f) - # json.dump(dict_output_json_dumps, f, indent=4) - # json.dump(list_of_dict_outputs, f) - - # 2024-01-05 Friday 20:54:27. This creates formatted JSON in the output file -- not in a list - # json.dump(dict_output, f, indent=4) - - # json.dump(list_of_dict_outputs, f, indent=4) - - # json.dump([obj for obj in list_of_dict_outputs], f) - - # 2024-01-05 Friday 21:11:21. THIS NOW WORKS! OUTPUT IS A LIST OF FORMATTED JSON OBJECTS! - json.dump([obj for obj in list_of_dict_outputs], f, indent=4) - - return purl_versions - - -if __name__ == "__main__": - purlcli()