Skip to content
83 changes: 47 additions & 36 deletions licomp_toolkit/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,26 @@ def validate(self, args):
LicompToolkitSchemaChecker().validate_file(args.file_name, deep=True)
return None, ReturnCodes.LICOMP_OK.value, None

def _resources_to_use(self, args):
lt = LicompToolkit()
resources = args.resources
new_resources = []
unsupported = []
if args.resources == ['all']:
new_resources = list(lt.licomp_resources().keys())
return new_resources, []
for resource in resources:
if 'licomp' not in resource:
resource = f'licomp_{resource}'
else:
resource = resource.replace('-', '_')

if not self.resource_avilable(resource):
unsupported.append(resource)
else:
new_resources.append(resource)
return new_resources, unsupported

def verify(self, args):
formatter = LicompToolkitFormatter.formatter(self.args.output_format)
try:
Expand All @@ -50,22 +70,9 @@ def verify(self, args):
else:
detailed_report = True

resources = args.resources
new_resources = []
unsupported = []
for resource in resources:
if 'licomp' not in resource:
resource = f'licomp_{resource}'
else:
resource = resource.replace('-', '_')

if not self.resource_avilable(resource):
unsupported.append(resource)
else:
new_resources.append(resource)
resources, unsupported = self._resources_to_use(args)
if unsupported:
return f'Resource(s) {", ".join(unsupported)} is/are not supported', ReturnCodes.LICOMP_UNSUPPORTED_RESOURCE.value, True
resources = new_resources
expr_checker = ExpressionExpressionChecker()
compatibilities = expr_checker.check_compatibility(self.__normalize_license(args.out_license),
self.__normalize_license(args.in_license),
Expand Down Expand Up @@ -106,22 +113,30 @@ def simplify(self, args):

def supported_resources(self, args):
formatter = LicompToolkitFormatter.formatter(args.output_format)
return formatter.format_licomp_resources([f'{x.name()}:{x.version()}' for x in self.licomp_toolkit.licomp_resources().values()]), ReturnCodes.LICOMP_OK.value, False
return formatter.format_licomp_resources(self.licomp_toolkit.licomp_resources_long()), ReturnCodes.LICOMP_OK.value, False

def _supports_helper(self, key, value, output_format):
licomp_resources = [x for x in self.licomp_toolkit.licomp_resources_long() if value in x[key]]
formatter = LicompToolkitFormatter.formatter(output_format)
return formatter.format_licomp_resources(licomp_resources)

def supports_license(self, args):
lic = args.license
licomp_resources = [f'{x.name()}:{x.version()}' for x in self.licomp_toolkit.licomp_resources().values() if lic in x.supported_licenses()]
formatter = LicompToolkitFormatter.formatter(args.output_format)
return formatter.format_licomp_resources(licomp_resources), ReturnCodes.LICOMP_OK.value, None
if lic not in self.licomp_toolkit.supported_licenses():
return None, ReturnCodes.LICOMP_UNSUPPORTED_LICENSE.value, f'License "{args.license}" not supported.'
return self._supports_helper('licenses', lic, args.output_format), ReturnCodes.LICOMP_OK.value, None

def supports_usecase(self, args):
try:
usecase = UseCase.string_to_usecase(args.usecase)
licomp_resources = [f'{x.name()}:{x.version()}' for x in self.licomp_toolkit.licomp_resources().values() if usecase in x.supported_usecases()]
formatter = LicompToolkitFormatter.formatter(args.output_format)
return formatter.format_licomp_resources(licomp_resources), ReturnCodes.LICOMP_OK.value, None
except KeyError:
return None, f'Use case "{args.usecase}" not supported. Supported use cases: {self.supported_usecases(args)[0]}'
usecase = args.usecase
if usecase not in [UseCase.usecase_to_string(x) for x in self.licomp_toolkit.supported_usecases()]:
return None, ReturnCodes.LICOMP_UNSUPPORTED_USECASE.value, f'Use case "{args.usecase}" not supported. Supported use cases: {self.supported_usecases(args)[0]}'
return self._supports_helper('usecases', usecase, args.output_format), ReturnCodes.LICOMP_OK.value, None

def supports_provisioning(self, args):
provisioning = args.provisioning
if provisioning not in [Provisioning.provisioning_to_string(x) for x in self.licomp_toolkit.supported_provisionings()]:
return None, ReturnCodes.LICOMP_UNSUPPORTED_PROVISIONING.value, f'Provisioning "{args.provisioning}" not supported. Supported provisionings: {self.supported_provisionings(args)[0]}'
return self._supports_helper('provisionings', provisioning, args.output_format), ReturnCodes.LICOMP_OK.value, None

def outbound_candidate(self, args):
suggester = OutboundSuggester()
Expand Down Expand Up @@ -152,15 +167,6 @@ def display_compatibility(self, args):
{'discard_unsupported': args.discard_unsupported_licenses})
return formatted, ReturnCodes.LICOMP_OK.value, None

