Skip to content

Commit 9b40b97

Browse files
committed
Streamline dedup function with objects
Use and return LicenseExpression objects makes the code simpler. Refactored combine_expressions() to deal with LicenseExpression objects. Signed-off-by: Philippe Ombredanne <[email protected]>
1 parent c295a9f commit 9b40b97

File tree

1 file changed

+55
-59
lines changed

1 file changed

+55
-59
lines changed

src/license_expression/__init__.py

Lines changed: 55 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -585,47 +585,32 @@ def simple_tokenizer(self, expression):
585585

586586
def dedup(self, expression):
587587
"""
588-
Return a de-duplicated expression
588+
Return a de-duplicated LicenseExpression given a license expfession
589+
string or LicenseExpression object.
589590
"""
590591
exp = self.parse(expression)
591-
dedup_expression = ''
592-
expression_list = []
592+
expressions = []
593593
for arg in exp.args:
594-
if isinstance(arg, (self.AND, self.OR)):
595-
# Run this recursive function if there is another AND/OR expression
596-
# and add the expression to the expression_list.
597-
expression_list.append(self.dedup(arg))
594+
if isinstance(arg, (self.AND, self.OR,)):
595+
# Run this recursive function if there is another AND/OR
596+
# expression and add the expression to the expressions list.
597+
expressions.append(self.dedup(arg))
598598
else:
599-
# Get the license key from the expression as a list.
600-
exp_key = self.license_keys(arg)
601-
# We treat the license with exception as a single license key so
602-
# that it won't over de-dupped for case such as
603-
# gpl-2.0 WITH classpath exception AND gpl-2.0
604-
if type(arg).__name__ == 'LicenseWithExceptionSymbol':
605-
key = ' WITH '.join(exp_key)
606-
else:
607-
# The list should only contains 1 license symbol as the "AND"
608-
# and "OR" condition is taken care in the above
609-
# isinstance(arg, (self.AND, self.OR)) and the "WITH" condition
610-
# is taken care in the above if condition.
611-
key = exp_key[0]
612-
# Add the license key to the expression_list if it's not already
613-
# present.
614-
if not key in expression_list:
615-
expression_list.append(key)
616-
617-
if isinstance(expression, self.LicenseSymbol) or type(expression).__name__ == 'LicenseWithExceptionSymbol':
618-
dedup_expression = str(expression)
619-
elif isinstance(expression, self.AND):
620-
dedup_expression = combine_expressions(expression_list, relation='AND')
621-
elif isinstance(expression, self.OR):
622-
dedup_expression = combine_expressions(expression_list, relation='OR')
599+
expressions.append(arg)
600+
601+
if isinstance(exp, BaseSymbol):
602+
deduped = exp
603+
elif isinstance(exp, (self.AND, self.OR,)):
604+
relation = exp.__class__.__name__
605+
deduped = combine_expressions(
606+
expressions,
607+
relation=relation,
608+
unique=True,
609+
licensing=self,
610+
)
623611
else:
624612
raise Exception('Unknown expression type: {}'.format(repr(expression)))
625-
626-
# Put the parentheses between the expression for grouping purpose.
627-
dedup_expression = '({})'.format(dedup_expression)
628-
return dedup_expression
613+
return deduped
629614

630615

631616
def build_symbols_from_unknown_tokens(tokens):
@@ -1283,8 +1268,9 @@ def as_symbols(symbols):
12831268
yield LicenseSymbolLike(symbol)
12841269

12851270
else:
1286-
raise TypeError('%(symbol)r is not a unicode string '
1287-
'or a LicenseSymbol-like instance.' % locals())
1271+
raise TypeError(
1272+
'%(symbol)r is not a unicode string '
1273+
'or a LicenseSymbol-like instance.' % locals())
12881274

12891275

12901276
def validate_symbols(symbols, validate_keys=False):
@@ -1399,26 +1385,35 @@ def validate_symbols(symbols, validate_keys=False):
13991385
errors.append('Invalid key: a key cannot be an expression keyword: %(ikw)s.' % locals())
14001386

14011387
warnings = []
1402-
for dupeal in sorted(dupe_aliases):
1403-
errors.append('Duplicated or empty aliases ignored for license key: %(dupeal)r.' % locals())
1388+
for dupe_alias in sorted(dupe_aliases):
1389+
errors.append('Duplicated or empty aliases ignored for license key: %(dupe_alias)r.' % locals())
14041390

14051391
return warnings, errors
14061392

14071393

1408-
def combine_expressions(expressions, relation='AND', licensing=Licensing()):
1394+
def combine_expressions(
1395+
expressions,
1396+
relation='AND',
1397+
unique=True,
1398+
licensing=Licensing(),
1399+
):
14091400
"""
1410-
Return a combined license expression string with relation, given a list of
1411-
license expressions strings.
1412-
For example:
1413-
>>> a = 'mit'
1414-
>>> b = 'gpl'
1415-
>>> combine_expressions([a, b])
1416-
'mit AND gpl'
1417-
>>> assert 'mit' == combine_expressions([a])
1418-
>>> combine_expressions([])
1419-
>>> combine_expressions(None)
1420-
>>> combine_expressions(('gpl', 'mit', 'apache',))
1421-
'gpl AND mit AND apache'
1401+
Return a combined LicenseExpression object with the `relation`,
1402+
given a list of license `expressions` strings or LicenseExpression.
1403+
If unique is True remove duplicates before combining expressions.
1404+
1405+
For example::
1406+
>>> a = 'mit'
1407+
>>> b = 'gpl'
1408+
>>> str(combine_expressions([a, b]))
1409+
'mit AND gpl'
1410+
>>> assert 'mit' == str(combine_expressions([a]))
1411+
>>> combine_expressions([])
1412+
>>> combine_expressions(None)
1413+
>>> str(combine_expressions(('gpl', 'mit', 'apache',)))
1414+
'gpl AND mit AND apache'
1415+
>>> str(combine_expressions(('gpl', 'mit', 'apache',), relation='OR'))
1416+
'gpl OR mit OR apache'
14221417
"""
14231418
if not expressions:
14241419
return
@@ -1428,14 +1423,15 @@ def combine_expressions(expressions, relation='AND', licensing=Licensing()):
14281423
'expressions should be a list or tuple and not: {}'.format(
14291424
type(expressions)))
14301425

1431-
# Remove duplicate element in the expressions list
1432-
expressions = list(dict((x, True) for x in expressions).keys())
1426+
# only del with LicenseExpression objects
1427+
expressions = [licensing.parse(le, simple=True) for le in expressions]
1428+
1429+
# Remove duplicate expressions
1430+
if unique:
1431+
expressions = list({str(x): x for x in expressions}.values())
14331432

14341433
if len(expressions) == 1:
14351434
return expressions[0]
14361435

1437-
expressions = [licensing.parse(le, simple=True) for le in expressions]
1438-
if relation == 'OR':
1439-
return str(licensing.OR(*expressions))
1440-
else:
1441-
return str(licensing.AND(*expressions))
1436+
relation = {'AND': licensing.AND, 'OR': licensing.OR}[relation]
1437+
return relation(*expressions)

0 commit comments

Comments
 (0)