or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

constants.mdexpressions.mdfactories.mdindex.mdlicensing.mdsymbols.md

constants.mddocs/

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

```