def supports_provisioning(self, args):
try:
provisioning = Provisioning.string_to_provisioning(args.provisioning)
licomp_resources = [f'{x.name()}:{x.version()}' for x in self.licomp_toolkit.licomp_resources().values() if provisioning in x.supported_provisionings()]
formatter = LicompToolkitFormatter.formatter(args.output_format)
return formatter.format_licomp_resources(licomp_resources), ReturnCodes.LICOMP_OK.value, None
except KeyError:
return None, f'Provisioning "{args.provisioning}" not supported. Supported provisionings: {self.supported_provisionings(args)[0]}'

def versions(self, args):
formatter = LicompToolkitFormatter.formatter(args.output_format)
return formatter.format_licomp_versions(self.licomp_toolkit.versions()), ReturnCodes.LICOMP_OK.value, False
Expand All @@ -169,7 +175,7 @@ def resource_avilable(self, resource):
return resource in self.licomp_toolkit.licomp_resources().keys()

def _working_return_code(return_code):
return return_code < ReturnCodes.LICOMP_LAST_SUCCESSFUL_CODE.value
return return_code >= 0 and return_code < ReturnCodes.LICOMP_LAST_SUCCESSFUL_CODE.value

def main():
logging.debug("Licomp Toolkit")
Expand All @@ -186,7 +192,7 @@ def main():
parser.add_argument('-r', '--resources',
type=str,
action='append',
help='use only specified licomp resource',
help='use specified licomp resource. For a list use the commands \'supported-resources\'. Use \'all\' to use all',
default=[])

parser.add_argument('-nv', '--no-verbose',
Expand Down Expand Up @@ -245,6 +251,11 @@ def main():
if _working_return_code(code):
if res:
print(res)
else:
if err:
print(err, file=sys.stderr)
else:
pass
else:
print(res, file=sys.stderr)

Expand Down
20 changes: 14 additions & 6 deletions licomp_toolkit/format.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,33 @@ def format_display_compatibilities(self, compats, settings={}):

class TextLicompToolkitFormatter(LicompToolkitFormatter):

def _format_licomp_resource(self, licomp_resource):
name = licomp_resource['name']
version = licomp_resource['version']
usecases = ','.join(licomp_resource['usecases'])
provisionings = ','.join(licomp_resource['provisionings'])
resource_type = licomp_resource['type']
return f'{name}:{version}:{usecases}:{provisionings}:{resource_type}'

def format_licomp_resources(self, licomp_resources):
return "\n".join(licomp_resources)
return '\n'.join([self._format_licomp_resource(x) for x in licomp_resources])

def format_licomp_licenses(self, licomp_licenses):
return "\n".join(licomp_licenses)
return '\n'.join(licomp_licenses)

def __get_responses(self, results, indent=""):
def __get_responses(self, results, indent=''):
output = []
for res in ['yes', 'no', 'schneben']:
result = results.get(res)
if not result:
count = 0
else:
count = result["count"]
count = result['count']
output.append(f'{indent}{res}: {count}')

return output

def __compatibility_statuses(self, statuses, indent=""):
def __compatibility_statuses(self, statuses, indent=''):
output = []
for status, values in statuses.items():
resources = []
Expand All @@ -121,7 +129,7 @@ def __compatibility_statuses(self, statuses, indent=""):

return output

def __statuses(self, statuses, indent=""):
def __statuses(self, statuses, indent=''):
output = []
for status, values in statuses.items():
resources = []
Expand Down
3 changes: 1 addition & 2 deletions licomp_toolkit/lic_expr.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ def summarise_compatibilities(self, operator, operands):
OR: self.__summarise_compatibilities_or,
}[operator](operands)

class ExpressionExpressionChecker():
class _OBSOLETE_ExpressionExpressionChecker():

