dcicutils 7.11.0.1b9__py3-none-any.whl → 7.12.0.1b4__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of dcicutils might be problematic. Click here for more details.
- dcicutils/glacier_utils.py +16 -4
- dcicutils/license_utils.py +677 -165
- dcicutils/misc_utils.py +9 -97
- dcicutils/scripts/run_license_checker.py +77 -0
- {dcicutils-7.11.0.1b9.dist-info → dcicutils-7.12.0.1b4.dist-info}/METADATA +1 -3
- {dcicutils-7.11.0.1b9.dist-info → dcicutils-7.12.0.1b4.dist-info}/RECORD +9 -9
- {dcicutils-7.11.0.1b9.dist-info → dcicutils-7.12.0.1b4.dist-info}/entry_points.txt +1 -0
- dcicutils/sheet_utils.py +0 -1131
- {dcicutils-7.11.0.1b9.dist-info → dcicutils-7.12.0.1b4.dist-info}/LICENSE.txt +0 -0
- {dcicutils-7.11.0.1b9.dist-info → dcicutils-7.12.0.1b4.dist-info}/WHEEL +0 -0
dcicutils/license_utils.py
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
import contextlib
|
2
|
+
import csv
|
2
3
|
import datetime
|
4
|
+
import glob
|
3
5
|
import io
|
4
6
|
import json
|
5
7
|
# import logging
|
@@ -23,13 +25,18 @@ except ImportError: # pragma: no cover - not worth unit testing this case
|
|
23
25
|
# import piplicenses
|
24
26
|
|
25
27
|
from collections import defaultdict
|
26
|
-
from typing import Any, Dict, DefaultDict, List, Optional, Type, Union
|
28
|
+
from typing import Any, Dict, DefaultDict, List, Optional, Type, TypeVar, Union
|
27
29
|
|
28
30
|
# For obscure reasons related to how this file is used for early prototyping, these must use absolute references
|
29
31
|
# to modules, not relative references. Later when things are better installed, we can make refs relative again.
|
32
|
+
from dcicutils.exceptions import InvalidParameterError
|
30
33
|
from dcicutils.lang_utils import there_are
|
31
|
-
from dcicutils.misc_utils import
|
34
|
+
from dcicutils.misc_utils import (
|
35
|
+
PRINT, get_error_message, ignorable, ignored, json_file_contents, local_attrs, environ_bool,
|
36
|
+
remove_suffix,
|
37
|
+
)
|
32
38
|
|
39
|
+
T = TypeVar("T")
|
33
40
|
|
34
41
|
# logging.basicConfig()
|
35
42
|
# logger = logging.getLogger(__name__)
|
@@ -43,6 +50,14 @@ _NAME = 'name'
|
|
43
50
|
_STATUS = 'status'
|
44
51
|
|
45
52
|
|
53
|
+
def pattern(x):
|
54
|
+
return re.compile(x, re.IGNORECASE)
|
55
|
+
|
56
|
+
|
57
|
+
def augment(d: dict, by: dict):
|
58
|
+
return dict(d, **by)
|
59
|
+
|
60
|
+
|
46
61
|
class LicenseStatus:
|
47
62
|
ALLOWED = "ALLOWED"
|
48
63
|
SPECIALLY_ALLOWED = "SPECIALLY_ALLOWED"
|
@@ -51,6 +66,23 @@ class LicenseStatus:
|
|
51
66
|
UNEXPECTED_MISSING = "UNEXPECTED_MISSING"
|
52
67
|
|
53
68
|
|
69
|
+
class LicenseOptions:
|
70
|
+
# General verbosity, such as progress information
|
71
|
+
VERBOSE = environ_bool("LICENSE_UTILS_VERBOSE", default=True)
|
72
|
+
# Specific additional debugging output
|
73
|
+
DEBUG = environ_bool("LICENSE_UTILS_DEBUG", default=False)
|
74
|
+
CONDA_PREFIX = os.environ.get("CONDA_LICENSE_CHECKER_PREFIX", os.environ.get("CONDA_PREFIX", ""))
|
75
|
+
|
76
|
+
@classmethod
|
77
|
+
@contextlib.contextmanager
|
78
|
+
def selected_options(cls, verbose=VERBOSE, debug=DEBUG, conda_prefix=CONDA_PREFIX):
|
79
|
+
"""
|
80
|
+
Allows a script, for example, to specify overrides for these options dynamically.
|
81
|
+
"""
|
82
|
+
with local_attrs(cls, VERBOSE=verbose, DEBUG=debug, CONDA_PREFIX=conda_prefix):
|
83
|
+
yield
|
84
|
+
|
85
|
+
|
54
86
|
class LicenseFramework:
|
55
87
|
|
56
88
|
NAME = None
|
@@ -87,13 +119,13 @@ class LicenseFrameworkRegistry:
|
|
87
119
|
yield
|
88
120
|
|
89
121
|
@classmethod
|
90
|
-
def
|
122
|
+
def register_framework(cls, *, name):
|
91
123
|
"""
|
92
124
|
Declares a python license framework classs.
|
93
125
|
Mostly these names will be language names like 'python' or 'javascript',
|
94
126
|
but they might be names of other, non-linguistic frameworks (like 'cgap-pipeline', for example).
|
95
127
|
"""
|
96
|
-
def _decorator(framework_class):
|
128
|
+
def _decorator(framework_class: T) -> T:
|
97
129
|
if not issubclass(framework_class, LicenseFramework):
|
98
130
|
raise ValueError(f"The class {framework_class.__name__} does not inherit from LicenseFramework.")
|
99
131
|
framework_class.NAME = name
|
@@ -117,25 +149,108 @@ class LicenseFrameworkRegistry:
|
|
117
149
|
return sorted(cls.LICENSE_FRAMEWORKS.values(), key=lambda x: x.NAME)
|
118
150
|
|
119
151
|
|
120
|
-
|
152
|
+
# This is intended to match ' (= 3)', ' (>= 3)', ' (version 3)', ' (version 3 or greater)'
|
153
|
+
# It will incidentally and harmlessly also take ' (>version 3)' or '(>= 3 or greater)'.
|
154
|
+
# It will also correctly handle the unlikely case of ' (= 3 or greater)'
|
155
|
+
|
156
|
+
_OR_LATER_PATTERN = '(?:[- ]or[ -](?:greater|later))'
|
157
|
+
_PARENTHETICAL_VERSION_CONSTRAINT = re.compile(f'( [(]([>]?)(?:[=]|version) ([0-9.]+)({_OR_LATER_PATTERN}?)[)])')
|
158
|
+
_POSTFIX_OR_LATER_PATTERN = re.compile(f"({_OR_LATER_PATTERN})")
|
159
|
+
_GPL_VERSION_CHOICE = re.compile('^GPL-v?([0-9.+]) (?:OR|[|]) GPL-v?([0-9.+])$')
|
160
|
+
|
161
|
+
|
162
|
+
def simplify_license_versions(licenses_spec: str, *, for_package_name) -> str:
|
163
|
+
m = _GPL_VERSION_CHOICE.match(licenses_spec)
|
164
|
+
if m:
|
165
|
+
version_a, version_b = m.groups()
|
166
|
+
return f"GPL-{version_a}-or-{version_b}"
|
167
|
+
# We only care which licenses were mentioned, not what algebra is used on them.
|
168
|
+
# (Thankfully there are no NOTs, and that's probably not by accident, since that would be too big a set.)
|
169
|
+
# So for us, either (FOO AND BAR) or (FOO OR BAR) is the same because we want to treat it as "FOO,BAR".
|
170
|
+
# If all of those licenses match, all is good. That _does_ mean some things like (MIT OR GPL-3.0) will
|
171
|
+
# have trouble passing unless both MIT and GPL-3.0 are allowed.
|
172
|
+
transform_count = 0
|
173
|
+
original_licenses_spec = licenses_spec
|
174
|
+
ignorable(original_licenses_spec) # sometimes useful for debugging
|
175
|
+
while True:
|
176
|
+
if transform_count > 100: # It'd be surprising if there were even ten of these to convert.
|
177
|
+
warnings.warn(f"Transforming {for_package_name} {licenses_spec!r} seemed to be looping."
|
178
|
+
f" Please report this as a bug.")
|
179
|
+
return licenses_spec # return the unmodified
|
180
|
+
transform_count += 1
|
181
|
+
m = _PARENTHETICAL_VERSION_CONSTRAINT.search(licenses_spec)
|
182
|
+
if not m:
|
183
|
+
break
|
184
|
+
matched, greater, version_spec, greater2 = m.groups()
|
185
|
+
is_greater = bool(greater or greater2)
|
186
|
+
licenses_spec = licenses_spec.replace(matched,
|
187
|
+
f"-{version_spec}"
|
188
|
+
f"{'+' if is_greater else ''}")
|
189
|
+
transform_count = 0
|
190
|
+
while True:
|
191
|
+
if transform_count > 100: # It'd be surprising if there were even ten of these to convert.
|
192
|
+
warnings.warn(f"Transforming {for_package_name} {licenses_spec!r} seemed to be looping."
|
193
|
+
f" Please report this as a bug.")
|
194
|
+
return licenses_spec # return the unmodified
|
195
|
+
transform_count += 1
|
196
|
+
m = _POSTFIX_OR_LATER_PATTERN.search(licenses_spec)
|
197
|
+
if not m:
|
198
|
+
break
|
199
|
+
matched = m.group(1)
|
200
|
+
licenses_spec = licenses_spec.replace(matched, '+')
|
201
|
+
if LicenseOptions.DEBUG and licenses_spec != original_licenses_spec:
|
202
|
+
PRINT(f"Rewriting {original_licenses_spec!r} as {licenses_spec!r}.")
|
203
|
+
return licenses_spec
|
204
|
+
|
205
|
+
|
206
|
+
def extract_boolean_terms(boolean_expression: str, for_package_name: str) -> List[str]:
|
207
|
+
# We only care which licenses were mentioned, not what algebra is used on them.
|
208
|
+
# (Thankfully there are no NOTs, and that's probably not by accident, since that would be too big a set.)
|
209
|
+
# So for us, either (FOO AND BAR) or (FOO OR BAR) is the same because we want to treat it as "FOO,BAR".
|
210
|
+
# If all of those licenses match, all is good. That _does_ mean some things like (MIT OR GPL-3.0) will
|
211
|
+
# have trouble passing unless both MIT and GPL-3.0 are allowed.
|
212
|
+
revised_boolean_expression = (
|
213
|
+
boolean_expression
|
214
|
+
.replace('(', '')
|
215
|
+
.replace(')', '')
|
216
|
+
.replace(' AND ', ',')
|
217
|
+
.replace(' and ', ',')
|
218
|
+
.replace(' & ', ',')
|
219
|
+
.replace(' OR ', ',')
|
220
|
+
.replace(' or ', ',')
|
221
|
+
.replace('|', ',')
|
222
|
+
.replace(';', ',')
|
223
|
+
.replace(' + ', ',')
|
224
|
+
.replace('file ', f'Custom: {for_package_name} file ')
|
225
|
+
)
|
226
|
+
terms = [x for x in sorted(map(lambda x: x.strip(), revised_boolean_expression.split(','))) if x]
|
227
|
+
if LicenseOptions.DEBUG and revised_boolean_expression != boolean_expression:
|
228
|
+
PRINT(f"Rewriting {boolean_expression!r} as {terms!r}.")
|
229
|
+
return terms
|
230
|
+
|
231
|
+
|
232
|
+
@LicenseFrameworkRegistry.register_framework(name='javascript')
|
121
233
|
class JavascriptLicenseFramework(LicenseFramework):
|
122
234
|
|
123
235
|
@classmethod
|
124
|
-
def implicated_licenses(cls, *, licenses_spec: str):
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
# If all of those licenses match, all is good. That _does_ mean some things like (MIT OR GPL-3.0) will
|
129
|
-
# have trouble passing unless both MIT and GPL-3.0 are allowed.
|
130
|
-
licenses = sorted(map(lambda x: x.strip(),
|
131
|
-
(licenses_spec
|
132
|
-
.replace('(', '')
|
133
|
-
.replace(')', '')
|
134
|
-
.replace(' AND ', ',')
|
135
|
-
.replace(' OR ', ',')
|
136
|
-
).split(',')))
|
236
|
+
def implicated_licenses(cls, *, package_name, licenses_spec: str) -> List[str]:
|
237
|
+
ignored(package_name)
|
238
|
+
licenses_spec = simplify_license_versions(licenses_spec, for_package_name=package_name)
|
239
|
+
licenses = extract_boolean_terms(licenses_spec, for_package_name=package_name)
|
137
240
|
return licenses
|
138
241
|
|
242
|
+
VERSION_PATTERN = re.compile('^.+?([@][0-9.][^@]*|)$')
|
243
|
+
|
244
|
+
@classmethod
|
245
|
+
def strip_version(cls, raw_name):
|
246
|
+
name = raw_name
|
247
|
+
m = cls.VERSION_PATTERN.match(raw_name) # e.g., @foo/bar@3.7
|
248
|
+
if m:
|
249
|
+
suffix = m.group(1)
|
250
|
+
if suffix:
|
251
|
+
name = remove_suffix(m.group(1), name)
|
252
|
+
return name
|
253
|
+
|
139
254
|
@classmethod
|
140
255
|
def get_dependencies(cls):
|
141
256
|
output = subprocess.check_output(['npx', 'license-checker', '--summary', '--json'],
|
@@ -147,24 +262,20 @@ class JavascriptLicenseFramework(LicenseFramework):
|
|
147
262
|
# e.g., this happens if there's no javascript in the repo
|
148
263
|
raise Exception("No javascript license data was found.")
|
149
264
|
result = []
|
150
|
-
for
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
PRINT(f"Rewriting {licenses_spec!r} as {licenses!r}")
|
155
|
-
elif licenses_spec:
|
156
|
-
licenses = [licenses_spec]
|
157
|
-
else:
|
158
|
-
licenses = []
|
265
|
+
for raw_name, record in records.items():
|
266
|
+
name = cls.strip_version(raw_name)
|
267
|
+
raw_licenses_spec = record.get(_LICENSES)
|
268
|
+
licenses = cls.implicated_licenses(licenses_spec=raw_licenses_spec, package_name=name)
|
159
269
|
entry = {
|
160
|
-
_NAME: name
|
161
|
-
_LICENSES: licenses
|
270
|
+
_NAME: name,
|
271
|
+
_LICENSES: licenses,
|
272
|
+
_FRAMEWORK: 'javascript'
|
162
273
|
}
|
163
274
|
result.append(entry)
|
164
275
|
return result
|
165
276
|
|
166
277
|
|
167
|
-
@LicenseFrameworkRegistry.
|
278
|
+
@LicenseFrameworkRegistry.register_framework(name='python')
|
168
279
|
class PythonLicenseFramework(LicenseFramework):
|
169
280
|
|
170
281
|
@classmethod
|
@@ -184,15 +295,107 @@ class PythonLicenseFramework(LicenseFramework):
|
|
184
295
|
entry = {
|
185
296
|
_NAME: license_name,
|
186
297
|
_LICENSES: licenses,
|
187
|
-
|
298
|
+
_FRAMEWORK: 'python',
|
188
299
|
}
|
189
300
|
result.append(entry)
|
190
301
|
return sorted(result, key=lambda x: x.get(_NAME).lower())
|
191
302
|
|
192
303
|
|
193
|
-
|
304
|
+
@LicenseFrameworkRegistry.register_framework(name='conda')
|
305
|
+
class CondaLicenseFramework(LicenseFramework):
|
306
|
+
|
307
|
+
@classmethod
|
308
|
+
def get_dependencies(cls):
|
309
|
+
prefix = LicenseOptions.CONDA_PREFIX
|
310
|
+
result = []
|
311
|
+
filespec = os.path.join(prefix, "conda-meta/*.json")
|
312
|
+
files = glob.glob(filespec)
|
313
|
+
for file in files:
|
314
|
+
data = json_file_contents(file)
|
315
|
+
package_name = data['name']
|
316
|
+
package_license = data.get('license') or "MISSING"
|
317
|
+
if package_license:
|
318
|
+
# print(f"package_license={package_license}")
|
319
|
+
simplified_package_license_spec = simplify_license_versions(package_license,
|
320
|
+
for_package_name=package_name)
|
321
|
+
# print(f" =simplified_package_license_spec => {simplified_package_license_spec}")
|
322
|
+
package_licenses = extract_boolean_terms(simplified_package_license_spec,
|
323
|
+
for_package_name=package_name)
|
324
|
+
# print(f"=> {package_licenses}")
|
325
|
+
else:
|
326
|
+
package_licenses = []
|
327
|
+
entry = {
|
328
|
+
_NAME: package_name,
|
329
|
+
_LICENSES: package_licenses,
|
330
|
+
_FRAMEWORK: 'conda',
|
331
|
+
}
|
332
|
+
result.append(entry)
|
333
|
+
result.sort(key=lambda x: x['name'])
|
334
|
+
# print(f"conda get_dependencies result={json.dumps(result, indent=2)}")
|
335
|
+
# print("conda deps = ", json.dumps(result, indent=2))
|
336
|
+
return result
|
337
|
+
|
338
|
+
|
339
|
+
@LicenseFrameworkRegistry.register_framework(name='r')
|
340
|
+
class RLicenseFramework(LicenseFramework):
|
341
|
+
|
342
|
+
R_PART_SPEC = re.compile("^Part of R [0-9.]+$")
|
343
|
+
R_LANGUAGE_LICENSE_NAME = 'R-language-license'
|
344
|
+
|
345
|
+
@classmethod
|
346
|
+
def implicated_licenses(cls, *, package_name, licenses_spec: str) -> List[str]:
|
347
|
+
if cls.R_PART_SPEC.match(licenses_spec):
|
348
|
+
return [cls.R_LANGUAGE_LICENSE_NAME]
|
349
|
+
licenses_spec = simplify_license_versions(licenses_spec, for_package_name=package_name)
|
350
|
+
licenses = extract_boolean_terms(licenses_spec, for_package_name=package_name)
|
351
|
+
return licenses
|
352
|
+
|
353
|
+
@classmethod
|
354
|
+
def get_dependencies(cls):
|
355
|
+
# NOTE: Although the R Language itself is released under the GPL, our belief is that it is
|
356
|
+
# still possible to write programs in R that are not GPL, even programs that use commercial licenses.
|
357
|
+
# So we do ordinary license checking here, same as in other frameworks.
|
358
|
+
# For notes on this, see the R FAQ.
|
359
|
+
# Ref: https://cran.r-project.org/doc/FAQ/R-FAQ.html#Can-I-use-R-for-commercial-purposes_003f
|
360
|
+
|
361
|
+
_PACKAGE = "Package"
|
362
|
+
_LICENSE = "License"
|
363
|
+
|
364
|
+
found_problems = 0
|
365
|
+
|
366
|
+
output_bytes = subprocess.check_output(['r', '--no-echo', '-q', '-e',
|
367
|
+
f'write.csv(installed.packages()[,c("Package", "License")])'],
|
368
|
+
# This will output to stderr if there's an error,
|
369
|
+
# but it will still put {} on stdout, which is good enough for us.
|
370
|
+
stderr=subprocess.DEVNULL)
|
371
|
+
output = output_bytes.decode('utf-8')
|
372
|
+
result = []
|
373
|
+
first_line = True
|
374
|
+
for entry in csv.reader(io.StringIO(output)): # [ignore, package, license]
|
375
|
+
if first_line:
|
376
|
+
first_line = False
|
377
|
+
if entry == ["", _PACKAGE, _LICENSE]: # we expect headers
|
378
|
+
continue
|
379
|
+
try:
|
380
|
+
package_name = entry[1]
|
381
|
+
licenses_spec = entry[2]
|
382
|
+
licenses = cls.implicated_licenses(package_name=package_name, licenses_spec=licenses_spec)
|
383
|
+
entry = {
|
384
|
+
_NAME: package_name,
|
385
|
+
_LICENSES: licenses,
|
386
|
+
_FRAMEWORK: 'r',
|
387
|
+
}
|
388
|
+
result.append(entry)
|
389
|
+
except Exception as e:
|
390
|
+
found_problems += 1
|
391
|
+
if LicenseOptions.VERBOSE:
|
392
|
+
PRINT(get_error_message(e))
|
393
|
+
if found_problems > 0:
|
394
|
+
warnings.warn(there_are(found_problems, kind="problem", show=False, punctuate=True, tense='past'))
|
395
|
+
return sorted(result, key=lambda x: x.get(_NAME).lower())
|
194
396
|
|
195
|
-
|
397
|
+
|
398
|
+
class LicenseFileParser:
|
196
399
|
|
197
400
|
SEPARATORS = '-.,'
|
198
401
|
SEPARATORS_AND_WHITESPACE = SEPARATORS + ' \t'
|
@@ -230,8 +433,6 @@ class LicenseFileParser:
|
|
230
433
|
lines = []
|
231
434
|
for i, line in enumerate(fp):
|
232
435
|
line = line.strip(' \t\n\r')
|
233
|
-
if cls.VERBOSE: # pragma: no cover - this is just for debugging
|
234
|
-
PRINT(str(i).rjust(3), line)
|
235
436
|
m = cls.COPYRIGHT_LINE.match(line) if line[:1].isupper() else None
|
236
437
|
if not m:
|
237
438
|
lines.append(line)
|
@@ -316,14 +517,12 @@ class LicenseChecker:
|
|
316
517
|
Note that if you don't like these license names, which are admittedly non-standard and do nt seem to use
|
317
518
|
SPDX naming conventions, you can customize the get_dependencies method to return a different
|
318
519
|
list, one of the form
|
319
|
-
[{"name": "libname", "license_classifier": ["license1", "license2", ...], "
|
520
|
+
[{"name": "libname", "license_classifier": ["license1", "license2", ...], "framework": "python"}]
|
320
521
|
by whatever means you like and using whatever names you like.
|
321
522
|
"""
|
322
523
|
|
323
524
|
# Set this to True in subclasses if you want your organization's policy to be that you see
|
324
525
|
# some visible proof of which licenses were checked.
|
325
|
-
VERBOSE = True
|
326
|
-
|
327
526
|
LICENSE_TITLE = None
|
328
527
|
COPYRIGHT_OWNER = None
|
329
528
|
LICENSE_FRAMEWORKS = None
|
@@ -378,6 +577,22 @@ class LicenseChecker:
|
|
378
577
|
check_license_title=license_title or cls.LICENSE_TITLE,
|
379
578
|
analysis=analysis)
|
380
579
|
|
580
|
+
CHOICE_REGEXPS = {}
|
581
|
+
|
582
|
+
@classmethod
|
583
|
+
def _make_regexp_for_choices(cls, choices):
|
584
|
+
inner_pattern = '|'.join('^' + (re.escape(choice) if isinstance(choice, str) else choice.pattern) + '$'
|
585
|
+
for choice in choices) or "^$"
|
586
|
+
return re.compile(f"({inner_pattern})", re.IGNORECASE)
|
587
|
+
|
588
|
+
@classmethod
|
589
|
+
def _find_regexp_for_choices(cls, choices):
|
590
|
+
key = str(choices)
|
591
|
+
regexp = cls.CHOICE_REGEXPS.get(key)
|
592
|
+
if not regexp:
|
593
|
+
cls.CHOICE_REGEXPS[key] = regexp = cls._make_regexp_for_choices(choices)
|
594
|
+
return regexp
|
595
|
+
|
381
596
|
@classmethod
|
382
597
|
def analyze_license_dependencies_for_framework(cls, *,
|
383
598
|
analysis: LicenseAnalysis,
|
@@ -385,7 +600,7 @@ class LicenseChecker:
|
|
385
600
|
acceptable: Optional[List[str]] = None,
|
386
601
|
exceptions: Optional[Dict[str, str]] = None,
|
387
602
|
) -> None:
|
388
|
-
|
603
|
+
acceptability_regexp = cls._find_regexp_for_choices((acceptable or []) + (cls.ALLOWED or []))
|
389
604
|
exceptions = dict(cls.EXCEPTIONS or {}, **(exceptions or {}))
|
390
605
|
|
391
606
|
try:
|
@@ -415,7 +630,7 @@ class LicenseChecker:
|
|
415
630
|
by_special_exception = False
|
416
631
|
for license_name in license_names:
|
417
632
|
special_exceptions = exceptions.get(license_name, [])
|
418
|
-
if license_name in acceptable:
|
633
|
+
if acceptability_regexp.match(license_name): # license_name in acceptable:
|
419
634
|
pass
|
420
635
|
elif name in special_exceptions:
|
421
636
|
by_special_exception = True
|
@@ -430,7 +645,7 @@ class LicenseChecker:
|
|
430
645
|
_LICENSES: license_names,
|
431
646
|
_STATUS: status
|
432
647
|
})
|
433
|
-
if
|
648
|
+
if LicenseOptions.VERBOSE: # pragma: no cover - this is just for debugging
|
434
649
|
PRINT(f"Checked {framework.NAME} {name}:"
|
435
650
|
f" {'; '.join(license_names) if license_names else '---'} ({status})")
|
436
651
|
|
@@ -459,7 +674,7 @@ class LicenseChecker:
|
|
459
674
|
def show_unacceptable_licenses(cls, *, analysis: LicenseAnalysis) -> LicenseAnalysis:
|
460
675
|
if analysis.unacceptable:
|
461
676
|
PRINT(there_are(analysis.unacceptable, kind="unacceptable license", show=False, punctuation_mark=':'))
|
462
|
-
for license, names in analysis.unacceptable.items():
|
677
|
+
for license, names in sorted(analysis.unacceptable.items()):
|
463
678
|
PRINT(f" {license}: {', '.join(names)}")
|
464
679
|
return analysis
|
465
680
|
|
@@ -499,6 +714,30 @@ class LicenseChecker:
|
|
499
714
|
raise LicenseAcceptabilityCheckFailure(unacceptable_licenses=analysis.unacceptable)
|
500
715
|
|
501
716
|
|
717
|
+
class LicenseCheckerRegistry:
|
718
|
+
|
719
|
+
REGISTRY: Dict[str, Type[LicenseChecker]] = {}
|
720
|
+
|
721
|
+
@classmethod
|
722
|
+
def register_checker(cls, name: str):
|
723
|
+
def _register(license_checker_class: Type[LicenseChecker]):
|
724
|
+
cls.REGISTRY[name] = license_checker_class
|
725
|
+
return license_checker_class
|
726
|
+
return _register
|
727
|
+
|
728
|
+
@classmethod
|
729
|
+
def lookup_checker(cls, name: str) -> Type[LicenseChecker]:
|
730
|
+
result: Optional[Type[LicenseChecker]] = cls.REGISTRY.get(name)
|
731
|
+
if result is None:
|
732
|
+
raise InvalidParameterError(parameter='checker_name', value=name,
|
733
|
+
options=cls.all_checker_names())
|
734
|
+
return result
|
735
|
+
|
736
|
+
@classmethod
|
737
|
+
def all_checker_names(cls):
|
738
|
+
return list(cls.REGISTRY.keys())
|
739
|
+
|
740
|
+
|
502
741
|
class LicenseCheckFailure(Exception):
|
503
742
|
|
504
743
|
DEFAULT_MESSAGE = "License check failure."
|
@@ -523,16 +762,13 @@ class LicenseAcceptabilityCheckFailure(LicenseCheckFailure):
|
|
523
762
|
super().__init__(message=message)
|
524
763
|
|
525
764
|
|
526
|
-
|
765
|
+
@LicenseCheckerRegistry.register_checker('park-lab-common')
|
766
|
+
class ParkLabCommonLicenseChecker(LicenseChecker):
|
527
767
|
"""
|
528
|
-
|
529
|
-
If you're at some other organization, we recommend you make a class that has values
|
530
|
-
suitable to your own organizational needs.
|
768
|
+
Minimal checker common to all tech from Park Lab.
|
531
769
|
"""
|
532
770
|
|
533
771
|
COPYRIGHT_OWNER = "President and Fellows of Harvard College"
|
534
|
-
LICENSE_TITLE = "(The )?MIT License"
|
535
|
-
LICENSE_FRAMEWORKS = ['python', 'javascript']
|
536
772
|
|
537
773
|
ALLOWED = [
|
538
774
|
|
@@ -548,16 +784,39 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
548
784
|
'AFL-2.1',
|
549
785
|
|
550
786
|
# Linking = Permissive, Private Use = Yes
|
787
|
+
# Apache licenses before version 2.0 are controversial, but we here construe an unmarked naming to imply
|
788
|
+
# any version, and hence v2.
|
551
789
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
552
790
|
'Apache Software License',
|
553
791
|
'Apache-Style',
|
554
|
-
|
792
|
+
pattern("Apache([- ]2([.]0)?)?([- ]Licen[cs]e)?([- ]with[- ]LLVM[- ]exception)?"),
|
793
|
+
# 'Apache-2.0',
|
794
|
+
|
795
|
+
# Artistic License 1.0 was confusing to people, so its status as permissive is in general uncertain,
|
796
|
+
# however the issue seems to revolve around point 8 (relating to whether or not perl is deliberately
|
797
|
+
# exposed). That isn't in play for our uses, so we don't flag it here.
|
798
|
+
# Artistic license 2.0 is a permissive license.
|
799
|
+
# Ref: https://en.wikipedia.org/wiki/Artistic_License
|
800
|
+
'Artistic-1.0-Perl',
|
801
|
+
pattern('Artistic[- ]2([.]0)?'),
|
802
|
+
|
803
|
+
# According to Wikipedia, the Boost is considered permissive and BSD-like.
|
804
|
+
# Refs:
|
805
|
+
# *
|
806
|
+
# * https://en.wikipedia.org/wiki/Boost_(C%2B%2B_libraries)#License
|
807
|
+
pattern('(BSL|Boost(([- ]Software)?[- ]License)?)([- ]1([.]0)?)?'),
|
555
808
|
|
556
809
|
# Linking = Permissive, Private Use = Yes
|
557
810
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
558
|
-
'BSD
|
559
|
-
'BSD
|
560
|
-
'BSD-
|
811
|
+
pattern('((modified[- ])?[234][- ]Clause[- ])?BSD([- ][234][- ]Clause)?( Licen[cs]e)?'),
|
812
|
+
# 'BSD License',
|
813
|
+
# 'BSD-2-Clause',
|
814
|
+
# 'BSD-3-Clause',
|
815
|
+
# 'BSD 3-Clause',
|
816
|
+
|
817
|
+
# BZIP2 is a permissive license
|
818
|
+
# Ref: https://github.com/asimonov-im/bzip2/blob/master/LICENSE
|
819
|
+
pattern('bzip2(-1[.0-9]*)'),
|
561
820
|
|
562
821
|
# Linking = Public Domain, Private Use = Public Domain
|
563
822
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
@@ -570,6 +829,10 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
570
829
|
'CC-BY-3.0',
|
571
830
|
'CC-BY-4.0',
|
572
831
|
|
832
|
+
# The curl license is a permissive license.
|
833
|
+
# Ref: https://curl.se/docs/copyright.html
|
834
|
+
'curl',
|
835
|
+
|
573
836
|
# Linking = Permissive, Private Use = ?
|
574
837
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
575
838
|
'CDDL',
|
@@ -583,9 +846,32 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
583
846
|
'Eclipse Public License',
|
584
847
|
'EPL-2.0',
|
585
848
|
|
849
|
+
# The FSF Unlimited License (FSFUL) seems to be a completely permissive license.
|
850
|
+
# Refs:
|
851
|
+
# * https://spdx.org/licenses/FSFUL.html
|
852
|
+
# * https://fedoraproject.org/wiki/Licensing/FSF_Unlimited_License
|
853
|
+
'FSF Unlimited License',
|
854
|
+
'FSFUL',
|
855
|
+
|
856
|
+
# The FreeType license is a permissive license.
|
857
|
+
# Ref: LicenseRef-FreeType
|
858
|
+
pattern('(Licen[cs]eRef-)?(FTL|FreeType( Licen[cs]e)?)'),
|
859
|
+
|
586
860
|
# Linking = Yes, Cat = Permissive Software Licenses
|
587
861
|
# Ref: https://en.wikipedia.org/wiki/Historical_Permission_Notice_and_Disclaimer
|
588
862
|
'Historical Permission Notice and Disclaimer (HPND)',
|
863
|
+
'HPND',
|
864
|
+
pattern('(Licen[cs]eRef-)?PIL'),
|
865
|
+
# The Pillow or Python Image Library is an HPND license, which is a simple permissive license:
|
866
|
+
# Refs:
|
867
|
+
# * https://github.com/python-pillow/Pillow/blob/main/LICENSE
|
868
|
+
# * https://www.fsf.org/blogs/licensing/historical-permission-notice-and-disclaimer-added-to-license-list
|
869
|
+
|
870
|
+
# The IJG license, used by Independent JPEG Group (IJG) is a custom permissive license.
|
871
|
+
# Refs:
|
872
|
+
# * https://en.wikipedia.org/wiki/Libjpeg
|
873
|
+
# * https://github.com/libjpeg-turbo/libjpeg-turbo/blob/main/LICENSE.md
|
874
|
+
'IJG',
|
589
875
|
|
590
876
|
# Linking = Permissive, Private Use = Permissive
|
591
877
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
@@ -610,10 +896,11 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
610
896
|
'OFL-1.1',
|
611
897
|
|
612
898
|
# Ref: https://en.wikipedia.org/wiki/Public_domain
|
613
|
-
'Public Domain',
|
899
|
+
pattern('(Licen[cs]eRef-)?Public[- ]Domain([- ]dedic[t]?ation)?'), # "dedictation" is a typo in docutils
|
614
900
|
|
615
901
|
# Linking = Permissive, Private Use = Permissive
|
616
902
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
903
|
+
pattern('(Licen[cs]eRef-)?PSF-2([.][.0-9]*)'),
|
617
904
|
'Python Software Foundation License',
|
618
905
|
'Python-2.0',
|
619
906
|
|
@@ -621,11 +908,32 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
621
908
|
# Ref: https://en.wikipedia.org/wiki/Pylons_project
|
622
909
|
'Repoze Public License',
|
623
910
|
|
911
|
+
# The TCL or Tcl/Tk licenses are permissive licenses.
|
912
|
+
# Ref: https://www.tcl.tk/software/tcltk/license.html
|
913
|
+
# The one used by the tktable library has a 'bourbon' clause that doesn't add compliance requirements
|
914
|
+
# Ref: https://github.com/wjoye/tktable/blob/master/license.txt
|
915
|
+
pattern('Tcl([/]tk)?'),
|
916
|
+
|
917
|
+
# The Ubuntu Font Licence is mostly permissive. It contains some restrictions if you are going to modify the
|
918
|
+
# fonts that require you to change the name to avoid confusion. But for our purposes, we're assuming that's
|
919
|
+
# not done, and so we're not flagging it.
|
920
|
+
pattern('Ubuntu Font Licen[cs]e Version( 1([.]0)?)?'),
|
921
|
+
|
624
922
|
# Linking = Permissive/Public domain, Private Use = Permissive/Public domain
|
625
923
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
626
924
|
'The Unlicense (Unlicense)',
|
627
925
|
'Unlicense',
|
628
926
|
|
927
|
+
# Various licenses seem to call themselves or be summed up as unlimited.
|
928
|
+
# So far we know of none that are not highly permissive.
|
929
|
+
# * boot and KernSmooth are reported by R as being 'Unlimited'
|
930
|
+
# Refs:
|
931
|
+
# * https://cran.r-project.org/web/packages/KernSmooth/index.html
|
932
|
+
# (https://github.com/cran/KernSmooth/blob/master/LICENCE.note)
|
933
|
+
# * https://cran.r-project.org/package=boot
|
934
|
+
# (https://github.com/cran/boot/blob/master/DESCRIPTION)
|
935
|
+
'Unlimited',
|
936
|
+
|
629
937
|
# Linking = Permissive, Private Use = ?
|
630
938
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
631
939
|
'W3C License',
|
@@ -646,6 +954,109 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
646
954
|
'Zope Public License',
|
647
955
|
]
|
648
956
|
|
957
|
+
EXCEPTIONS = {
|
958
|
+
|
959
|
+
# The Bioconductor zlibbioc license is a permissive license.
|
960
|
+
# Ref: https://github.com/Bioconductor/zlibbioc/blob/devel/LICENSE
|
961
|
+
'Custom: bioconductor-zlibbioc file LICENSE': [
|
962
|
+
'bioconductor-zlibbioc'
|
963
|
+
],
|
964
|
+
|
965
|
+
# The Bioconductor rsamtools license is an MIT license
|
966
|
+
# Ref: https://bioconductor.org/packages/release/bioc/licenses/Rsamtools/LICENSE
|
967
|
+
'Custom: bioconductor-rsamtools file LICENSE': [
|
968
|
+
'bioconductor-rsamtools'
|
969
|
+
],
|
970
|
+
|
971
|
+
# DFSG = Debian Free Software Guidelines
|
972
|
+
# Ref: https://en.wikipedia.org/wiki/Debian_Free_Software_Guidelines
|
973
|
+
# Used as an apparent modifier to other licenses, to say they are approved per Debian.
|
974
|
+
# For example in this case, pytest-timeout has license: DFSG approved, MIT License,
|
975
|
+
# but is really just an MIT License that someone has checked is DFSG approved.
|
976
|
+
'DFSG approved': [
|
977
|
+
'pytest-timeout', # MIT Licensed
|
978
|
+
],
|
979
|
+
|
980
|
+
'FOSS': [
|
981
|
+
# The r-stringi library is a conda library that implements a stringi (pronounced "stringy") library for R.
|
982
|
+
# The COnda source feed is: https://github.com/conda-forge/r-stringi-feedstock
|
983
|
+
# This page explains that the home source is https://stringi.gagolewski.com/ but that's a doc page.
|
984
|
+
# The doc page says:
|
985
|
+
# > stringi’s source code is hosted on GitHub.
|
986
|
+
# > It is distributed under the open source BSD-3-clause license.
|
987
|
+
# The source code has a license that begins with a BSD-3-clause license and includes numerous others,
|
988
|
+
# but they all appear to be permissive.
|
989
|
+
# Ref: https://github.com/gagolews/stringi/blob/master/LICENSE
|
990
|
+
'stringi', 'r-stringi',
|
991
|
+
],
|
992
|
+
|
993
|
+
# Linking = With Restrictions, Private Use = Yes
|
994
|
+
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
995
|
+
'GNU Lesser General Public License v2 or later (LGPLv2+)': [
|
996
|
+
'chardet' # used at runtime during server operation (ingestion), but not modified or distributed
|
997
|
+
],
|
998
|
+
|
999
|
+
# Linking = With Restrictions, Private Use = Yes
|
1000
|
+
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
1001
|
+
'GNU Lesser General Public License v3 or later (LGPLv3+)': [
|
1002
|
+
# used only privately in testing, not used in server code, not modified, not distributed
|
1003
|
+
'pytest-redis',
|
1004
|
+
# required by pytest-redis (used only where it's used)
|
1005
|
+
'mirakuru',
|
1006
|
+
],
|
1007
|
+
|
1008
|
+
'GNU General Public License (GPL)': [
|
1009
|
+
'docutils', # Used only privately as a separate documentation-generation task for ReadTheDocs
|
1010
|
+
],
|
1011
|
+
|
1012
|
+
'MIT/X11 Derivative': [
|
1013
|
+
# The license used by libxkbcommon is complicated and involves numerous included licenses,
|
1014
|
+
# but all are permissive.
|
1015
|
+
# Ref: https://github.com/xkbcommon/libxkbcommon/blob/master/LICENSE
|
1016
|
+
'libxkbcommon',
|
1017
|
+
],
|
1018
|
+
|
1019
|
+
'None': [
|
1020
|
+
# It's not obvious why Conda shows this license as 'None'.
|
1021
|
+
# In fact, though, BSD 3-Clause "New" or "Revised" License
|
1022
|
+
# Ref: https://github.com/AnacondaRecipes/_libgcc_mutex-feedstock/blob/master/LICENSE.txt
|
1023
|
+
'_libgcc_mutex',
|
1024
|
+
],
|
1025
|
+
|
1026
|
+
'PostgreSQL': [
|
1027
|
+
# The libpq library is actually licensed with a permissive BSD 3-Clause "New" or "Revised" License
|
1028
|
+
# Ref: https://github.com/lpsmith/postgresql-libpq/blob/master/LICENSE
|
1029
|
+
'libpq',
|
1030
|
+
],
|
1031
|
+
|
1032
|
+
'UCSD': [
|
1033
|
+
# It isn't obvious why these show up with a UCSD license in Conda.
|
1034
|
+
# The actual sources say it should be a 2-clause BSD license:
|
1035
|
+
# Refs:
|
1036
|
+
# * https://github.com/AlexandrovLab/SigProfilerMatrixGenerator/blob/master/LICENSE
|
1037
|
+
# * https://github.com/AlexandrovLab/SigProfilerPlotting/blob/master/LICENSE
|
1038
|
+
'sigprofilermatrixgenerator',
|
1039
|
+
'sigprofilerplotting',
|
1040
|
+
],
|
1041
|
+
|
1042
|
+
'X11': [
|
1043
|
+
# The ncurses library has a VERY complicated history, BUT seems consistently permissive
|
1044
|
+
# and the most recent version seems to be essentially the MIT license.
|
1045
|
+
# Refs:
|
1046
|
+
# * https://en.wikipedia.org/wiki/Ncurses#License
|
1047
|
+
# * https://invisible-island.net/ncurses/ncurses-license.html
|
1048
|
+
'ncurses'
|
1049
|
+
],
|
1050
|
+
|
1051
|
+
'zlib-acknowledgement': [
|
1052
|
+
# It isn't clear whey libpng shows up with this license name, but the license for libpng
|
1053
|
+
# is a permissive license.
|
1054
|
+
# Ref: https://github.com/glennrp/libpng/blob/libpng16/LICENSE
|
1055
|
+
'libpng',
|
1056
|
+
],
|
1057
|
+
|
1058
|
+
}
|
1059
|
+
|
649
1060
|
EXPECTED_MISSING_LICENSES = [
|
650
1061
|
|
651
1062
|
# This is a name we use for our C4 portals. And it isn't published.
|
@@ -726,7 +1137,7 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
726
1137
|
'responses',
|
727
1138
|
|
728
1139
|
# This seems to get flagged sometimes, but is not the pypi snovault library, it's what our dcicsnovault
|
729
|
-
# calls itself internally
|
1140
|
+
# calls itself internally. In any case, it's under MIT license and OK.
|
730
1141
|
# Ref: https://github.com/4dn-dcic/snovault/blob/master/LICENSE.txt
|
731
1142
|
'snovault',
|
732
1143
|
|
@@ -757,141 +1168,242 @@ class C4InfrastructureLicenseChecker(LicenseChecker):
|
|
757
1168
|
|
758
1169
|
]
|
759
1170
|
|
760
|
-
EXCEPTIONS = {
|
761
1171
|
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
766
|
-
|
767
|
-
|
768
|
-
# There are some slightly different versions of what appear to be BSD licenses here,
|
769
|
-
# but clearly the license is permissive.
|
770
|
-
# Ref: https://www.npmjs.com/package/mutation-observer?activeTab=readme
|
771
|
-
'mutation-observer',
|
772
|
-
],
|
1172
|
+
@LicenseCheckerRegistry.register_checker('park-lab-pipeline')
|
1173
|
+
class ParkLabPipelineLicenseChecker(ParkLabCommonLicenseChecker):
|
1174
|
+
"""
|
1175
|
+
Minimal checker common to pipelines from Park Lab.
|
1176
|
+
"""
|
773
1177
|
|
774
|
-
|
775
|
-
# The use of this URL appears to be a syntax error in the definition of entries-ponyfill
|
776
|
-
# In fact this seems to be covered by a CC0-1.0 license.
|
777
|
-
# Ref: https://unpkg.com/browse/object.entries-ponyfill@1.0.1/LICENSE
|
778
|
-
'object.entries-ponyfill',
|
779
|
-
],
|
1178
|
+
LICENSE_FRAMEWORKS = ['python', 'conda', 'r']
|
780
1179
|
|
781
|
-
'Custom: https://github.com/saikocat/colorbrewer.': [
|
782
|
-
# The use of this URL appears to be a syntax error in the definition of cartocolor
|
783
|
-
# In fact, this seems to be covered by a CC-BY-3.0 license.
|
784
|
-
# Ref: https://www.npmjs.com/package/cartocolor?activeTab=readme
|
785
|
-
'cartocolor',
|
786
|
-
],
|
787
1180
|
|
788
|
-
|
789
|
-
|
790
|
-
|
791
|
-
|
792
|
-
|
793
|
-
'emitter-component',
|
794
|
-
],
|
1181
|
+
@LicenseCheckerRegistry.register_checker('park-lab-gpl-pipeline')
|
1182
|
+
class ParkLabGplPipelineLicenseChecker(ParkLabCommonLicenseChecker):
|
1183
|
+
"""
|
1184
|
+
Minimal checker common to GPL pipelines from Park Lab.
|
1185
|
+
"""
|
795
1186
|
|
796
|
-
|
797
|
-
# seems to lack a license, but appears to be forked from the jsts library that uses
|
798
|
-
# the Eclipse Public License 1.0 and Eclipse Distribution License 1.0, so probably a permissive
|
799
|
-
# license is intended.
|
800
|
-
'Custom: https://travis-ci.org/DenisCarriere/turf-jsts.svg': [
|
801
|
-
'turf-jsts'
|
802
|
-
],
|
803
|
-
|
804
|
-
# DFSG = Debian Free Software Guidelines
|
805
|
-
# Ref: https://en.wikipedia.org/wiki/Debian_Free_Software_Guidelines
|
806
|
-
# Used as an apparent modifier to other licenses, to say they are approved per Debian.
|
807
|
-
# For example in this case, pytest-timeout has license: DFSG approved, MIT License,
|
808
|
-
# but is really just an MIT License that someone has checked is DFSG approved.
|
809
|
-
'DFSG approved': [
|
810
|
-
'pytest-timeout', # MIT Licensed
|
811
|
-
],
|
1187
|
+
ALLOWED = ParkLabPipelineLicenseChecker.ALLOWED + [
|
812
1188
|
|
813
1189
|
# Linking = With Restrictions, Private Use = Yes
|
814
1190
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
815
|
-
|
816
|
-
|
817
|
-
],
|
818
|
-
|
819
|
-
#
|
1191
|
+
# The "exceptions", if present, indicate waivers to source delivery requirements.
|
1192
|
+
# Ref: https://spdx.org/licenses/LGPL-3.0-linking-exception.html
|
1193
|
+
pattern('GNU Lesser General Public License v2( or later)?( [(]LGPL[v]?[23][+]?[)])?'),
|
1194
|
+
# 'GNU Lesser General Public License v2 or later (LGPLv2+)',
|
1195
|
+
# 'GNU Lesser General Public License v3 or later (LGPLv3+)',
|
1196
|
+
# 'LGPLv2', 'LGPL-v2', 'LGPL-v2.0', 'LGPL-2', 'LGPL-2.0',
|
1197
|
+
# 'LGPLv2+', 'LGPL-v2+', 'LGPL-v2.0+', 'LGPL-2+', 'LGPL-2.0+',
|
1198
|
+
# 'LGPLv3', 'LGPL-v3', 'LGPL-v3.0', 'LGPL-3', 'LGPL-3.0',
|
1199
|
+
# 'LGPLv3+', 'LGPL-v3+', 'LGPL-v3.0+', 'LGPL-3+', 'LGPL-3.0+',
|
1200
|
+
pattern('LGPL[v-]?[.0-9]*([+]|-only)?([- ]with[- ]exceptions)?'),
|
1201
|
+
|
1202
|
+
# Uncertain whether this is LGPL 2 or 3, but in any case we think weak copyleft should be OK
|
1203
|
+
# for pipeline or server use as long as we're not distributing sources.
|
1204
|
+
'LGPL',
|
1205
|
+
'GNU Library or Lesser General Public License (LGPL)',
|
1206
|
+
|
1207
|
+
# GPL
|
1208
|
+
# * library exception operates like LGPL
|
1209
|
+
# * classpath exception is a linking exception related to Oracle
|
1210
|
+
# Refs:
|
1211
|
+
# * https://www.gnu.org/licenses/old-licenses/gpl-1.0.en.html
|
1212
|
+
# * https://spdx.org/licenses/GPL-2.0-with-GCC-exception.html
|
1213
|
+
# * https://spdx.org/licenses/GPL-3.0-with-GCC-exception.html
|
1214
|
+
pattern('(GNU General Public License|GPL)[ ]?[v-]?[123]([.]0)?([+]|[- ]only)?'
|
1215
|
+
'([- ]with[- ]GCC(([- ]runtime)?[- ]library)?[- ]exception([- ][.0-9]*)?)?'
|
1216
|
+
'([- ]with[- ]Classpath[- ]exception([- ][.0-9]+)?)?'),
|
1217
|
+
|
1218
|
+
# Linking = "GPLv3 compatible only", Private Use = Yes
|
820
1219
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
821
|
-
'
|
822
|
-
|
823
|
-
|
824
|
-
|
1220
|
+
'GPL-2-or-3', # we sometimes generate this token
|
1221
|
+
# 'GPLv2+', 'GPL-v2+', 'GPL-v2.0+', 'GPL-2+', 'GPL-2.0+',
|
1222
|
+
# 'GPLv3', 'GPL-v3', 'GPL-v3.0', 'GPL-3', 'GPL-3.0',
|
1223
|
+
# 'GPLv3+', 'GPL-v3+', 'GPL-v3.0+', 'GPL-3+', 'GPL-3.0+',
|
1224
|
+
# 'GPLv3-only', 'GPL-3-only', 'GPL-v3-only', 'GPL-3.0-only', 'GPL-v3.0-only',
|
825
1225
|
|
826
|
-
|
827
|
-
|
828
|
-
|
1226
|
+
# Uncertain whether this is GPL 2 or 3, but we'll assume that means we can use either.
|
1227
|
+
# And version 3 is our preferred interpretation.
|
1228
|
+
'GNU General Public License',
|
1229
|
+
'GPL',
|
829
1230
|
|
830
|
-
|
831
|
-
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
832
|
-
# 'GNU Lesser General Public License v3 or later (LGPLv3+)',
|
1231
|
+
RLicenseFramework.R_LANGUAGE_LICENSE_NAME
|
833
1232
|
|
834
|
-
|
835
|
-
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
836
|
-
'GNU Library or Lesser General Public License (LGPL)': [
|
837
|
-
'psycopg2', # Used at runtime during server operation, but not modified or distributed
|
838
|
-
'psycopg2-binary', # Used at runtime during server operation, but not modified or distributed
|
839
|
-
'chardet', # Potentially used downstream in loadxl to detect charset for text files
|
840
|
-
'pyzmq', # Used in post-deploy-perf-tests, not distributed, and not modified or distributed
|
841
|
-
],
|
1233
|
+
]
|
842
1234
|
|
843
|
-
'GPL-2.0': [
|
844
|
-
# The license file for the node-forge javascript library says:
|
845
|
-
#
|
846
|
-
# "You may use the Forge project under the terms of either the BSD License or the
|
847
|
-
# GNU General Public License (GPL) Version 2."
|
848
|
-
#
|
849
|
-
# (We choose to use it under the BSD license.)
|
850
|
-
# Ref: https://www.npmjs.com/package/node-forge?activeTab=code
|
851
|
-
'node-forge',
|
852
|
-
],
|
853
1235
|
|
854
|
-
|
1236
|
+
@LicenseCheckerRegistry.register_checker('park-lab-common-server')
|
1237
|
+
class ParkLabCommonServerLicenseChecker(ParkLabCommonLicenseChecker):
|
1238
|
+
"""
|
1239
|
+
Checker for servers from Park Lab.
|
855
1240
|
|
856
|
-
|
857
|
-
|
858
|
-
|
859
|
-
'domready',
|
1241
|
+
If you're at some other organization, we recommend you make a class that has values
|
1242
|
+
suitable to your own organizational needs.
|
1243
|
+
"""
|
860
1244
|
|
861
|
-
|
862
|
-
# Ref: https://github.com/javaee/jsonp/blob/master/LICENSE.txt
|
863
|
-
# About CDDL ...
|
864
|
-
# Linking = Permissive, Private Use = ?
|
865
|
-
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
866
|
-
'jsonp',
|
1245
|
+
LICENSE_FRAMEWORKS = ['python', 'javascript']
|
867
1246
|
|
868
|
-
|
869
|
-
|
870
|
-
|
1247
|
+
EXCEPTIONS = augment(
|
1248
|
+
ParkLabCommonLicenseChecker.EXCEPTIONS,
|
1249
|
+
by={
|
1250
|
+
'BSD*': [
|
1251
|
+
# Although modified to insert the author name into the license text itself,
|
1252
|
+
# the license for these libraries are essentially BSD-3-Clause.
|
1253
|
+
'formatio',
|
1254
|
+
'samsam',
|
1255
|
+
|
1256
|
+
# There are some slightly different versions of what appear to be BSD licenses here,
|
1257
|
+
# but clearly the license is permissive.
|
1258
|
+
# Ref: https://www.npmjs.com/package/mutation-observer?activeTab=readme
|
1259
|
+
'mutation-observer',
|
1260
|
+
],
|
1261
|
+
|
1262
|
+
'Custom: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global': [
|
1263
|
+
# The use of this URL appears to be a syntax error in the definition of entries-ponyfill
|
1264
|
+
# In fact this seems to be covered by a CC0-1.0 license.
|
1265
|
+
# Ref: https://unpkg.com/browse/object.entries-ponyfill@1.0.1/LICENSE
|
1266
|
+
'object.entries-ponyfill',
|
1267
|
+
],
|
1268
|
+
|
1269
|
+
'Custom: https://github.com/saikocat/colorbrewer.': [
|
1270
|
+
# The use of this URL appears to be a syntax error in the definition of cartocolor
|
1271
|
+
# In fact, this seems to be covered by a CC-BY-3.0 license.
|
1272
|
+
# Ref: https://www.npmjs.com/package/cartocolor?activeTab=readme
|
1273
|
+
'cartocolor',
|
1274
|
+
],
|
1275
|
+
|
1276
|
+
'Custom: https://travis-ci.org/component/emitter.png': [
|
1277
|
+
# The use of this png appears to be a syntax error in the definition of emitter-component.
|
1278
|
+
# In fact, emitter-component uses an MIT License
|
1279
|
+
# Ref: https://www.npmjs.com/package/emitter-component
|
1280
|
+
# Ref: https://github.com/component/emitter/blob/master/LICENSE
|
1281
|
+
'emitter-component',
|
1282
|
+
],
|
1283
|
+
|
1284
|
+
# The 'turfs-jsts' repository (https://github.com/DenisCarriere/turf-jsts/blob/master/README.md)
|
1285
|
+
# seems to lack a license, but appears to be forked from the jsts library that uses
|
1286
|
+
# the Eclipse Public License 1.0 and Eclipse Distribution License 1.0, so probably a permissive
|
1287
|
+
# license is intended.
|
1288
|
+
'Custom: https://travis-ci.org/DenisCarriere/turf-jsts.svg': [
|
1289
|
+
'turf-jsts'
|
1290
|
+
],
|
1291
|
+
|
1292
|
+
'GNU General Public License (GPL)': [
|
1293
|
+
'docutils', # Used only privately as a separate documentation-generation task for ReadTheDocs
|
1294
|
+
],
|
1295
|
+
|
1296
|
+
# Linking = With Restrictions, Private Use = Yes
|
871
1297
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
872
|
-
'
|
1298
|
+
# 'GNU Lesser General Public License v3 or later (LGPLv3+)',
|
873
1299
|
|
874
|
-
#
|
875
|
-
# Linking = Permissive, Private Use = Yes
|
1300
|
+
# Linking = With Restrictions, Private Use = Yes
|
876
1301
|
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
877
|
-
'
|
878
|
-
|
879
|
-
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
888
|
-
|
1302
|
+
'GNU Library or Lesser General Public License (LGPL)': [
|
1303
|
+
'psycopg2', # Used at runtime during server operation, but not modified or distributed
|
1304
|
+
'psycopg2-binary', # Used at runtime during server operation, but not modified or distributed
|
1305
|
+
'chardet', # Potentially used downstream in loadxl to detect charset for text files
|
1306
|
+
'pyzmq', # Used in post-deploy-perf-tests, not distributed, and not modified or distributed
|
1307
|
+
],
|
1308
|
+
|
1309
|
+
'GPL-2.0': [
|
1310
|
+
# The license file for the node-forge javascript library says:
|
1311
|
+
#
|
1312
|
+
# "You may use the Forge project under the terms of either the BSD License or the
|
1313
|
+
# GNU General Public License (GPL) Version 2."
|
1314
|
+
#
|
1315
|
+
# (We choose to use it under the BSD license.)
|
1316
|
+
# Ref: https://www.npmjs.com/package/node-forge?activeTab=code
|
1317
|
+
'node-forge',
|
1318
|
+
],
|
1319
|
+
|
1320
|
+
'MIT*': [
|
1321
|
+
|
1322
|
+
# This library uses a mix of licenses, but they (MIT, CC0) generally seem permissive.
|
1323
|
+
# (It also mentions that some tools for building/testing use other libraries.)
|
1324
|
+
# Ref: https://github.com/requirejs/domReady/blob/master/LICENSE
|
1325
|
+
'domready',
|
1326
|
+
|
1327
|
+
# This library is under 'COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.1'
|
1328
|
+
# Ref: https://github.com/javaee/jsonp/blob/master/LICENSE.txt
|
1329
|
+
# About CDDL ...
|
1330
|
+
# Linking = Permissive, Private Use = ?
|
1331
|
+
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
1332
|
+
'jsonp',
|
1333
|
+
|
1334
|
+
# This library says pretty clearly it intends MIT license.
|
1335
|
+
# Ref: https://www.npmjs.com/package/component-indexof
|
1336
|
+
# Linking = Permissive, Private Use = Yes
|
1337
|
+
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
1338
|
+
'component-indexof',
|
1339
|
+
|
1340
|
+
# These look like a pretty straight MIT license.
|
1341
|
+
# Linking = Permissive, Private Use = Yes
|
1342
|
+
# Ref: https://en.wikipedia.org/wiki/Comparison_of_free_and_open-source_software_licenses
|
1343
|
+
'mixin', # LICENSE file at https://www.npmjs.com/package/mixin?activeTab=code
|
1344
|
+
'stack-trace', # https://github.com/stacktracejs/stacktrace.js/blob/master/LICENSE
|
1345
|
+
'typed-function', # LICENSE at https://www.npmjs.com/package/typed-function?activeTab=code
|
1346
|
+
|
1347
|
+
],
|
1348
|
+
|
1349
|
+
'UNLICENSED': [
|
1350
|
+
# The udn-browser library is our own and has been observed to sometimes show up in some contexts
|
1351
|
+
# as UNLICENSED, when really it's MIT.
|
1352
|
+
# Ref: https://github.com/dbmi-bgm/udn-browser/blob/main/LICENSE
|
1353
|
+
'udn-browser',
|
1354
|
+
],
|
1355
|
+
})
|
1356
|
+
|
1357
|
+
|
1358
|
+
@LicenseCheckerRegistry.register_checker('c4-infrastructure')
|
1359
|
+
class C4InfrastructureLicenseChecker(ParkLabCommonServerLicenseChecker):
|
1360
|
+
"""
|
1361
|
+
Checker for C4 infrastructure (Fourfront, CGAP, SMaHT) from Park Lab.
|
1362
|
+
"""
|
889
1363
|
|
890
|
-
|
1364
|
+
LICENSE_TITLE = "(The )?MIT License"
|
891
1365
|
|
892
1366
|
|
1367
|
+
@LicenseCheckerRegistry.register_checker('c4-python-infrastructure')
|
893
1368
|
class C4PythonInfrastructureLicenseChecker(C4InfrastructureLicenseChecker):
|
894
1369
|
"""
|
895
|
-
|
1370
|
+
Checker for C4 python library infrastructure (Fourfront, CGAP, SMaHT) from Park Lab.
|
896
1371
|
"""
|
897
1372
|
LICENSE_FRAMEWORKS = ['python']
|
1373
|
+
|
1374
|
+
|
1375
|
+
@LicenseCheckerRegistry.register_checker('scan2-pipeline')
|
1376
|
+
class Scan2PipelineLicenseChecker(ParkLabGplPipelineLicenseChecker):
|
1377
|
+
"""
|
1378
|
+
Checker for SCAN2 library from Park Lab.
|
1379
|
+
"""
|
1380
|
+
|
1381
|
+
EXCEPTIONS = augment(
|
1382
|
+
ParkLabGplPipelineLicenseChecker.EXCEPTIONS,
|
1383
|
+
by={
|
1384
|
+
'Custom: Matrix file LICENCE': [
|
1385
|
+
# The custom information in https://cran.r-project.org/web/packages/Matrix/LICENCE
|
1386
|
+
# says there are potential extra restrictions beyond a simple GPL license
|
1387
|
+
# if SparseSuite is used, but it is not requested explicitly by Scan2, and we're
|
1388
|
+
# trusting that any other libraries used by Scan2 would have investigated this.
|
1389
|
+
# So, effectively, we think the Matrix library for this situation operates the
|
1390
|
+
# same as if it were just GPL-3 licensed, and we are fine with that.
|
1391
|
+
'Matrix'
|
1392
|
+
],
|
1393
|
+
|
1394
|
+
"MISSING": [
|
1395
|
+
# mysql-common and mysql-libs are GPL, but since they are delivered by conda
|
1396
|
+
# and not distributed as part of the Scan2 distribution, they should be OK.
|
1397
|
+
# Ref: https://redresscompliance.com/mysql-license-a-complete-guide-to-licensing/#:~:text=commercial%20use # noQA
|
1398
|
+
'mysql-common',
|
1399
|
+
'mysql-libs',
|
1400
|
+
|
1401
|
+
# This is our own library
|
1402
|
+
'r-scan2', 'scan2',
|
1403
|
+
]
|
1404
|
+
}
|
1405
|
+
)
|
1406
|
+
|
1407
|
+
EXPECTED_MISSING_LICENSES = ParkLabGplPipelineLicenseChecker.EXPECTED_MISSING_LICENSES + [
|
1408
|
+
|
1409
|
+
]
|