or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

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

licensing.mddocs/

0

# Core Licensing Operations

1

2

The Licensing class provides the main interface for parsing, validating, and manipulating license expressions. It extends the boolean algebra system to provide license-specific functionality including expression parsing, validation, equivalence testing, and containment analysis.

3

4

## Capabilities

5

6

### Licensing Class

7

8

The primary class for working with license expressions, providing comprehensive parsing, validation, and comparison capabilities.

9

10

```python { .api }

11

class Licensing:

12

"""

13

Main entry point for license expression operations.

14

Extends boolean.BooleanAlgebra with license-specific functionality.

15

"""

16

17

def __init__(self, symbols=None, quiet: bool = True):

18

"""

19

Initialize Licensing with optional license symbols.

20

21

Parameters:

22

- symbols: Iterable of LicenseSymbol objects or strings

23

- quiet: Print warnings and errors found in symbols unless True (default: True)

24

"""

25

26

def parse(self, expression: str, validate: bool = False, strict: bool = False, simple: bool = False, **kwargs) -> LicenseExpression:

27

"""

28

Parse a license expression string into a LicenseExpression object.

29

30

Parameters:

31

- expression: License expression string to parse

32

- validate: Whether to validate symbols against known licenses

33

- strict: Whether to use strict validation (raises exceptions for unknown symbols)

34

- simple: Whether to return a simple expression without normalization

35

- **kwargs: Additional keyword arguments

36

37

Returns:

38

LicenseExpression object representing the parsed expression

39

40

Raises:

41

- ExpressionParseError: If parsing fails or strict=True with unknown symbols

42

- ExpressionError: If validation fails with unknown symbols

43

"""

44

45

def validate(self, expression: str, strict: bool = True, **kwargs) -> ExpressionInfo:

46

"""

47

Validate a license expression and return detailed validation information.

48

49

Parameters:

50

- expression: License expression string to validate

51

- strict: Whether to use strict validation (default: True)

52

- **kwargs: Additional keyword arguments

53

54

Returns:

55

ExpressionInfo object containing validation results, errors, and normalized expression

56

"""

57

58

def is_equivalent(self, expr1, expr2) -> bool:

59

"""

60

Test if two license expressions are logically equivalent.

61

62

Parameters:

63

- expr1: First expression (string or LicenseExpression)

64

- expr2: Second expression (string or LicenseExpression)

65

66

Returns:

67

True if expressions are equivalent, False otherwise

68

"""

69

70

def contains(self, expr1, expr2) -> bool:

71

"""

72

Test if the first expression contains (implies) the second expression.

73

74

Parameters:

75

- expr1: Container expression (string or LicenseExpression)

76

- expr2: Contained expression (string or LicenseExpression)

77

78

Returns:

79

True if expr1 contains expr2, False otherwise

80

"""

81

82

def license_symbols(self, expression, unique: bool = True, decompose: bool = True, **kwargs) -> list:

83

"""

84

Extract all license symbols from an expression.

85

86

Parameters:

87

- expression: License expression (string or LicenseExpression)

88

- unique: Only return unique symbols (default: True)

89

- decompose: Decompose composite symbols like LicenseWithExceptionSymbol (default: True)

90

- **kwargs: Additional keyword arguments

91

92

Returns:

93

List of LicenseSymbol objects found in the expression

94

"""

95

96

def primary_license_symbol(self, expression, decompose: bool = True, **kwargs):

97

"""

98

Return the left-most license symbol of an expression or None.

99

100

Parameters:

101

- expression: License expression (string or LicenseExpression)

102

- decompose: Only return license symbol from decomposed composite symbols (default: True)

103

- **kwargs: Additional keyword arguments

104

105

Returns:

106

First LicenseSymbol object found in the expression, or None

107

"""

108

109

def primary_license_key(self, expression, **kwargs) -> str:

110

"""

111

Return the left-most license key of an expression or None.

112

113

Parameters:

114

- expression: License expression (string or LicenseExpression)

115

- **kwargs: Additional keyword arguments

116

117

Returns:

118

First license key found in the expression, or None

119

"""

120

121

def license_keys(self, expression, unique: bool = True, **kwargs) -> list:

122

"""

123

Return a list of license keys used in an expression.

124

125

Parameters:

126

- expression: License expression (string or LicenseExpression)

127

- unique: Only return unique keys (default: True)

128

- **kwargs: Additional keyword arguments

129

130

Returns:

131

List of license key strings found in the expression

132

"""

133

134

def unknown_license_symbols(self, expression, unique: bool = True, **kwargs) -> list:

135

"""

136

Return a list of unknown license symbols used in an expression.

137

138

Parameters:

139

- expression: License expression (string or LicenseExpression)

140

- unique: Only return unique symbols (default: True)

141

- **kwargs: Additional keyword arguments

142

143

Returns:

144

List of unknown LicenseSymbol objects found in the expression

145

"""

146

147

def unknown_license_keys(self, expression, unique: bool = True, **kwargs) -> list:

148

"""

149

Return a list of unknown license keys used in an expression.

150

151

Parameters:

152

- expression: License expression (string or LicenseExpression)

153

- unique: Only return unique keys (default: True)

154

- **kwargs: Additional keyword arguments

155

156

Returns:

157

List of unknown license key strings found in the expression

158

"""

159

160

def validate_license_keys(self, expression):

161

"""

162

Validate license keys in an expression, raising ExpressionError for unknown keys.

163

164

Parameters:

165

- expression: License expression (string or LicenseExpression)

166

167

Raises:

168

- ExpressionError: If unknown license keys are found

169

"""

170

171

def dedup(self, expression):

172

"""

173

Return a deduplicated LicenseExpression given a license expression.

174

175

Deduplication is similar to simplification but specialized for license expressions.

176

Unlike simplification, choices (OR expressions) are kept as-is to preserve options.

177

178

Parameters:

179

- expression: License expression (string or LicenseExpression)

180

181

Returns:

182

Deduplicated LicenseExpression object

183

"""

184

185

def tokenize(self, expression: str, strict: bool = False, simple: bool = False):

186

"""

187

Return an iterable of 3-tuple describing each token in an expression string.

188

189

Parameters:

190

- expression: License expression string to tokenize

191

- strict: Use strict validation for WITH expressions (default: False)

192

- simple: Use simple tokenizer assuming no spaces in license symbols (default: False)

193

194

Returns:

195

Iterator of (token_obj, token_string, position) tuples

196

"""

197

198

def get_advanced_tokenizer(self):

199

"""

200

Return an AdvancedTokenizer instance for this Licensing.

201

202

Returns:

203

AdvancedTokenizer instance that recognizes known symbol keys and aliases

204

"""

205

206

def simple_tokenizer(self, expression: str):

207

"""

208

Return an iterable of Token from an expression string using simple tokenization.

209

210

Parameters:

211

- expression: License expression string to tokenize

212

213

Returns:

214

Iterator of Token objects from simple tokenization

215

"""

216

217

def advanced_tokenizer(self, expression: str):

218

"""

219

Return an iterable of Token from an expression string using advanced tokenization.

220

221

Parameters:

222

- expression: License expression string to tokenize

223

224

Returns:

225

Iterator of Token objects from advanced tokenization

226

"""

227

```