def __init__(self):
self.le_checker = LicenseExpressionChecker()
Expand All @@ -251,7 +251,6 @@ def __parsed_expression_to_name(self, parsed_expression):
return parsed_expression[parsed_expression[COMPATIBILITY_TYPE]]

def check_compatibility(self, outbound, inbound, usecase, provisioning, detailed_report=False):

inbound_parsed = self.le_parser.parse_license_expression(inbound)

outbound_parsed = self.le_parser.parse_license_expression(outbound)
Expand Down
73 changes: 46 additions & 27 deletions licomp_toolkit/toolkit.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,25 +41,15 @@ class LicompToolkit(Licomp):

def __init__(self):
Licomp.__init__(self)
self.LICOMP_RESOURCES = {}
self.LICOMP_RESOURCE_NAMES = {
"osadl": {
"package": "licomp_osadl.osadl",
"class": "LicompOsadl",
},
"reclicense": {
"package": "licomp_reclicense.reclicense",
"class": "LicompReclicense",
},
"hermione": {
"package": "licomp_hermione.hermione",
"class": "LicompHermione",
},
"dwheeler": {
"package": "licomp_dwheeler.dwheeler",
"class": "LicompDw",
},
}
self._licomp_resources = {}
for licomp in [LicompReclicense, LicompOsadl]:
licomp_instance = licomp()
self._licomp_resources[licomp_instance.name()] = licomp_instance

self._licomp_resources_optional = {}
for licomp in [LicompHermione, LicompProprietary, LicompDw, GnuQuickGuideLicense]:
licomp_instance = licomp()
self._licomp_resources_optional[licomp_instance.name()] = licomp_instance

def supported_api_version(self):
return my_supported_api_version
Expand All @@ -76,24 +66,53 @@ def __add_meta(self, compatibilities):
compatibilities["meta"]['disclaimer'] = disclaimer

def licomp_resources(self):
if not self.LICOMP_RESOURCES:
for licomp in [LicompReclicense, LicompOsadl, LicompHermione, LicompProprietary, LicompDw, GnuQuickGuideLicense]:
licomp_instance = licomp()
self.LICOMP_RESOURCES[licomp_instance.name()] = licomp_instance
return self.LICOMP_RESOURCES
return self._licomp_resources | self._licomp_resources_optional

def licomp_standard_resources(self):
return self._licomp_resources

def licomp_optional_resources(self):
return self._licomp_resources_optional

def licomp_resource_long(self, resource):
return {
'name': resource.name(),
'version': resource.version(),
'usecases': [UseCase.usecase_to_string(x) for x in resource.supported_usecases()],
'provisionings': [Provisioning.provisioning_to_string(x) for x in resource.supported_provisionings()],
'licenses': resource.supported_licenses(),
'type': self._resource_type(resource),
}

def licomp_resources_long(self):
_resources = []
for resource in self.licomp_resources().values():
_resources.append(self.licomp_resource_long(resource))
return _resources

def _resource_type(self, resource):
if self._resource_is_standard(resource):
return 'standard'
return 'optional'

def _resource_is_optional(self, resource):
return resource.name() in self._licomp_resources_optional

def _resource_is_standard(self, resource):
return not self._resource_is_optional(resource)

