license.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. # vi:sts=4:sw=4:et
  2. """Code for parsing OpenEmbedded license strings"""
  3. import ast
  4. import re
  5. from fnmatch import fnmatchcase as fnmatch
  6. def license_ok(license, dont_want_licenses):
  7. """ Return False if License exist in dont_want_licenses else True """
  8. for dwl in dont_want_licenses:
  9. # If you want to exclude license named generically 'X', we
  10. # surely want to exclude 'X+' as well. In consequence, we
  11. # will exclude a trailing '+' character from LICENSE in
  12. # case INCOMPATIBLE_LICENSE is not a 'X+' license.
  13. lic = license
  14. if not re.search('\+$', dwl):
  15. lic = re.sub('\+', '', license)
  16. if fnmatch(lic, dwl):
  17. return False
  18. return True
  19. class LicenseError(Exception):
  20. pass
  21. class LicenseSyntaxError(LicenseError):
  22. def __init__(self, licensestr, exc):
  23. self.licensestr = licensestr
  24. self.exc = exc
  25. LicenseError.__init__(self)
  26. def __str__(self):
  27. return "error in '%s': %s" % (self.licensestr, self.exc)
  28. class InvalidLicense(LicenseError):
  29. def __init__(self, license):
  30. self.license = license
  31. LicenseError.__init__(self)
  32. def __str__(self):
  33. return "invalid characters in license '%s'" % self.license
  34. license_operator_chars = '&|() '
  35. license_operator = re.compile('([' + license_operator_chars + '])')
  36. license_pattern = re.compile('[a-zA-Z0-9.+_\-]+$')
  37. class LicenseVisitor(ast.NodeVisitor):
  38. """Get elements based on OpenEmbedded license strings"""
  39. def get_elements(self, licensestr):
  40. new_elements = []
  41. elements = list([x for x in license_operator.split(licensestr) if x.strip()])
  42. for pos, element in enumerate(elements):
  43. if license_pattern.match(element):
  44. if pos > 0 and license_pattern.match(elements[pos-1]):
  45. new_elements.append('&')
  46. element = '"' + element + '"'
  47. elif not license_operator.match(element):
  48. raise InvalidLicense(element)
  49. new_elements.append(element)
  50. return new_elements
  51. """Syntax tree visitor which can accept elements previously generated with
  52. OpenEmbedded license string"""
  53. def visit_elements(self, elements):
  54. self.visit(ast.parse(' '.join(elements)))
  55. """Syntax tree visitor which can accept OpenEmbedded license strings"""
  56. def visit_string(self, licensestr):
  57. self.visit_elements(self.get_elements(licensestr))
  58. class FlattenVisitor(LicenseVisitor):
  59. """Flatten a license tree (parsed from a string) by selecting one of each
  60. set of OR options, in the way the user specifies"""
  61. def __init__(self, choose_licenses):
  62. self.choose_licenses = choose_licenses
  63. self.licenses = []
  64. LicenseVisitor.__init__(self)
  65. def visit_Str(self, node):
  66. self.licenses.append(node.s)
  67. def visit_BinOp(self, node):
  68. if isinstance(node.op, ast.BitOr):
  69. left = FlattenVisitor(self.choose_licenses)
  70. left.visit(node.left)
  71. right = FlattenVisitor(self.choose_licenses)
  72. right.visit(node.right)
  73. selected = self.choose_licenses(left.licenses, right.licenses)
  74. self.licenses.extend(selected)
  75. else:
  76. self.generic_visit(node)
  77. def flattened_licenses(licensestr, choose_licenses):
  78. """Given a license string and choose_licenses function, return a flat list of licenses"""
  79. flatten = FlattenVisitor(choose_licenses)
  80. try:
  81. flatten.visit_string(licensestr)
  82. except SyntaxError as exc:
  83. raise LicenseSyntaxError(licensestr, exc)
  84. return flatten.licenses
  85. def is_included(licensestr, whitelist=None, blacklist=None):
  86. """Given a license string and whitelist and blacklist, determine if the
  87. license string matches the whitelist and does not match the blacklist.
  88. Returns a tuple holding the boolean state and a list of the applicable
  89. licenses which were excluded (or None, if the state is True)
  90. """
  91. def include_license(license):
  92. return any(fnmatch(license, pattern) for pattern in whitelist)
  93. def exclude_license(license):
  94. return any(fnmatch(license, pattern) for pattern in blacklist)
  95. def choose_licenses(alpha, beta):
  96. """Select the option in an OR which is the 'best' (has the most
  97. included licenses)."""
  98. alpha_weight = len(list(filter(include_license, alpha)))
  99. beta_weight = len(list(filter(include_license, beta)))
  100. if alpha_weight > beta_weight:
  101. return alpha
  102. else:
  103. return beta
  104. if not whitelist:
  105. whitelist = ['*']
  106. if not blacklist:
  107. blacklist = []
  108. licenses = flattened_licenses(licensestr, choose_licenses)
  109. excluded = [lic for lic in licenses if exclude_license(lic)]
  110. included = [lic for lic in licenses if include_license(lic)]
  111. if excluded:
  112. return False, excluded
  113. else:
  114. return True, included
  115. class ManifestVisitor(LicenseVisitor):
  116. """Walk license tree (parsed from a string) removing the incompatible
  117. licenses specified"""
  118. def __init__(self, dont_want_licenses, canonical_license, d):
  119. self._dont_want_licenses = dont_want_licenses
  120. self._canonical_license = canonical_license
  121. self._d = d
  122. self._operators = []
  123. self.licenses = []
  124. self.licensestr = ''
  125. LicenseVisitor.__init__(self)
  126. def visit(self, node):
  127. if isinstance(node, ast.Str):
  128. lic = node.s
  129. if license_ok(self._canonical_license(self._d, lic),
  130. self._dont_want_licenses) == True:
  131. if self._operators:
  132. ops = []
  133. for op in self._operators:
  134. if op == '[':
  135. ops.append(op)
  136. elif op == ']':
  137. ops.append(op)
  138. else:
  139. if not ops:
  140. ops.append(op)
  141. elif ops[-1] in ['[', ']']:
  142. ops.append(op)
  143. else:
  144. ops[-1] = op
  145. for op in ops:
  146. if op == '[' or op == ']':
  147. self.licensestr += op
  148. elif self.licenses:
  149. self.licensestr += ' ' + op + ' '
  150. self._operators = []
  151. self.licensestr += lic
  152. self.licenses.append(lic)
  153. elif isinstance(node, ast.BitAnd):
  154. self._operators.append("&")
  155. elif isinstance(node, ast.BitOr):
  156. self._operators.append("|")
  157. elif isinstance(node, ast.List):
  158. self._operators.append("[")
  159. elif isinstance(node, ast.Load):
  160. self.licensestr += "]"
  161. self.generic_visit(node)
  162. def manifest_licenses(licensestr, dont_want_licenses, canonical_license, d):
  163. """Given a license string and dont_want_licenses list,
  164. return license string filtered and a list of licenses"""
  165. manifest = ManifestVisitor(dont_want_licenses, canonical_license, d)
  166. try:
  167. elements = manifest.get_elements(licensestr)
  168. # Replace '()' to '[]' for handle in ast as List and Load types.
  169. elements = ['[' if e == '(' else e for e in elements]
  170. elements = [']' if e == ')' else e for e in elements]
  171. manifest.visit_elements(elements)
  172. except SyntaxError as exc:
  173. raise LicenseSyntaxError(licensestr, exc)
  174. # Replace '[]' to '()' for output correct license.
  175. manifest.licensestr = manifest.licensestr.replace('[', '(').replace(']', ')')
  176. return (manifest.licensestr, manifest.licenses)
  177. class ListVisitor(LicenseVisitor):
  178. """Record all different licenses found in the license string"""
  179. def __init__(self):
  180. self.licenses = set()
  181. def visit_Str(self, node):
  182. self.licenses.add(node.s)
  183. def list_licenses(licensestr):
  184. """Simply get a list of all licenses mentioned in a license string.
  185. Binary operators are not applied or taken into account in any way"""
  186. visitor = ListVisitor()
  187. try:
  188. visitor.visit_string(licensestr)
  189. except SyntaxError as exc:
  190. raise LicenseSyntaxError(licensestr, exc)
  191. return visitor.licenses