File: README.rst

package info (click to toggle)
python-license-expression 30.4.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,588 kB
  • sloc: python: 6,130; sh: 315; makefile: 51
file content (262 lines) | stat: -rw-r--r-- 10,893 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
==================
license-expression
==================

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

- License: Apache-2.0
- Python: 3.9+
- Homepage: https://github.com/aboutcode-org/license-expression/
- Install: `pip install license-expression` also available in most Linux distro.

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:

- AboutCode-toolkit https://github.com/aboutcode-org/aboutcode-toolkit
- AlekSIS (School Information System) https://edugit.org/AlekSIS/official/AlekSIS-Core
- Barista https://github.com/Optum/barista
- Conda forge tools https://github.com/conda-forge/conda-smithy
- DejaCode https://dejacode.com
- DeltaCode https://github.com/nexB/deltacode
- FenixscanX https://github.com/SmartsYoung/FenixscanX
- FetchCode https://github.com/aboutcode-org/fetchcode
- Flict https://github.com/vinland-technology/flict and https://github.com/vinland-technology
- license.sh https://github.com/webscopeio/license.sh
- liferay_inbound_checker https://github.com/carmenbianca/liferay_inbound_checker
- REUSE https://reuse.software/ and https://github.com/fsfe/reuse-tool
- ScanCode-io https://github.com/aboutcode-org/scancode.io
- ScanCode-toolkit https://github.com/aboutcode-org/scancode-toolkit
- SecObserve https://github.com/MaibornWolff/SecObserve

See also for details:
- https://spdx.github.io/spdx-spec/appendix-IV-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!):

- JavaScript https://github.com/jslicense/spdx-expression-parse.js
- Rust https://github.com/ehuss/license-exprs
- Haskell https://github.com/phadej/spdx
- Go https://github.com/kyoh86/go-spdx
- Ada https://github.com/Fabien-Chouteau/spdx_ada
- Java https://github.com/spdx/tools and https://github.com/aschet/spdx-license-expression-tools

Build and tests status
======================

+--------------------------+------------------------+----------------------------------+
|**Linux & macOS (Travis)**| **Windows (AppVeyor)** |**Linux, Windows & macOS (Azure)**|
+==========================+========================+==================================+
|                          |                        |                                  |
| |travis-badge-icon|      | |appveyor-badge-icon|  |   |azure-badge-icon|             |
|                          |                        |                                  |
+--------------------------+------------------------+----------------------------------+

Source code and download
========================

- GitHub https://github.com/aboutcode-org/license-expression.git
- PyPI https://pypi.python.org/pypi/license-expression

Also available in several Linux distros:

- Arch Linux https://archlinux.org/packages/extra/any/python-license-expression/
- Debian https://packages.debian.org/unstable/source/license-expression
- DragonFly BSD https://github.com/DragonFlyBSD/DPorts/tree/master/textproc/py-license-expression
- Fedora https://src.fedoraproject.org/rpms/python-license-expression/
- FreeBSD https://www.freshports.org/textproc/py-license-expression
- NixOS https://github.com/NixOS/nixpkgs/blob/release-21.05/pkgs/development/python-modules/license-expression/default.nix
- openSUSE https://build.opensuse.org/package/show/openSUSE:Factory/python-license-expression


Support
=======

- Submit bugs and questions at: https://github.com/aboutcode-org/license-expression/issues
- Join the chat at: https://gitter.im/aboutcode-org/discuss

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.20 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``


.. |travis-badge-icon| image:: https://api.travis-ci.org/nexB/license-expression.png?branch=master
    :target: https://travis-ci.org/nexB/license-expression
    :alt: Travis tests status
    :align: middle

.. |appveyor-badge-icon| image:: https://ci.appveyor.com/api/projects/status/github/nexB/license-expression?svg=true
    :target: https://ci.appveyor.com/project/nexB/license-expression
    :alt: Appveyor tests status
    :align: middle

.. |azure-badge-icon| image:: https://dev.azure.com/nexB/license-expression/_apis/build/status/nexB.license-expression?branchName=master
    :target: https://dev.azure.com/nexB/license-expression/_build/latest?definitionId=2&branchName=master
    :alt: Azure pipelines tests status
    :align: middle