licomp-toolkit 0.6.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,564 @@
1
+ # SPDX-FileCopyrightText: 2024 Henrik Sandklef
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from collections import OrderedDict
6
+
7
+ from functools import cmp_to_key
8
+ import json
9
+ import logging
10
+
11
+ from licomp_toolkit.toolkit import LicompToolkit
12
+ from licomp.interface import UseCase
13
+ from licomp.interface import Provisioning
14
+ from licomp.interface import Modification
15
+
16
+ class LicensePolicyException(Exception):
17
+
18
+ def __init__(self, message):
19
+ self.message = message
20
+ super().__init__(self.message)
21
+
22
+
23
+ class LicensePolicy:
24
+
25
+ def __init__(self, policy_file):
26
+ data = self._read_json_file(policy_file)
27
+ self.policy_meta = data['meta']
28
+ self.policy = data['policy']
29
+
30
+ def _read_json_data(self, data):
31
+ return json.load(data)
32
+
33
+ def _read_json_file(self, file_name):
34
+ with open(file_name) as fp:
35
+ return self._read_json_data(fp)
36
+
37
+ def allowed(self):
38
+ return self.policy['allowed']
39
+
40
+ def avoided(self):
41
+ return self.policy['avoided']
42
+
43
+ def denied(self):
44
+ return self.policy['denied']
45
+
46
+ def meta(self):
47
+ return self.policy_meta
48
+
49
+ def list_presence(self, lic, ignore_missing=False):
50
+
51
+ if lic in self.allowed():
52
+ return 1, self.allowed().index(lic)
53
+ if lic in self.avoided():
54
+ return 2, self.avoided().index(lic)
55
+ if lic in self.denied():
56
+ return 3, self.denied().index(lic)
57
+
58
+ if ignore_missing:
59
+ return None, None
60
+ raise LicensePolicyException(f'License "{lic}" is not present in any of the policy\'s lists. Licenses: {self.allowed()}, {self.avoided()} ')
61
+
62
+ def list_nr_to_name(self, nr):
63
+ return {
64
+ 1: 'allowed',
65
+ 2: 'avoided',
66
+ 3: 'denied'}.get(nr, '')
67
+
68
+ def list_name_to_nr(self, nr):
69
+ return {
70
+ 'allowed': 1,
71
+ 'avoided': 2,
72
+ 'denied': 3}.get(nr, None)
73
+
74
+ def compare_preferences(self, lic1, lic2, ignore_missing=False):
75
+ return self.compare_preferences_general(lic1, lic2, ignore_missing=ignore_missing)
76
+
77
+ def compare_preferences_expressions(self, lic1, lic2, key, ignore_missing=False):
78
+ return self.compare_preferences_general(lic1, lic2, key, ignore_missing=ignore_missing)
79
+
80
+ def compare_preferences_general(self, lic1, lic2, key=None, ignore_missing=False):
81
+ """
82
+
83
+ returns
84
+ * negative if lic1 is more preferred than lic2
85
+ * None if both licenses are denied
86
+ raises
87
+ * LicensePolicyException if at least one license is not listed (if ignore_missing, then both licenses need to be not listed to raise exception)
88
+ """
89
+ logging.debug(f'compare_preferences {lic1}, {lic2}, {key}, {ignore_missing}')
90
+ if key:
91
+ list_key = key.replace('_license', '')
92
+ lic1_name = lic1[list_key]['license']
93
+ lic2_name = lic2[list_key]['license']
94
+ if lic1[list_key]['type'] == 'license':
95
+ lic1_list, lic1_index = self.list_presence(lic1[list_key]['license'], ignore_missing=False)
96
+ else:
97
+ lic1_list_name = lic1[list_key]['preferences']['license_list']
98
+ lic1_list = self.list_name_to_nr(lic1_list_name)
99
+ lic1_index = lic1[list_key]['preferences']['license_index']
100
+
101
+ if lic2[list_key]['type'] == 'license':
102
+ lic2_list, lic2_index = self.list_presence(lic2[list_key]['license'], ignore_missing=False)
103
+ else: # license-expression
104
+ lic2_list_name = lic2[list_key]['preferences']['license_list']
105
+ lic2_list = self.list_name_to_nr(lic2_list_name)
106
+ lic2_index = lic2[list_key]['preferences']['license_index']
107
+ else:
108
+ lic1_name = lic1
109
+ lic2_name = lic2
110
+ lic1_list, lic1_index = self.list_presence(lic1, ignore_missing=ignore_missing)
111
+ lic2_list, lic2_index = self.list_presence(lic2, ignore_missing=ignore_missing)
112
+
113
+ logging.debug(f'compare_preferences: "{lic1_list}", "{lic1_index}" "{lic2_list}", "{lic2_index}"')
114
+
115
+ if (not lic1_list) and (not lic2_list):
116
+ if ignore_missing:
117
+ logging.debug(f'compare_preferences({lic1_name}, {lic2_name}, {ignore_missing}): ignore since both None')
118
+ return None
119
+
120
+ raise LicensePolicyException(f'License "{lic1_name}" and/or "{lic2_name}" is not present in any of the policy\'s lists.')
121
+
122
+ if lic1_list == lic2_list:
123
+ # if both are denied, return None to indicate "error"
124
+ if lic1_list == 3:
125
+ return None
126
+ return lic1_index - lic2_index
127
+
128
+ if not lic1_list:
129
+ return 1
130
+ if not lic2_list:
131
+ return -1
132
+
133
+ return lic1_list - lic2_list
134
+
135
+ def most_preferred(self, lic1, lic2, ignore_missing=False):
136
+ pref = self.compare_preferences(lic1, lic2, ignore_missing)
137
+ if pref is None:
138
+ return None
139
+ if pref < 0:
140
+ return lic1
141
+ return lic2
142
+
143
+ def OBSOLETE_preferred_score_ignore_missing(self, lic1, lic2):
144
+ pref = self.compare_preferences(lic1, lic2, ignore_missing=True)
145
+ if pref is None:
146
+ return 10000
147
+ if pref < 0:
148
+ return -1
149
+ if lic1 == lic2:
150
+ return 0
151
+ return 1
152
+
153
+ def preferred_score_licenses(self, lic1, lic2, key):
154
+ logging.debug(f'preferred_score_licenses {lic1}, {lic2}, {key}')
155
+ pref = self.compare_preferences_expressions(lic1, lic2, key)
156
+ if pref < 0:
157
+ return -1
158
+ if lic1 == lic2:
159
+ return 0
160
+ return 1
161
+
162
+ def preferred_score_inbounds(self, lic1, lic2):
163
+ logging.debug(f'preferred_score_inbounds {lic1}, {lic2}')
164
+ return self.preferred_score_licenses(lic1, lic2, 'inbound_license')
165
+
166
+ def preferred_score_outbounds(self, lic1, lic2):
167
+ logging.debug(f'preferred_score_outbounds {lic1}, {lic2}')
168
+ return self.preferred_score_licenses(lic1, lic2, 'outbound_license')
169
+
170
+ def least_preferred(self, lic1, lic2, ignore_missing=False):
171
+ most_preferred = self.most_preferred(lic1, lic2, ignore_missing)
172
+ if most_preferred == lic1:
173
+ return lic2
174
+ return lic1
175
+
176
+ class DefaultLicensePolicy(LicensePolicy):
177
+
178
+ def __init__(self, resources, usecase, provisioning):
179
+ self.lt = LicompToolkit()
180
+ self.__licenses(resources, usecase, provisioning)
181
+ license_order = self.__order(resources, usecase, provisioning)
182
+ self.policy_meta = {}
183
+ self.policy = {
184
+ 'allowed': license_order,
185
+ 'avoided': [],
186
+ 'denied': [],
187
+ }
188
+
189
+ def __order(self, resources, usecase, provisioning):
190
+ scores = {}
191
+ for lic in self.licenses:
192
+ scores[lic] = 0
193
+ for out_license in self.licenses:
194
+ for in_license in self.licenses:
195
+ for resource in self.resources:
196
+ compat = resource.outbound_inbound_compatibility(out_license,
197
+ in_license,
198
+ UseCase.string_to_usecase(usecase),
199
+ Provisioning.string_to_provisioning(provisioning),
200
+ Modification.UNMODIFIED)
201
+ if compat['compatibility_status'] == 'yes':
202
+ scores[in_license] += 1
203
+
204
+ scores_dict = OrderedDict(sorted(scores.items(), key=lambda x: x[1], reverse=True))
205
+ return [x for (x, y) in scores_dict.items()]
206
+
207
+ def __licenses(self, resources, usecase, provisioning):
208
+ self.resources = []
209
+ self.licenses = []
210
+
211
+ logging.debug(f'__licenses {resources}, {usecase}, {provisioning}')
212
+ for resource in resources:
213
+ for licomp_resource in self.lt.licomp_resources():
214
+ if resource == licomp_resource:
215
+ self.licenses += self.lt.licomp_resources()[licomp_resource].supported_licenses()
216
+ self.resources.append(self.lt.licomp_resources()[licomp_resource])
217
+ logging.debug(f'__licenses {resources}, {usecase}, {provisioning} ==> {self.licenses}')
218
+
219
+
220
+ class LicensePolicyHandler:
221
+
222
+ def __init__(self, policy_file=None, resources=None, usecase=None, provisioning=None):
223
+ logging.debug("LicensePolicyHandler()")
224
+ if policy_file:
225
+ self.policy = LicensePolicy(policy_file)
226
+ self.policy_type = 'policy_file'
227
+ self.policy_file = policy_file
228
+ else:
229
+ self.policy = DefaultLicensePolicy(resources, usecase, provisioning)
230
+ self.policy_type = 'default'
231
+ self.policy_file = None
232
+
233
+ def __is_license_expression(self, lic):
234
+ CONTAINS_AND = 'AND' in lic
235
+ CONTAINS_OR = 'OR' in lic
236
+ return CONTAINS_AND or CONTAINS_OR
237
+
238
+ def usable_license(self, lic, key):
239
+ if key in lic:
240
+ license_name = lic[key]['license']
241
+ else:
242
+ license_name = lic
243
+ if self.__is_license_expression(license_name):
244
+ # should have been checked already, so skip
245
+ policy_ok = True
246
+ else:
247
+ policy_ok = license_name in (self.policy.allowed() + self.policy.avoided())
248
+ compat_ok = lic['compatibility'] == 'yes'
249
+
250
+ ret = policy_ok and compat_ok
251
+ logging.debug(f'usable_license {lic} | policy_ok: {policy_ok}, compat_ok: {compat_ok} ===> {ret}')
252
+
253
+ return ret
254
+
255
+ def unusable_licenses(self, licenses, key):
256
+ problematic_licenses = []
257
+ for lic in licenses:
258
+ license_name = lic[key]['license']
259
+ if self.__is_license_expression(license_name):
260
+ # should have been checked already, so skip
261
+ policy_ok = True
262
+ else:
263
+ policy_ok = license_name in (self.policy.allowed() + self.policy.avoided())
264
+ compat_ok = lic['compatibility'] == 'yes'
265
+
266
+ ret = policy_ok and compat_ok
267
+ logging.debug(f'unusable_license {lic} | policy_ok: {policy_ok}, compat_ok: {compat_ok} ===> {ret}')
268
+ if not ret:
269
+ problematic_licenses.append(lic)
270
+ return len(problematic_licenses) > 0
271
+
272
+ def scored_general(self, licenses, operator, key):
273
+ compare_function = {
274
+ 'inbound': self.policy.preferred_score_inbounds,
275
+ 'outbound': self.policy.preferred_score_outbounds,
276
+ }[key]
277
+ if operator == "OR":
278
+ logging.debug(f'preferred_licenses {licenses}, {operator}')
279
+ filtered_licenses = []
280
+ unusable_licenses = []
281
+ for lic in licenses:
282
+ if self.usable_license(lic, key):
283
+ filtered_licenses.append(lic)
284
+ else:
285
+ print("UNUSABLE: " + str(key))
286
+ print("UNUSABLE: " + str(lic))
287
+ unusable_licenses.append(lic[key])
288
+ sorted_licenses = sorted(filtered_licenses, key=cmp_to_key(compare_function))
289
+ return sorted_licenses, unusable_licenses
290
+ elif operator == "AND":
291
+ if self.unusable_licenses(licenses, key):
292
+ sorted_licenses = []
293
+ unusable_licenses = [x[key] for x in licenses]
294
+ else:
295
+ sorted_licenses = sorted(licenses, key=cmp_to_key(compare_function))
296
+ unusable_licenses = []
297
+
298
+ return sorted_licenses, unusable_licenses
299
+ else:
300
+ raise Exception(f'scored_general got a bad operator "{operator}"')
301
+
302
+ def scored_inbounds(self, inbounds, operator):
303
+ return self.scored_general(inbounds, operator, 'inbound')
304
+
305
+ def scored_outbounds(self, outbounds, operator):
306
+ return self.scored_general(outbounds, operator, 'outbound')
307
+
308
+ def __check_policy_check(self, reply):
309
+ keys = [
310
+ 'license',
311
+ 'type',
312
+ 'preferences:license',
313
+ 'preferences:license_list',
314
+ 'preferences:license_index',
315
+ ]
316
+ for key in keys:
317
+ in_value = reply['inbound']
318
+ out_value = reply['outbound']
319
+ for sub_key in key.split(':'):
320
+ assert sub_key in in_value # noqa: S101
321
+ assert sub_key in out_value # noqa: S101
322
+ in_value = in_value[sub_key]
323
+ out_value = out_value[sub_key]
324
+
325
+ assert 'compatibility' in reply # noqa: S101
326
+ assert 'unusable' in reply # noqa: S101
327
+
328
+ def __pack_policy_check(self, outbound_pref, inbound_pref, compat, unusable):
329
+ return {
330
+ 'outbound': outbound_pref,
331
+ 'inbound': inbound_pref,
332
+ 'compatibility': compat,
333
+ 'unusable': unusable,
334
+ }
335
+
336
+ def __pack_policy_unusable(self, unusables):
337
+ return unusables
338
+
339
+ def __pack_policy_preferences(self, license_string, license_list, license_index):
340
+ return {
341
+ 'license': license_string,
342
+ 'license_list': license_list,
343
+ 'license_index': license_index,
344
+ }
345
+
346
+ def __pack_policy_check_license(self, license_name, license_type, pref_lic, pref_lic_list, pref_lic_idx):
347
+ return {
348
+ 'license': license_name,
349
+ 'type': license_type,
350
+ 'preferences': {
351
+ 'license': pref_lic,
352
+ 'license_list': pref_lic_list,
353
+ 'license_index': pref_lic_idx,
354
+ },
355
+ }
356
+
357
+ def __apply_to_compat_object(self, compat_object, ignore_missing=False, indent=0):
358
+ """
359
+ Specification of the policy_check format
360
+
361
+ define license_data:
362
+
363
+ license (str)
364
+ license_type (str: license | license-expression)
365
+ preferrences {
366
+ license
367
+ license_list
368
+ license_index
369
+ }
370
+
371
+ unusable [
372
+ { license (str),
373
+ reason (str)
374
+ }
375
+ ]
376
+
377
+ inbound (license_data)
378
+ outbound (license_data)
379
+ compatibility (str)
380
+
381
+ """
382
+ unusable = []
383
+ if 'outbound-expression' in compat_object['compatibility_check']:
384
+ operator = compat_object['operator']
385
+ outbounds = []
386
+ for operand in compat_object['operands']:
387
+ operand['compatibility_object']['TEST'] = "Liverpool"
388
+ self.__apply_to_compat_object(operand['compatibility_object'], indent + 4)
389
+ self.__check_policy_check(operand['compatibility_object']['policy_check'])
390
+
391
+ outbounds.append(operand['compatibility_object']['policy_check'])
392
+
393
+ scored_outbounds, unusable = self.scored_outbounds(outbounds, operator)
394
+
395
+ outbound_list = []
396
+ outbound_list_index = -1
397
+ preferred_inbound_license = None
398
+ compatibility = 'no'
399
+
400
+ if len(scored_outbounds) > 0:
401
+ if operator == 'AND':
402
+ outbound_name = ' AND '.join([x['outbound']['preferences']['license'] for x in scored_outbounds])
403
+ outbound_pref = scored_outbounds[-1]
404
+ else:
405
+ outbound_name = scored_outbounds[0]['outbound']['preferences']['license']
406
+ outbound_pref = scored_outbounds[0]
407
+
408
+ outbound_list = outbound_pref['outbound']['preferences']['license_list']
409
+ outbound_list_index = outbound_pref['outbound']['preferences']['license_index']
410
+ inbound_prefs = outbound_pref['inbound']['preferences']
411
+ preferred_inbound_license = inbound_prefs['license']
412
+ preferred_inbound_license_list = inbound_prefs['license_list']
413
+ preferred_inbound_license_index = inbound_prefs['license_index']
414
+
415
+ compatibility = 'yes'
416
+ else:
417
+ outbound_name = None
418
+ outbound_list = None
419
+ outbound_list_index = None
420
+
421
+ preferred_inbound_license = None
422
+ preferred_inbound_license_list = None
423
+ preferred_inbound_license_index = None
424
+
425
+ compatibility = 'no'
426
+ if 'inbound-expression' in compat_object['compatibility_check']:
427
+ inbound_license_type = 'license-expression'
428
+ else:
429
+ inbound_license_type = 'license'
430
+
431
+ policy_check_inbound_license = self.__pack_policy_check_license(
432
+ compat_object['inbound_license'],
433
+ inbound_license_type,
434
+ preferred_inbound_license,
435
+ preferred_inbound_license_list,
436
+ preferred_inbound_license_index,
437
+ )
438
+ policy_check_outbound_license = self.__pack_policy_check_license(
439
+ compat_object['outbound_license'],
440
+ 'license-expression',
441
+ outbound_name,
442
+ outbound_list,
443
+ outbound_list_index,
444
+ )
445
+ unusable = self.__pack_policy_unusable(unusable)
446
+ policy_license_object = self.__pack_policy_check(
447
+ policy_check_outbound_license,
448
+ policy_check_inbound_license,
449
+ compatibility,
450
+ unusable,
451
+ )
452
+
453
+ compat_object['policy_check'] = policy_license_object
454
+
455
+ elif 'outbound-license' in compat_object['compatibility_check']:
456
+
457
+ if 'inbound-expression' in compat_object['compatibility_check']:
458
+ inner_compat_object = compat_object
459
+ operator = inner_compat_object['operator']
460
+ inbounds = []
461
+ for operand in inner_compat_object['operands']:
462
+ self.__apply_to_compat_object(operand['compatibility_object'], indent + 4)
463
+ self.__check_policy_check(operand['compatibility_object']['policy_check'])
464
+ inbounds.append(operand['compatibility_object']['policy_check'])
465
+
466
+ scored_inbounds, unusable = self.scored_inbounds(inbounds, operator)
467
+ inbound_list = ''
468
+ inbound_list_index = -1
469
+ preferred_inbound_license = None
470
+
471
+ if len(scored_inbounds) > 0:
472
+ if operator == 'AND':
473
+ # use last (least preferred) license
474
+ inbound_list = scored_inbounds[-1]['inbound']['preferences']['license_list']
475
+ inbound_list_index = scored_inbounds[-1]['inbound']['preferences']['license_index']
476
+ preferred_inbound_license = ' AND '.join([x['inbound']['preferences']['license'] for x in scored_inbounds])
477
+ else:
478
+ # use first (most preferred) license
479
+ inbound_list = scored_inbounds[0]['inbound']['preferences']['license_list']
480
+ inbound_list_index = scored_inbounds[0]['inbound']['preferences']['license_index']
481
+ preferred_inbound_license = scored_inbounds[0]['inbound']['preferences']['license']
482
+
483
+ out_lic = inner_compat_object["outbound_license"]
484
+ out_list_nr, out_index = self.policy.list_presence(out_lic, ignore_missing=True)
485
+ out_list_name = self.policy.list_nr_to_name(out_list_nr)
486
+
487
+ policy_check_inbound_license = self.__pack_policy_check_license(
488
+ inner_compat_object['inbound_license'],
489
+ 'license-expression',
490
+ preferred_inbound_license,
491
+ inbound_list,
492
+ inbound_list_index,
493
+ )
494
+ policy_check_outbound_license = self.__pack_policy_check_license(
495
+ out_lic,
496
+ 'license',
497
+ out_lic,
498
+ out_list_name,
499
+ out_index,
500
+ )
501
+ packed_unusable = self.__pack_policy_unusable(unusable)
502
+ policy_license_object = self.__pack_policy_check(
503
+ policy_check_outbound_license,
504
+ policy_check_inbound_license,
505
+ compat_object['compatibility'],
506
+ packed_unusable)
507
+ inner_compat_object['policy_check'] = policy_license_object
508
+
509
+ if 'inbound-license' in compat_object['compatibility_check']:
510
+
511
+ in_lic = compat_object['inbound_license']
512
+ in_list_nr, in_list_index = self.policy.list_presence(in_lic, ignore_missing=True)
513
+ in_list_name = self.policy.list_nr_to_name(in_list_nr)
514
+
515
+ out_lic = compat_object["outbound_license"]
516
+ out_list_nr, out_index = self.policy.list_presence(out_lic, ignore_missing=True)
517
+ out_list_name = self.policy.list_nr_to_name(out_list_nr)
518
+
519
+ policy_check_inbound_license = self.__pack_policy_check_license(
520
+ in_lic,
521
+ 'license',
522
+ in_lic,
523
+ in_list_name,
524
+ in_list_index,
525
+ )
526
+ policy_check_outbound_license = self.__pack_policy_check_license(
527
+ out_lic,
528
+ 'license',
529
+ out_lic,
530
+ out_list_name,
531
+ out_index,
532
+ )
533
+ packed_unusable = self.__pack_policy_unusable(unusable)
534
+ policy_license_object = self.__pack_policy_check(
535
+ policy_check_outbound_license,
536
+ policy_check_inbound_license,
537
+ compat_object['compatibility'],
538
+ packed_unusable,
539
+ )
540
+ compat_object['policy_check'] = policy_license_object
541
+
542
+ else:
543
+ raise Exception("We should not be here")
544
+ return None
545
+
546
+ def apply_policy(self, compat_report, ignore_missing=False):
547
+ if not self.policy:
548
+ logging.debug('apply_policy, no policy. Creating default one')
549
+ resources = compat_report['resources']
550
+ usecase = compat_report['usecase']
551
+ provisioning = compat_report['provisioning']
552
+ self.policy = DefaultLicensePolicy(resources, usecase, provisioning)
553
+ self.policy_type = 'default'
554
+ self.policy_file = None
555
+
556
+ top_object = compat_report['compatibility_report']
557
+ logging.debug("apply_policy")
558
+ self.__apply_to_compat_object(top_object,
559
+ ignore_missing=ignore_missing)
560
+ self.__check_policy_check(top_object['policy_check'])
561
+ meta = compat_report['meta']
562
+ meta['policy_type'] = self.policy_type
563
+ meta['policy_file'] = self.policy_file
564
+ return compat_report
@@ -0,0 +1,12 @@
1
+ # SPDX-FileCopyrightText: 2026 Henrik Sandklef
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ from enum import Enum
6
+
7
+ from licomp.return_codes import ReturnCodes
8
+
9
+ class LicompToolkitReturnCodes(Enum):
10
+ LICOMP_TOOLKIT_INVALID_FILE = ReturnCodes.LICOMP_LAST_ERROR_CODE.value + 1
11
+
12
+ LICOMP_TOOLKIT_LAST_ERROR_CODE = ReturnCodes.LICOMP_LAST_ERROR_CODE.value + 100
@@ -0,0 +1,67 @@
1
+ # SPDX-FileCopyrightText: 2025 Henrik Sandklef
2
+ #
3
+ # SPDX-License-Identifier: GPL-3.0-or-later
4
+
5
+ import json
6
+ import jsonschema
7
+ import logging
8
+ import os
9
+
10
+ from licomp_toolkit.toolkit import LicompToolkit
11
+ from licomp.interface import LicompException
12
+
13
+ SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__))
14
+ DATA_DIR = os.path.realpath(os.path.join(SCRIPT_DIR, "data"))
15
+ SCHEMA_FILE = os.path.realpath(os.path.join(DATA_DIR, "reply_schema.json"))
16
+
17
+ class LicompToolkitSchemaChecker:
18
+
19
+ def __init__(self):
20
+ with open(SCHEMA_FILE) as fp:
21
+ self.expr_expr_schema = json.load(fp)
22
+
23
+ def __validate_deeply(self, compat):
24
+ validations = 0
25
+ lt = LicompToolkit()
26
+
27
+ compat_check = compat['compatibility_check']
28
+ if compat_check == 'outbound-license -> inbound-license':
29
+ compat_object = compat['compatibility_object']
30
+ if not compat_object:
31
+ details = compat['compatibility_details']
32
+ else:
33
+ details = compat_object['compatibility_details']
34
+ compatibilities = details['compatibilities']
35
+ for compatibility_object in compatibilities:
36
+ logging.debug(f' {compatibility_object["resource_name"]}')
37
+ inner_validations = lt.validate(compatibility_object)
38
+ validations += 1
39
+ logging.debug('Validation OK')
40
+ return validations
41
+ else:
42
+
43
+ if compat_check == 'outbound-expression -> inbound-license' or compat_check == 'outbound-expression -> inbound-expression':
44
+ compat_object = compat
45
+ elif compat['compatibility_check'] == 'outbound-license -> inbound-expression':
46
+ compat_object = compat
47
+ else:
48
+ raise LicompException("Validation failed. Invalid state: " + compat_check)
49
+
50
+ for operand in compat_object['operands']:
51
+ operand_compat_object = operand['compatibility_object']
52
+ inner_validations = self.__validate_deeply(operand_compat_object)
53
+ validations += inner_validations
54
+ return validations
55
+
56
+ def validate(self, content, deep=False):
57
+ jsonschema.validate(instance=content,
58
+ schema=self.expr_expr_schema)
59
+ validations = 1
60
+ if deep:
61
+ report = content['compatibility_report']
62
+ validations = self.__validate_deeply(report)
63
+ return validations
64
+
65
+ def validate_file(self, filename, deep=False):
66
+ with open(filename) as fp:
67
+ return self.validate(json.load(fp), deep)