def __summarize_compatibility(self, compatibilities, outbound, inbound, usecase, provisioning, resources):
compatibilities["summary"] = {}
statuses = {}
compats = {}
compatibilities['nr_licomp'] = len(resources)
# for resource_name in self.licomp_resources():
# for resource_name in self._licomp_resources():
for compat in compatibilities["compatibilities"]:
logging.debug(f': {compat}')
logging.debug(f': {compat["resource_name"]}')
self.__add_to_list(statuses, compat['status'], compat)
self.__add_to_list(compats, compat['compatibility_status'], compat)
compatibilities["summary"]["resources"] = [f'{x.name()}:{x.version()}' for x in self.licomp_resources().values()]
compatibilities["summary"]["resources"] = self.licomp_resources_long()
compatibilities["summary"]["outbound"] = outbound
compatibilities["summary"]["inbound"] = inbound
compatibilities["summary"]["usecase"] = UseCase.usecase_to_string(usecase)
Expand Down Expand Up @@ -337,7 +356,7 @@ def check_compatibility(self, outbound, inbound, usecase, provisioning, resource
except KeyError:
raise LicompException(f'Provisioning {provisioning} not supported.', ReturnCodes.LICOMP_UNSUPPORTED_PROVISIONING)

licomp_resources = list(self.licomp_toolkit.licomp_resources().keys())
licomp_resources = list(self.licomp_toolkit.licomp_standard_resources().keys())
if not resources:
resources = licomp_resources
else:
Expand Down
6 changes: 6 additions & 0 deletions licomp_toolkit_test.tmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
digraph depends {
graph [label="License Compatibility Graph (library)" labelloc=t]
node [shape=plaintext]
"MIT" -> "BSD-3-Clause" [color="darkblue" style="dotted"]
"BSD-3-Clause" -> "MIT" [color="darkgreen" ]
}
19 changes: 18 additions & 1 deletion tests/python/test_resources.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,32 @@

lt = LicompToolkit()
resource_names = [x for x in lt.licomp_resources()]
standard_resource_names = [x for x in lt.licomp_standard_resources()]
optional_resource_names = [x for x in lt.licomp_optional_resources()]

def test_supported_resources():
assert len(lt.licomp_resources()) == 6

def test_supported_resources_reclicense():
def test_supported_resources():
assert "licomp_reclicense" in resource_names
assert "licomp_osadl" in resource_names
assert "licomp_proprietary" in resource_names
assert "licomp_hermione" in resource_names
assert "licomp_dwheeler" in resource_names
assert "licomp_gnuguide" in resource_names

def test_supported_resources_standard():
assert "licomp_reclicense" in standard_resource_names
assert "licomp_osadl" in standard_resource_names
assert "licomp_proprietary" not in standard_resource_names
assert "licomp_hermione" not in standard_resource_names
assert "licomp_dwheeler" not in standard_resource_names
assert "licomp_gnuguide" not in standard_resource_names

def test_supported_resources_standard():
assert "licomp_reclicense" not in optional_resource_names
assert "licomp_osadl" not in optional_resource_names
assert "licomp_proprietary" in optional_resource_names
assert "licomp_hermione" in optional_resource_names
assert "licomp_dwheeler" in optional_resource_names
assert "licomp_gnuguide" in optional_resource_names
10 changes: 5 additions & 5 deletions tests/shell/test-cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ test_version()
test_supp_unsupp()
{
echo "# test supported/unsupported licenses"
test_licomp_tk "verify -il MIT -ol MIT" "${EXTRACT_COMPAT}.summary.results.yes.count" 5
test_licomp_tk "verify -il MIT -ol MIT" "${EXTRACT_COMPAT}.summary.results.yes.count" 1
test_licomp_tk "verify -il MIT -ol MIT2" "${EXTRACT_COMPAT}.summary.results.nr_valid" 0
test_licomp_tk "verify -il MIT2 -ol MIT" "${EXTRACT_COMPAT}.summary.results.nr_valid" 0
test_licomp_tk "verify -il MIT2 -ol MIT2" "${EXTRACT_COMPAT}.summary.results.nr_valid" 0
Expand All @@ -94,16 +94,16 @@ test_snippet_bindist()
echo "# snippet vs bin dist"
test_licomp_tk "-u snippet verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.nr_valid" 1
test_licomp_tk "-u snippet verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.yes.count" 1
test_licomp_tk "verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.nr_valid" 4
test_licomp_tk "verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.yes.count" 4
test_licomp_tk "verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.nr_valid" 1
test_licomp_tk "verify -il BSD-3-Clause -ol LGPL-2.1-or-later" "${EXTRACT_COMPAT}.summary.results.yes.count" 1
}

test_supports_license()
{
echo "# supports license"
test_licomp_tk_text " -of text supports-license MIT" " | wc -l" 6
test_licomp_tk_text " -of json supports-license MIT" " | jq .[] | wc -l" 6
test_licomp_tk_text " supports-license MIT" " | jq .[] | wc -l" 6
test_licomp_tk_text " -of json supports-license MIT" " | jq .[].name | wc -l" 6
test_licomp_tk_text " supports-license MIT" " | jq .[].name | wc -l" 6
}

test_supports_provisioning()
Expand Down
2 changes: 1 addition & 1 deletion tests/shell/test_returns.sh
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ test_verify()
run_comp_test 0 "verify -il BSD-3-Clause -ol GPL-2.0-only"

# Success and mixed compatibility
run_comp_test 9 "verify -ol 0BSD -il MS-PL"
run_comp_test 2 "verify -ol 0BSD -il MS-PL"

# Success and incompatible
run_comp_test 2 "verify -il GPL-2.0-only -ol BSD-3-Clause"
Expand Down
Loading