Skip to main content

license-expression is a comprehensive utility library to parse, compare, simplify and normalize license expressions (such as SPDX license expressions) using boolean logic.

Software project licenses are often a combination of several free and open source software licenses. License expressions – as specified by SPDX – provide a concise and human readable way to express these licenses without having to read long license texts, while still being machine-readable.

License expressions are used by key FOSS projects such as Linux; several packages ecosystem use them to document package licensing metadata such as npm and Rubygems; they are important when exchanging software data (such as with SPDX and SBOM in general) as a way to express licensing precisely.

license-expression is a comprehensive utility library to parse, compare, simplify and normalize these license expressions (such as SPDX license expressions) using boolean logic like in: GPL-2.0-or-later WITH Classpath-exception-2.0 AND MIT.

It includes the license keys from SPDX https://spdx.org/licenses/ (version 3.26) and ScanCode LicenseDB (from scancode-toolkit version 32.3.1, last published on 2025-01-10). See https://scancode-licensedb.aboutcode.org/ to get started quickly.

license-expression is both powerful and simple to use and is a used as the license expression engine in several projects and products such as:

See also for details: - https://spdx.github.io/spdx-spec/v2.3/SPDX-license-expressions

license-expression is also packaged for most Linux distributions. See below.

Alternative:

There is no known alternative library for Python, but there are several similar libraries in other languages (but not as powerful of course!):

Source code and download

Also available in several Linux distros:

Support

Description

This module defines a mini language to parse, validate, simplify, normalize and compare license expressions using a boolean logic engine.

This supports SPDX license expressions and also accepts other license naming conventions and license identifiers aliases to resolve and normalize any license expressions.

Using boolean logic, license expressions can be tested for equality, containment, equivalence and can be normalized or simplified.

It also bundles the SPDX License list (3.26 as of now) and the ScanCode license DB (based on latest ScanCode) to easily parse and validate expressions using the license symbols.

Usage examples

The main entry point is the Licensing object that you can use to parse, validate, compare, simplify and normalize license expressions.

Create an SPDX Licensing and parse expressions:

>>> from license_expression import get_spdx_licensing
>>> licensing = get_spdx_licensing()
>>> expression = ' GPL-2.0 or LGPL-2.1 and mit '
>>> parsed = licensing.parse(expression)
>>> print(parsed.pretty())
OR(
  LicenseSymbol('GPL-2.0-only'),
  AND(
    LicenseSymbol('LGPL-2.1-only'),
    LicenseSymbol('MIT')
  )
)

>>> str(parsed)
'GPL-2.0-only OR (LGPL-2.1-only AND MIT)'

>>> licensing.parse('unknwon with foo', validate=True, strict=True)
license_expression.ExpressionParseError: A plain license symbol cannot be used
as an exception in a "WITH symbol" statement. for token: "foo" at position: 13

>>> licensing.parse('unknwon with foo', validate=True)
license_expression.ExpressionError: Unknown license key(s): unknwon, foo

>>> licensing.validate('foo and MIT and GPL-2.0+')
ExpressionInfo(
    original_expression='foo and MIT and GPL-2.0+',
    normalized_expression=None,
    errors=['Unknown license key(s): foo'],
    invalid_symbols=['foo']
)

Create a simple Licensing and parse expressions:

>>> from license_expression import Licensing, LicenseSymbol
>>> licensing = Licensing()
>>> expression = ' GPL-2.0 or LGPL-2.1 and mit '
>>> parsed = licensing.parse(expression)
>>> expression = ' GPL-2.0 or LGPL-2.1 and mit '
>>> expected = 'GPL-2.0-only OR (LGPL-2.1-only AND mit)'
>>> assert str(parsed) == expected
>>> assert parsed.render('{symbol.key}') == expected

Create a Licensing with your own license symbols:

>>> expected = [
...   LicenseSymbol('GPL-2.0'),
...   LicenseSymbol('LGPL-2.1'),
...   LicenseSymbol('mit')
... ]
>>> assert licensing.license_symbols(expression) == expected
>>> assert licensing.license_symbols(parsed) == expected

>>> symbols = ['GPL-2.0+', 'Classpath', 'BSD']
>>> licensing = Licensing(symbols)
>>> expression = 'GPL-2.0+ with Classpath or (bsd)'
>>> parsed = licensing.parse(expression)
>>> expected = 'GPL-2.0+ WITH Classpath OR BSD'
>>> assert parsed.render('{symbol.key}') == expected

>>> expected = [
...   LicenseSymbol('GPL-2.0+'),
...   LicenseSymbol('Classpath'),
...   LicenseSymbol('BSD')
... ]
>>> assert licensing.license_symbols(parsed) == expected
>>> assert licensing.license_symbols(expression) == expected

