0
# Constants and Error Handling
1
2
The license-expression library provides comprehensive error handling through exception classes and error constants. It also exposes token constants for advanced parsing and processing scenarios.
3
4
## Capabilities
5
6
### Exception Classes
7
8
Custom exception classes for handling license expression errors.
9
10
```python { .api }
11
class ExpressionError(Exception):
12
"""
13
Base exception class for license expression errors.
14
Raised when general expression processing fails.
15
"""
16
pass
17
18
class ParseError(Exception):
19
"""
20
Base parsing error class imported from boolean.py.
21
Contains information about parsing failures.
22
"""
23
24
@property
25
def token_type(self) -> int:
26
"""Type of token that caused the error."""
27
28
@property
29
def token_string(self) -> str:
30
"""String representation of the problematic token."""
31
32
@property
33
def position(self) -> int:
34
"""Position in the input string where the error occurred."""
35
36
@property
37
def error_code(self) -> int:
38
"""Numeric error code identifying the specific parsing error."""
39
40
class ExpressionParseError(ParseError, ExpressionError):
41
"""
42
Exception raised during license expression parsing.
43
Inherits from both ParseError (from boolean.py) and ExpressionError.
44
Contains detailed information about parsing failures.
45
"""
46
47
@property
48
def token_type(self) -> int:
49
"""Type of token that caused the error."""
50
51
@property
52
def token_string(self) -> str:
53
"""String representation of the problematic token."""
54
55
@property
56
def position(self) -> int:
57
"""Position in the input string where the error occurred."""
58
59
@property
60
def error_code(self) -> int:
61
"""Numeric error code identifying the specific parsing error."""
62
```
63
64
### Expression Validation Info
65
66
Data structure containing validation results.
67
68
```python { .api }
69
class ExpressionInfo:
70
"""
71
Contains detailed information about expression validation results.
72
"""
73
74
original_expression: str
75
"""The original expression string that was validated."""
76
77
normalized_expression: str
78
"""Normalized version of the expression, or None if validation failed."""
79
80
errors: list
81
"""List of error messages describing validation failures."""
82
83
invalid_symbols: list
84
"""List of unrecognized license symbols found in the expression."""
85
```
86
87
### Parse Error Constants
88
89
Error codes specific to license expression parsing.
90
91
```python { .api }
92
# License expression specific error codes
93
PARSE_EXPRESSION_NOT_UNICODE: int = 100
94
"""Expression string must be a string."""
95
96
PARSE_INVALID_EXCEPTION: int = 101
97
"""A license exception symbol can only be used as an exception in a 'WITH exception' statement."""
98
99
PARSE_INVALID_SYMBOL_AS_EXCEPTION: int = 102
100
"""A plain license symbol cannot be used as an exception in a 'WITH symbol' statement."""
101
102
PARSE_INVALID_SYMBOL: int = 103
103
"""A proper license symbol is needed."""
104
```
105
106
### Boolean Parse Error Constants
107
108
Error codes inherited from the boolean.py library.
109
110
```python { .api }
111
# Imported from boolean.py
112
PARSE_ERRORS: dict
113
"""Dictionary mapping error codes to error messages."""
114
115
PARSE_INVALID_EXPRESSION: int
116
"""Invalid expression structure."""
117
118
PARSE_INVALID_NESTING: int
119
"""Invalid parentheses nesting."""
120
121
PARSE_INVALID_OPERATOR_SEQUENCE: int
122
"""Invalid sequence of boolean operators."""
123
124
PARSE_INVALID_SYMBOL_SEQUENCE: int
125
"""Invalid sequence of symbols."""
126
127
PARSE_UNBALANCED_CLOSING_PARENS: int
128
"""Unbalanced closing parentheses."""
129
130
PARSE_UNKNOWN_TOKEN: int
131
"""Unknown token encountered during parsing."""
132
```
133
134
### Token Constants
135
136
Constants representing different types of tokens in license expressions.
137
138
```python { .api }
139
# Token type constants (imported from boolean.py)
140
TOKEN_SYMBOL: int
141
"""Token representing a license symbol."""
142
143
TOKEN_AND: int
144
"""Token representing the AND boolean operator."""
145
146
TOKEN_OR: int
147
"""Token representing the OR boolean operator."""
148
149
TOKEN_LPAR: int
150
"""Token representing a left parenthesis '('."""
151
152
TOKEN_RPAR: int
153
"""Token representing a right parenthesis ')'."""
154
155
TOKEN_WITH: int = 10
156
"""Token representing the WITH exception operator (license-expression specific)."""
157
```
158
159
### Keyword Constants
160
161
Predefined keyword objects and collections for license expression parsing.
162
163
```python { .api }
164
# Keyword objects for expression parsing
165
KW_LPAR: Keyword
166
"""Keyword object for left parenthesis '('."""
167
168
KW_RPAR: Keyword
169
"""Keyword object for right parenthesis ')'."""
170
171
KW_AND: Keyword
172
"""Keyword object for AND boolean operator."""
173
174
KW_OR: Keyword
175
"""Keyword object for OR boolean operator."""
176
177
KW_WITH: Keyword
178
"""Keyword object for WITH exception operator."""
179
180
# Keyword collections
181
KEYWORDS: tuple
182
"""Tuple containing all keyword objects (KW_LPAR, KW_RPAR, KW_AND, KW_OR, KW_WITH)."""
183
184
KEYWORDS_STRINGS: set
185
"""Set of keyword string values for quick lookup."""
186
187
OPERATORS: dict
188
"""Dictionary mapping operator strings to keyword objects."""
189
```
190
191
### Helper Classes
192
193
Named tuple and helper classes for token processing.
194
195
```python { .api }
196
class Keyword:
197
"""
198
Named tuple representing a keyword token.
199
"""
200
value: str
201
"""The string value of the keyword."""
202
203
type: int
204
"""The token type of the keyword."""
205
206
def __len__(self) -> int:
207
"""Return length of the keyword value."""
208
```
209
210
## Usage Examples
211
212
### Exception Handling
213
214
```python
215
from license_expression import (
216
get_spdx_licensing,
217
ExpressionError,
218
ExpressionParseError
219
)
220
221
licensing = get_spdx_licensing()
222
223
# Handle general expression errors
224
try:
225
result = licensing.validate('unknown-license')
226
if result.errors:
227
print("Validation errors:", result.errors)
228
except ExpressionError as e:
229
print(f"Expression error: {e}")
230
231
# Handle parsing errors with detailed information
232
try:
233
licensing.parse('MIT and', validate=True, strict=True)
234
except ExpressionParseError as e:
235
print(f"Parse error at position {e.position}: {e}")
236
print(f"Error code: {e.error_code}")
237
print(f"Problematic token: '{e.token_string}' (type: {e.token_type})")
238
```
239
240
### Working with Error Codes
241
242
```python
243
from license_expression import (
244
PARSE_INVALID_EXCEPTION,
245
PARSE_INVALID_SYMBOL_AS_EXCEPTION,
246
PARSE_ERRORS,
247
get_spdx_licensing
248
)
249
250
licensing = get_spdx_licensing()
251
252
# Test different error conditions
253
try:
254
# Using a license as an exception (should fail)
255
licensing.parse('MIT WITH Apache-2.0', validate=True, strict=True)
256
except ExpressionParseError as e:
257
if e.error_code == PARSE_INVALID_SYMBOL_AS_EXCEPTION:
258
print("Cannot use regular license as exception")
259
print(f"Error message: {PARSE_ERRORS[e.error_code]}")
260
261
try:
262
# Using an exception without WITH (should fail)
263
licensing.parse('Classpath-exception-2.0', validate=True, strict=True)
264
except ExpressionParseError as e:
265
if e.error_code == PARSE_INVALID_EXCEPTION:
266
print("Exception symbols must be used with WITH")
267
```
268
269
### Expression Validation Results
270
271
```python
272
from license_expression import get_spdx_licensing
273
274
licensing = get_spdx_licensing()
275
276
# Validate expressions and examine results
277
expressions = [
278
'MIT and Apache-2.0', # Valid
279
'UnknownLicense or MIT', # Has unknown symbol
280
'MIT WITH ClasspathException', # Invalid exception usage
281
]
282
283
for expr in expressions:
284
result = licensing.validate(expr)
285
print(f"\nExpression: {result.original_expression}")
286
print(f"Normalized: {result.normalized_expression}")
287
print(f"Errors: {result.errors}")
288
print(f"Invalid symbols: {result.invalid_symbols}")
289
```
290
291
### Token Type Checking
292
293
```python
294
from license_expression import (
295
TOKEN_SYMBOL, TOKEN_AND, TOKEN_OR,
296
TOKEN_LPAR, TOKEN_RPAR, TOKEN_WITH
297
)
298
299
# These constants can be used for custom token processing
300
def analyze_token_type(token_type):
301
token_names = {
302
TOKEN_SYMBOL: "License Symbol",
303
TOKEN_AND: "AND Operator",
304
TOKEN_OR: "OR Operator",
305
TOKEN_LPAR: "Left Parenthesis",
306
TOKEN_RPAR: "Right Parenthesis",
307
TOKEN_WITH: "WITH Operator"
308
}
309
return token_names.get(token_type, f"Unknown token type: {token_type}")
310
311
# Example usage (for advanced scenarios)
312
print(analyze_token_type(TOKEN_SYMBOL)) # "License Symbol"
313
print(analyze_token_type(TOKEN_AND)) # "AND Operator"
314
```
315
316
### Custom Error Handling
317
318
```python
319
from license_expression import (
320
get_spdx_licensing,
321
ExpressionError,
322
ExpressionParseError,
323
PARSE_ERRORS
324
)
325
326
def safe_parse_expression(licensing, expression):
327
"""
328
Safely parse an expression with comprehensive error handling.
329
"""
330
try:
331
return licensing.parse(expression, validate=True, strict=True)
332
except ExpressionParseError as e:
333
error_msg = PARSE_ERRORS.get(e.error_code, "Unknown parse error")
334
return {
335
'error': 'parse_error',
336
'message': error_msg,
337
'position': e.position,
338
'token': e.token_string
339
}
340
except ExpressionError as e:
341
return {
342
'error': 'expression_error',
343
'message': str(e)
344
}
345
except Exception as e:
346
return {
347
'error': 'unexpected_error',
348
'message': str(e)
349
}
350
351
# Use the safe parser
352
licensing = get_spdx_licensing()
353
result = safe_parse_expression(licensing, 'MIT WITH Apache-2.0')
354
print(result)
355
```
356
357
### Error Code Lookup
358
359
```python
360
from license_expression import PARSE_ERRORS
361
362
# Display all available error codes and messages
363
print("Available parse error codes:")
364
for code, message in PARSE_ERRORS.items():
365
print(f" {code}: {message}")
366
```
367
368
### Working with Keywords
369
370
```python
371
from license_expression import Keyword
372
373
# Create keyword tokens (used internally by the parser)
374
and_keyword = Keyword('AND', TOKEN_AND)
375
print(f"Keyword: {and_keyword.value}, Type: {and_keyword.type}")
376
print(f"Length: {len(and_keyword)}") # Length of 'AND' = 3
377
```
378
379
## Error Message Customization
380
381
For applications that need to provide user-friendly error messages:
382
383
```python
384
from license_expression import (
385
get_spdx_licensing,
386
ExpressionParseError,
387
PARSE_INVALID_SYMBOL_AS_EXCEPTION,
388
PARSE_INVALID_EXCEPTION
389
)
390
391
def user_friendly_error(expression, error):
392
"""Convert technical error to user-friendly message."""
393
if isinstance(error, ExpressionParseError):
394
if error.error_code == PARSE_INVALID_SYMBOL_AS_EXCEPTION:
395
return f"'{error.token_string}' is a license, not an exception. Use it without 'WITH'."
396
elif error.error_code == PARSE_INVALID_EXCEPTION:
397
return f"'{error.token_string}' is an exception and must be used with 'WITH'."
398
399
return f"Error parsing expression: {error}"
400
401
# Example usage
402
licensing = get_spdx_licensing()
403
try:
404
licensing.parse('MIT WITH Apache-2.0', validate=True, strict=True)
405
except ExpressionParseError as e:
406
friendly_msg = user_friendly_error('MIT WITH Apache-2.0', e)
407
print(friendly_msg)
408
```