| Viewing file:  __init__.py (5.59 KB)      -rw-r--r-- Select action/file-type:
 
  (+) |  (+) |  (+) | Code (+) | Session (+) |  (+) | SDB (+) |  (+) |  (+) |  (+) |  (+) |  (+) | 
 
########################################################################################
 # Adapted from:
 #  https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py
 #
 # MIT License
 #
 # Copyright (c) 2017-present Ofek Lev <oss@ofek.dev>
 #
 # Permission is hereby granted, free of charge, to any person obtaining a copy of this
 # software and associated documentation files (the "Software"), to deal in the Software
 # without restriction, including without limitation the rights to use, copy, modify,
 # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
 # permit persons to whom the Software is furnished to do so, subject to the following
 # conditions:
 #
 # The above copyright notice and this permission notice shall be included in all copies
 # or substantial portions of the Software.
 #
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
 # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
 # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
 # CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
 # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 #
 #
 # With additional allowance of arbitrary `LicenseRef-` identifiers, not just
 # `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`.
 #
 #######################################################################################
 from __future__ import annotations
 
 import re
 from typing import NewType, cast
 
 from pip._vendor.packaging.licenses._spdx import EXCEPTIONS, LICENSES
 
 __all__ = [
 "NormalizedLicenseExpression",
 "InvalidLicenseExpression",
 "canonicalize_license_expression",
 ]
 
 license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$")
 
 NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str)
 
 
 class InvalidLicenseExpression(ValueError):
 """Raised when a license-expression string is invalid
 
 >>> canonicalize_license_expression("invalid")
 Traceback (most recent call last):
 ...
 packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid'
 """
 
 
 def canonicalize_license_expression(
 raw_license_expression: str,
 ) -> NormalizedLicenseExpression:
 if not raw_license_expression:
 message = f"Invalid license expression: {raw_license_expression!r}"
 raise InvalidLicenseExpression(message)
 
 # Pad any parentheses so tokenization can be achieved by merely splitting on
 # whitespace.
 license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ")
 licenseref_prefix = "LicenseRef-"
 license_refs = {
 ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :]
 for ref in license_expression.split()
 if ref.lower().startswith(licenseref_prefix.lower())
 }
 
 # Normalize to lower case so we can look up licenses/exceptions
 # and so boolean operators are Python-compatible.
 license_expression = license_expression.lower()
 
 tokens = license_expression.split()
 
 # Rather than implementing boolean logic, we create an expression that Python can
 # parse. Everything that is not involved with the grammar itself is treated as
 # `False` and the expression should evaluate as such.
 python_tokens = []
 for token in tokens:
 if token not in {"or", "and", "with", "(", ")"}:
 python_tokens.append("False")
 elif token == "with":
 python_tokens.append("or")
 elif token == "(" and python_tokens and python_tokens[-1] not in {"or", "and"}:
 message = f"Invalid license expression: {raw_license_expression!r}"
 raise InvalidLicenseExpression(message)
 else:
 python_tokens.append(token)
 
 python_expression = " ".join(python_tokens)
 try:
 invalid = eval(python_expression, globals(), locals())
 except Exception:
 invalid = True
 
 if invalid is not False:
 message = f"Invalid license expression: {raw_license_expression!r}"
 raise InvalidLicenseExpression(message) from None
 
 # Take a final pass to check for unknown licenses/exceptions.
 normalized_tokens = []
 for token in tokens:
 if token in {"or", "and", "with", "(", ")"}:
 normalized_tokens.append(token.upper())
 continue
 
 if normalized_tokens and normalized_tokens[-1] == "WITH":
 if token not in EXCEPTIONS:
 message = f"Unknown license exception: {token!r}"
 raise InvalidLicenseExpression(message)
 
 normalized_tokens.append(EXCEPTIONS[token]["id"])
 else:
 if token.endswith("+"):
 final_token = token[:-1]
 suffix = "+"
 else:
 final_token = token
 suffix = ""
 
 if final_token.startswith("licenseref-"):
 if not license_ref_allowed.match(final_token):
 message = f"Invalid licenseref: {final_token!r}"
 raise InvalidLicenseExpression(message)
 normalized_tokens.append(license_refs[final_token] + suffix)
 else:
 if final_token not in LICENSES:
 message = f"Unknown license: {final_token!r}"
 raise InvalidLicenseExpression(message)
 normalized_tokens.append(LICENSES[final_token]["id"] + suffix)
 
 normalized_expression = " ".join(normalized_tokens)
 
 return cast(
 NormalizedLicenseExpression,
 normalized_expression.replace("( ", "(").replace(" )", ")"),
 )
 
 |