And expression can be deduplicated, to remove duplicate license subexpressions without changing the order and without consider license choices as simplifiable:

>>> expression2 = ' GPL-2.0 or (mit and LGPL 2.1) or bsd Or GPL-2.0  or (mit and LGPL 2.1)'
>>> parsed2 = licensing.parse(expression2)
>>> str(parsed2)
'GPL-2.0 OR (mit AND LGPL 2.1) OR BSD OR GPL-2.0 OR (mit AND LGPL 2.1)'
>>> assert str(parsed2.simplify()) == 'BSD OR GPL-2.0 OR (LGPL 2.1 AND mit)'

Expression can be simplified, treating them as boolean expressions:

>>> expression2 = ' GPL-2.0 or (mit and LGPL 2.1) or bsd Or GPL-2.0  or (mit and LGPL 2.1)'
>>> parsed2 = licensing.parse(expression2)
>>> str(parsed2)
'GPL-2.0 OR (mit AND LGPL 2.1) OR BSD OR GPL-2.0 OR (mit AND LGPL 2.1)'
>>> assert str(parsed2.simplify()) == 'BSD OR GPL-2.0 OR (LGPL 2.1 AND mit)'

Two expressions can be compared for equivalence and containment:

>>> expr1 = licensing.parse(' GPL-2.0 or (LGPL 2.1 and mit) ')
>>> expr2 = licensing.parse(' (mit and LGPL 2.1)  or GPL-2.0 ')
>>> licensing.is_equivalent(expr1, expr2)
True
>>> licensing.is_equivalent(' GPL-2.0 or (LGPL 2.1 and mit) ',
...                         ' (mit and LGPL 2.1)  or GPL-2.0 ')
True
>>> expr1.simplify() == expr2.simplify()
True
>>> expr3 = licensing.parse(' GPL-2.0 or mit or LGPL 2.1')
>>> licensing.is_equivalent(expr2, expr3)
False
>>> expr4 = licensing.parse('mit and LGPL 2.1')
>>> expr4.simplify() in expr2.simplify()
True
>>> licensing.contains(expr2, expr4)
True

Development

  • Checkout a clone from https://github.com/aboutcode-org/license-expression.git

  • Then run ./configure --dev and then source tmp/bin/activate on Linux and POSIX. This will install all dependencies in a local virtualenv, including development deps.

  • On Windows run configure.bat --dev and then Scripts\bin\activate instead.

  • To run the tests, run pytest -vvs

Release files for license-expression 30.4.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for license-expression 30.4.4
File Size Uploaded
license_expression-30.4.4.tar.gz 186.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for license-expression 30.4.4
File Interpreter ABI Platform
license_expression-30.4.4-py3-none-any.whl Python 3 none any Details

Total release size: 307.0 kB

Release files / license_expression-30.4.4.tar.gz

Download URL license_expression-30.4.4.tar.gz
Size 186.4 kB
Tags Source
SHA-256 checksum
How to use checksums
73448f0aacd8d0808895bdc4b2c8e01a8d67646e4188f887375398c761f340fd
BLAKE2b-256 checksum
How to use checksums
4071d89bb0e71b1415453980fd32315f2a037aad9f7f70f695c7cec7035feb13
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release files / license_expression-30.4.4-py3-none-any.whl

Download URL license_expression-30.4.4-py3-none-any.whl
Size 120.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
421788fdcadb41f049d2dc934ce666626265aeccefddd25e162a26f23bcbf8a4
BLAKE2b-256 checksum
How to use checksums
af40791891d4c0c4dab4c5e187c17261cedc26285fd41541577f900470a45a4d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.9

Release history Release notifications | RSS feed

This release

30.4.4 This release

2 release files

30.4.3

2 release files

30.4.2

1 release file

30.4.1

2 release files

30.4.0

2 release files

30.3.1

2 release files

30.3.0

2 release files

30.1.1

2 release files

30.1.0

2 release files

30.0.0

2 release files

1.2

2 release files

1.0

2 release files

0.999

2 release files

0.99

1 release file

0.98

1 release file

0.97

1 release file

0.96

2 release files

0.95

2 release files

0.94

2 release files

0.93

2 release files

0.92

2 release files

0.91

2 release files

0.90

2 release files

0.31

2 release files

0.30

2 release files

0.21

2 release files

0.20

2 release files

0.19

2 release files

0.18

2 release files

0.17

2 release files

0.16

2 release files

0.15

2 release files

0.14

2 release files

0.13

2 release files

0.12

2 release files

0.8

2 release files

0.7

2 release files

0.6

2 release files

0.4

3 release files

0.3

4 release files

0.2

4 release files

0.1

4 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page