Skip to content

Commit

Permalink
rename use_flict to flict_simplify, make arguments more clear
Browse files Browse the repository at this point in the history
  • Loading branch information
mxmehl committed Aug 14, 2024
1 parent b272c1c commit 09e52e7
Show file tree
Hide file tree
Showing 5 changed files with 46 additions and 32 deletions.
6 changes: 3 additions & 3 deletions complassist/_flict.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ def _run_flict(
return code, stdout, stderr


def flict_simplify(expression: str, output_format: str, no_relicensing: bool = True) -> str:
def flict_simplify_license(expression: str, output_format: str, no_relicensing: bool = True) -> str:
"""Simplify a license expression using flict"""
options = ["-of", output_format]
if no_relicensing:
Expand All @@ -52,11 +52,11 @@ def flict_simplify(expression: str, output_format: str, no_relicensing: bool = T
return simplified


def flict_simplify_list(expressions: list[str]) -> list[str]:
def flict_simplify_license_list(expressions: list[str]) -> list[str]:
"""Simplify a list of license expressions"""
simplified = []
for lic in expressions:
simplified.append(flict_simplify(lic, output_format="text"))
simplified.append(flict_simplify_license(expression=lic, output_format="text"))

return list(set(simplified))

Expand Down
24 changes: 15 additions & 9 deletions complassist/_licensing.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,23 @@

from license_expression import ExpressionError, Licensing, get_spdx_licensing

from ._flict import flict_outbound_candidate, flict_simplify, flict_simplify_list
from ._flict import (
flict_outbound_candidate,
flict_simplify_license,
flict_simplify_license_list,
)
from ._sbom_parse import extract_items_from_cdx_sbom


def _extract_license_expression_and_names_from_sbom(
sbom_path: str, use_flict: bool = False
sbom_path: str, flict_simplify: bool = False
) -> tuple[list[str], list[str]]:
"""Exract all SPDX expressions and license names from an SBOM"""
lic_expressions = []
lic_names = []

for item in extract_items_from_cdx_sbom(
sbom_path, information=["name", "purl", "licenses-short"], use_flict=use_flict
sbom_path, information=["name", "purl", "licenses-short"], flict_simplify=flict_simplify
):
licenses_short: list[dict] = item.get("licenses-short", [])

Expand All @@ -38,16 +42,18 @@ def _extract_license_expression_and_names_from_sbom(
# If using flict, simplify these found licenses. Will reduce possible
# duplicates and fix problematic SPDX expressions (e.g. MPL-2.0+)
# That's far more performant than doing that for each license in the SBOM
if use_flict:
expressions = flict_simplify_list(expressions)
if flict_simplify:
expressions = flict_simplify_license_list(expressions)
names = sorted(list(set(lic_names)))

return expressions, names


def list_all_licenses(sbom_path: str, use_flict: bool = False) -> list[str]:
def list_all_licenses(sbom_path: str, flict_simplify: bool = False) -> list[str]:
"""List all detected licenses of an SBOM, unified and sorted"""
expressions, names = _extract_license_expression_and_names_from_sbom(sbom_path, use_flict)
expressions, names = _extract_license_expression_and_names_from_sbom(
sbom_path=sbom_path, flict_simplify=flict_simplify
)

# Combine both SPDX expressions and names, sort and unify again
return sorted(list(set(expressions + names)))
Expand Down Expand Up @@ -86,7 +92,7 @@ def _craft_single_spdx_expression(licenses: list[str]):
def get_outbound_candidate(sbom_path: str, simplify: bool = True) -> dict[str, str | list[str]]:
"""Get license outbound candidates from an SBOM"""
logging.info("Extracting, simplifying and validating found licenses. This can take a while")
licenses_in_sbom = list_all_licenses(sbom_path, use_flict=simplify)
licenses_in_sbom = list_all_licenses(sbom_path, flict_simplify=simplify)

# Check whether all licenses are valid SPDX expressions
licenses = _validate_spdx_licenses(licenses_in_sbom)
Expand All @@ -95,7 +101,7 @@ def get_outbound_candidate(sbom_path: str, simplify: bool = True) -> dict[str, s
expression = _craft_single_spdx_expression(licenses)
if simplify:
logging.debug("Simplify crafted license expression %s", expression)
expression = flict_simplify(expression, output_format="text")
expression = flict_simplify_license(expression, output_format="text")
logging.debug("Simplified licenses expression: %s", expression)

# Get outbound candidate
Expand Down
2 changes: 1 addition & 1 deletion complassist/_sbom_enrich.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def _enrich_component_with_cd_data(
"""
# Get purl, original licenses, and short/simplified licenses data from component
raw_data = extract_items_from_component(
component, ["purl", "licenses", "licenses-short", "copyright"], use_flict=True
component, ["purl", "licenses", "licenses-short", "copyright"], flict_simplify=True
)
# Put raw data into separate variables, slightly adapted
purl = raw_data["purl"]
Expand Down
24 changes: 14 additions & 10 deletions complassist/_sbom_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@

import logging

from ._flict import flict_simplify
from ._flict import flict_simplify_license
from ._helpers import read_json_file


def _unify_licenses_data(licenses_data: list[dict], use_flict: bool = True) -> list[dict]:
def _unify_licenses_data(licenses_data: list[dict], flict_simplify: bool = True) -> list[dict]:
"""Convert a list of license ids/expressions/names to a single string,
either an expression or a name"""

Expand All @@ -31,8 +31,8 @@ def _unify_licenses_data(licenses_data: list[dict], use_flict: bool = True) -> l
f"({expression})" for d in licenses_data for _, expression in d.items()
]
spdx_expression = " AND ".join(expressions_list)
if use_flict:
spdx_expression = flict_simplify(spdx_expression, output_format="text")
if flict_simplify:
spdx_expression = flict_simplify_license(spdx_expression, output_format="text")
return [{"spdx-expression": spdx_expression}]

# At least one free-text license contained, so we need to form a new free-text field
Expand Down Expand Up @@ -60,7 +60,7 @@ def _license_short_to_valid_cdx_item(short_license: list[dict]) -> list[dict]:
return []


def _shorten_cdx_licenses_item(licenses: list, use_flict: bool = True) -> list:
def _shorten_cdx_licenses_item(licenses: list, flict_simplify: bool = True) -> list:
"""Extract relevant license fields in a CycloneDX SBOM
(id, expression, name) in a simplified form (only expression or name)"""
collection: list[dict] = []
Expand Down Expand Up @@ -92,11 +92,11 @@ def _shorten_cdx_licenses_item(licenses: list, use_flict: bool = True) -> list:
licdata,
)

simplified_license_data = _unify_licenses_data(collection, use_flict=use_flict)
simplified_license_data = _unify_licenses_data(collection, flict_simplify=flict_simplify)
return _license_short_to_valid_cdx_item(simplified_license_data)


def extract_items_from_component(component: dict, items: list, use_flict: bool) -> dict:
def extract_items_from_component(component: dict, items: list, flict_simplify: bool) -> dict:
"""Extract certain items from a single component of a CycloneDX SBOM"""
# Very noisy logging, disabled
# logging.debug(
Expand All @@ -109,7 +109,7 @@ def extract_items_from_component(component: dict, items: list, use_flict: bool)
# output that is easier to parse later
if item == "licenses-short":
extraction[item] = _shorten_cdx_licenses_item(
component.get("licenses", []), use_flict=use_flict
component.get("licenses", []), flict_simplify=flict_simplify
)

# For all other fields, just return the output
Expand Down Expand Up @@ -145,14 +145,18 @@ def spdx_expression_to_cdx_licenses(spdx_expression: str | None) -> list:


def extract_items_from_cdx_sbom(
sbom_path: str, information: list, use_flict: bool = True
sbom_path: str, information: list, flict_simplify: bool = True
) -> list[dict]:
"""Extract certain items from all components of a CycloneDX SBOM (JSON)"""
sbom = read_json_file(sbom_path)

result = []
# Loop all contained components
for comp in sbom.get("components", []):
result.append(extract_items_from_component(comp, information, use_flict))
result.append(
extract_items_from_component(
component=comp, items=information, flict_simplify=flict_simplify
)
)

return result
22 changes: 13 additions & 9 deletions complassist/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ def main(): # pylint: disable=too-many-branches, too-many-statements
# SBOM commands
if args.command == "sbom":
if args.sbom_command == "generate":
generate_cdx_sbom(args.directory, args.output)
generate_cdx_sbom(directory=args.directory, output=args.output)

# Enrich SBOM by ClearlyDefined data
elif args.sbom_command == "enrich":
Expand All @@ -277,10 +277,10 @@ def main(): # pylint: disable=too-many-branches, too-many-statements
# Convert comma-separated information to list
info = args.extract.split(",")
extraction = extract_items_from_cdx_sbom(
args.file, information=info, use_flict=not args.no_simplify
sbom_path=args.file, information=info, flict_simplify=not args.no_simplify
)
if args.output == "json":
print(dict_to_json(extraction))
print(dict_to_json(data=extraction))
elif args.output == "dict":
print(extraction)
elif args.output == "none":
Expand All @@ -294,23 +294,27 @@ def main(): # pylint: disable=too-many-branches, too-many-statements
elif args.command == "clearlydefined":
# ClearlyDefined conversion
if args.clearlydefined_command == "convert":
print(purl_to_cd_coordinates(args.purl))
print(purl_to_cd_coordinates(purl=args.purl))

elif args.clearlydefined_command == "fetch":
if args.purl:
coordinates = purl_to_cd_coordinates(args.purl)
coordinates = purl_to_cd_coordinates(purl=args.purl)
else:
coordinates = args.coordinates

print_clearlydefined_result(get_clearlydefined_license_and_copyright(coordinates))
print_clearlydefined_result(
results=get_clearlydefined_license_and_copyright(coordinates)
)

# License compliance commands
elif args.command == "licensing":
# List all detected licenses in an SBOM, unified and sorted
if args.licensing_command == "list":
all_licenses = list_all_licenses(sbom_path=args.file, use_flict=not args.no_simplify)
all_licenses = list_all_licenses(
sbom_path=args.file, flict_simplify=not args.no_simplify
)
if args.output == "json":
print(dict_to_json(all_licenses))
print(dict_to_json(data=all_licenses))
elif args.output == "dict":
print(all_licenses)
elif args.output == "plain":
Expand All @@ -324,7 +328,7 @@ def main(): # pylint: disable=too-many-branches, too-many-statements
sbom_path=args.file, simplify=not args.no_simplify
)
if args.output == "json":
print(dict_to_json(outbound_candidates))
print(dict_to_json(data=outbound_candidates))
elif args.output == "dict":
print(outbound_candidates)
elif args.output == "plain":
Expand Down

0 comments on commit 09e52e7

Please sign in to comment.