228

229

### Usage Examples

230

231

#### Basic Parsing

232

233

```python

234

from license_expression import Licensing

235

236

licensing = Licensing()

237

expr = licensing.parse('MIT or Apache-2.0')

238

print(str(expr)) # 'MIT OR Apache-2.0'

239

```

240

241

#### Validation with Error Handling

242

243

```python

244

# Validate an expression with unknown licenses

245

result = licensing.validate('MIT and UnknownLicense')

246

print(result.errors) # ['Unknown license key(s): UnknownLicense']

247

print(result.invalid_symbols) # ['UnknownLicense']

248

249

# Strict parsing raises exceptions

250

try:

251

licensing.parse('MIT and UnknownLicense', validate=True, strict=True)

252

except ExpressionParseError as e:

253

print(f"Parse error: {e}")

254

```

255

256

#### Expression Comparison

257

258

```python

259

# Test equivalence

260

expr1 = 'MIT or (Apache-2.0 and GPL-2.0)'

261

expr2 = '(GPL-2.0 and Apache-2.0) or MIT'

262

print(licensing.is_equivalent(expr1, expr2)) # True

263

264

# Test containment

265

broad_expr = 'MIT or Apache-2.0 or GPL-2.0'

266

specific_expr = 'MIT'

267

print(licensing.contains(broad_expr, specific_expr)) # True

268

```

