0
# Expression Utilities
1
2
Utility functions for manipulating, combining, and validating license expressions. These functions provide additional capabilities beyond the core Licensing class, including expression combination, symbol validation, and specialized processing functions.
3
4
## Capabilities
5
6
### Expression Combination
7
8
Combine multiple license expressions into a single expression using boolean logic.
9
10
```python { .api }
11
def combine_expressions(
12
expressions,
13
relation: str = "AND",
14
unique: bool = True,
15
licensing = None
16
) -> LicenseExpression:
17
"""
18
Combine multiple license expressions with the specified boolean relation.
19
20
Parameters:
21
- expressions: List or tuple of license expression strings or LicenseExpression objects
22
- relation: Boolean relation to use ("AND" or "OR")
23
- unique: Remove duplicate expressions before combining
24
- licensing: Licensing instance to use for parsing (default: creates new instance)
25
26
Returns:
27
Combined LicenseExpression object, or None if expressions is empty
28
29
Raises:
30
- TypeError: If expressions is not a list/tuple or relation is invalid
31
"""
32
```
33
34
### Symbol Validation
35
36
Validate collections of license symbols for correctness and consistency.
37
38
```python { .api }
39
def validate_symbols(symbols, validate_keys: bool = False) -> tuple:
40
"""
41
Validate a collection of license symbols.
42
43
Parameters:
44
- symbols: Iterable of LicenseSymbol objects to validate
45
- validate_keys: Whether to validate that keys are proper license identifiers
46
47
Returns:
48
Tuple of (warnings, errors) where each is a list of validation messages
49
"""
50
```
51
52
### Symbol Utility Functions
53
54
Convert and process license symbols.
55
56
```python { .api }
57
def as_symbols(symbols) -> list:
58
"""
59
Convert symbol strings/objects to LicenseSymbol instances.
60
61
Parameters:
62
- symbols: Iterable of strings or LicenseSymbol objects
63
64
Returns:
65
List of LicenseSymbol objects
66
"""
67
68
def ordered_unique(seq) -> list:
69
"""
70
Return unique items from sequence while preserving order.
71
72
Parameters:
73
- seq: Input sequence to deduplicate
74
75
Returns:
76
List with unique items in original order
77
"""
78
```
79
80
## Usage Examples
81
82
### Combining Expressions
83
84
```python
85
from license_expression import combine_expressions, get_spdx_licensing
86
87
# Combine with default AND relation
88
expressions = ['MIT', 'Apache-2.0', 'BSD-3-Clause']
89
combined = combine_expressions(expressions)
90
print(str(combined)) # 'MIT AND Apache-2.0 AND BSD-3-Clause'
91
92
# Combine with OR relation
93
combined_or = combine_expressions(expressions, relation='OR')
94
print(str(combined_or)) # 'MIT OR Apache-2.0 OR BSD-3-Clause'
95
96
# Remove duplicates (default behavior)
97
duplicate_expressions = ['MIT', 'Apache-2.0', 'MIT', 'BSD-3-Clause']
98
combined_unique = combine_expressions(duplicate_expressions)
99
print(str(combined_unique)) # 'MIT AND Apache-2.0 AND BSD-3-Clause'
100
101
# Keep duplicates
102
combined_with_dupes = combine_expressions(duplicate_expressions, unique=False)
103
print(str(combined_with_dupes)) # 'MIT AND Apache-2.0 AND MIT AND BSD-3-Clause'
104
```
105
106
### Working with License Expression Objects
107
108
```python
109
from license_expression import combine_expressions, get_spdx_licensing
110
111
licensing = get_spdx_licensing()
112
113
# Combine parsed expressions
114
expr1 = licensing.parse('MIT OR Apache-2.0')
115
expr2 = licensing.parse('GPL-2.0')
116
expr3 = licensing.parse('BSD-3-Clause')
117
118
combined = combine_expressions([expr1, expr2, expr3], relation='OR')
119
print(str(combined)) # '(MIT OR Apache-2.0) OR GPL-2.0 OR BSD-3-Clause'
120
```
121
122
### Custom Licensing Instance
123
124
```python
125
from license_expression import combine_expressions, Licensing, LicenseSymbol
126
127
# Create custom licensing with specific symbols
128
custom_symbols = [
129
LicenseSymbol('CustomLicense1'),
130
LicenseSymbol('CustomLicense2'),
131
]
132
custom_licensing = Licensing(custom_symbols)
133
134
# Use custom licensing for combination
135
expressions = ['CustomLicense1', 'CustomLicense2']
136
combined = combine_expressions(expressions, licensing=custom_licensing)
137
print(str(combined)) # 'CustomLicense1 AND CustomLicense2'
138
```
139
140
### Single Expression Handling
141
142
```python
143
# Single expression returns the expression itself
144
single = combine_expressions(['MIT'])
145
print(str(single)) # 'MIT'
146
147
# Empty expressions return None
148
empty = combine_expressions([])
149
print(empty) # None
150
151
# None input returns None
152
none_result = combine_expressions(None)
153
print(none_result) # None (but raises TypeError)
154
```
155
156
### Symbol Validation
157
158
```python
159
from license_expression import validate_symbols, LicenseSymbol
160
161
# Create test symbols with various issues
162
symbols = [
163
LicenseSymbol('MIT'),
164
LicenseSymbol('Apache-2.0'),
165
LicenseSymbol('MIT'), # Duplicate key
166
LicenseSymbol('CustomLicense', ['MIT']), # Alias conflicts with existing key
167
'InvalidSymbol', # Not a LicenseSymbol object
168
LicenseSymbol('GPL-2.0', is_exception=True), # Invalid exception
169
]
170
171
warnings, errors = validate_symbols(symbols)
172
print("Warnings:", warnings)
173
print("Errors:", errors)
174
175
# Validate with key checking
176
warnings, errors = validate_symbols(symbols, validate_keys=True)
177
print("Key validation errors:", errors)
178
```
179
180
### Symbol Conversion and Processing
181
182
```python
183
from license_expression import as_symbols, ordered_unique
184
185
# Convert mixed symbol types to LicenseSymbol objects
186
mixed_symbols = ['MIT', LicenseSymbol('Apache-2.0'), 'GPL-2.0']
187
symbol_objects = as_symbols(mixed_symbols)
188
for symbol in symbol_objects:
189
print(type(symbol), symbol.key) # All are LicenseSymbol instances
190
191
# Remove duplicates while preserving order
192
duplicate_list = ['MIT', 'Apache-2.0', 'MIT', 'GPL-2.0', 'Apache-2.0']
193
unique_list = ordered_unique(duplicate_list)
194
print(unique_list) # ['MIT', 'Apache-2.0', 'GPL-2.0']
195
```
196
197
## Advanced Token Processing
198
199
The library includes internal functions for advanced expression parsing and token processing:
200
201
```python { .api }
202
def build_symbols_from_unknown_tokens(tokens) -> dict:
203
"""Build license symbols from unknown tokens during parsing."""
204
205
def build_token_groups_for_with_subexpression(tokens) -> list:
206
"""Build token groups for WITH subexpression processing."""
207
208
def is_with_subexpression(tokens_tripple) -> bool:
209
"""Check if a token triplet represents a WITH subexpression."""
210
211
def replace_with_subexpression_by_license_symbol(tokens, strict: bool = False) -> list:
212
"""Replace WITH subexpressions with license symbols in token stream."""
213
```
214
215
Note: These functions are primarily for internal use and advanced customization scenarios.
216
217
## Error Handling
218
219
Expression utility functions include comprehensive error handling:
220
221
```python
222
from license_expression import combine_expressions
223
224
# Invalid relation parameter
225
try:
226
combine_expressions(['MIT', 'Apache-2.0'], relation='INVALID')
227
except TypeError as e:
228
print(f"Error: {e}")
229
230
# Invalid expressions parameter type
231
try:
232
combine_expressions('MIT') # String instead of list
233
except TypeError as e:
234
print(f"Error: {e}")
235
236
# Invalid expressions in list
237
try:
238
combine_expressions([123, 'MIT']) # Number in list
239
except Exception as e:
240
print(f"Error: {e}")
241
```