269

270

#### Symbol Extraction

271

272

```python

273

expression = 'MIT with Exception or (Apache-2.0 and GPL-2.0+)'

274

symbols = licensing.license_symbols(expression)

275

for symbol in symbols:

276

print(f"Key: {symbol.key}, Exception: {symbol.is_exception}")

277

```

278

279

### Boolean Operations

280

281

The Licensing class provides boolean operators for combining expressions:

282

283

```python { .api }

284

class Licensing:

285

AND: type # Boolean AND operator class

286

OR: type # Boolean OR operator class

287

288

def __call__(self, *args, **kwargs):

289

"""Create boolean expressions using the licensing instance as callable."""

290

```

291

292

#### Boolean Operation Examples

293

294

```python

295

# Create expressions using boolean operations

296

mit = licensing.parse('MIT')

297

apache = licensing.parse('Apache-2.0')

298

299

# Combine with AND

300

combined_and = licensing.AND(mit, apache)

301

print(str(combined_and)) # 'MIT AND Apache-2.0'

302

303

# Combine with OR

304

combined_or = licensing.OR(mit, apache)

305

print(str(combined_or)) # 'MIT OR Apache-2.0'

306

```

307

308

### Expression Simplification

309

310

```python

311

# Simplify complex expressions

312

complex_expr = licensing.parse('MIT or (MIT and Apache-2.0) or MIT')

313

simplified = complex_expr.simplify()

314

print(str(simplified)) # 'MIT'

315

316

# Simplify with normalization

317

redundant = licensing.parse('(A and B) or (B and A) or A')

318

simplified = redundant.simplify()

319

print(str(simplified)) # 'A OR (A AND B)' or similar simplified form

320

```

321

322

### Advanced License Analysis

323

324

```python

325

from license_expression import get_spdx_licensing

326

327

licensing = get_spdx_licensing()

328

expression = 'MIT or (Apache-2.0 and GPL-2.0+) or MIT'

329

330

# Get primary license information

331

primary_symbol = licensing.primary_license_symbol(expression)

332

primary_key = licensing.primary_license_key(expression)

333

print(f"Primary license: {primary_key}") # 'MIT'

334

335

# Get all license keys (with and without duplicates)

336

all_keys = licensing.license_keys(expression, unique=False)

337

unique_keys = licensing.license_keys(expression, unique=True)

338

print(f"All keys: {all_keys}") # ['MIT', 'Apache-2.0', 'GPL-2.0+', 'MIT']

339

print(f"Unique keys: {unique_keys}") # ['MIT', 'Apache-2.0', 'GPL-2.0+']

340

341

# Find unknown licenses

342

unknown_keys = licensing.unknown_license_keys('MIT and UnknownLicense')

343

print(f"Unknown keys: {unknown_keys}") # ['UnknownLicense']

344

```

345

346

### Expression Deduplication

347

348

```python

349

# Deduplication preserves license choices (unlike simplification)

350

complex_expr = 'MIT or Apache-2.0 or MIT or (GPL-2.0 and MIT)'

351

deduplicated = licensing.dedup(complex_expr)

352

print(str(deduplicated)) # Removes MIT duplicates but preserves structure

353

354

# Compare with simplification

355

simplified = licensing.parse(complex_expr).simplify()

356

print(str(simplified)) # May be more aggressively simplified

357

```

358

359

### Advanced Tokenization

360

361

```python

362

expression = 'MIT WITH Classpath-exception-2.0 OR Apache-2.0'

363

364

# Tokenize with different methods

365

for token_obj, token_string, position in licensing.tokenize(expression):

366

print(f"Token: '{token_string}' at position {position}, type: {type(token_obj)}")

367

368

# Get advanced tokenizer for reuse

369

tokenizer = licensing.get_advanced_tokenizer()

370

```