@@ -576,8 +576,22 @@ def simple_tokenizer(self, expression):
576576
577577 def dedup (self , expression ):
578578 """
579- Return a de-duplicated LicenseExpression given a license expfession
579+ Return a deduplicated LicenseExpression given a license expression
580580 string or LicenseExpression object.
581+
582+ The deduplication process is similar to simplification but is specialized
583+ for working with license expressions. Simplification on the other hand
584+ is a generic boolean operation not aware of the specifis of license expressions.
585+
586+ The deduplication:
587+
588+ - Does not sort the licenses of sub-expression in an expression. They
589+ stay in the same order as in the original.
590+
591+ - Choices (as in MIT or GPL) are kept as-is and not treated as
592+ simplifiable. This way this avoid droping important choice options in
593+ complex expressions which is never desirable.
594+
581595 """
582596 exp = self .parse (expression )
583597 expressions = []
@@ -600,7 +614,7 @@ def dedup(self, expression):
600614 licensing = self ,
601615 )
602616 else :
603- raise Exception ('Unknown expression type: {}' . format ( repr ( expression )) )
617+ raise Exception (f 'Unknown expression type: { expression !r } ' )
604618 return deduped
605619
606620
@@ -717,8 +731,8 @@ def replace_with_subexpression_by_license_symbol(tokens, strict=False):
717731 Check validity of with subexpessions and raise ParseError as needed.
718732
719733 If `strict` is True also raise ParseError if the left hand side
720- LicenseSymbol has is_exception True or if the right hand side
721- LicenseSymbol has is_exception False.
734+ LicenseSymbol has is_exception True or if the right hand side LicenseSymbol
735+ has is_exception False.
722736 """
723737 token_groups = build_token_groups_for_with_subexpression (tokens )
724738
@@ -751,7 +765,7 @@ def replace_with_subexpression_by_license_symbol(tokens, strict=False):
751765
752766 else :
753767 # this should not be possible by design
754- raise Exception ('Licensing.tokenize is internally confused...:' + repr ( tval ) )
768+ raise Exception (f 'Licensing.tokenize is internally confused...: { tval !r } ' )
755769
756770 yield token
757771 continue
@@ -763,8 +777,8 @@ def replace_with_subexpression_by_license_symbol(tokens, strict=False):
763777 raise ParseError (
764778 TOKEN_SYMBOL , string , start , PARSE_INVALID_EXPRESSION )
765779
766- # from now on we have a tripple of tokens: a WITH sub-expression such as "A with
767- # B" seq of three tokens
780+ # from now on we have a tripple of tokens: a WITH sub-expression such as
781+ # "A with B" seq of three tokens
768782 lic_token , WITH , exc_token = token_group
769783
770784 token_string = ' ' .join ([
@@ -818,13 +832,14 @@ class Renderable(object):
818832
819833 def render (self , template = '{symbol.key}' , * args , ** kwargs ):
820834 """
821- Return a formatted string rendering for this expression using the `template`
822- format string to render each symbol. The variable available are `symbol.key`
823- and any other attribute that was attached to a license symbol instance and a
824- custom template can be provided to handle custom HTML rendering or similar.
835+ Return a formatted string rendering for this expression using the
836+ `template` format string to render each symbol. The variable available
837+ are `symbol.key` and any other attribute that was attached to a license
838+ symbol instance and a custom template can be provided to handle custom
839+ HTML rendering or similar.
825840
826- For symbols that hold multiple licenses (e.g. a WITH statement) the template
827- is applied to each symbol individually.
841+ For symbols that hold multiple licenses (e.g. a WITH statement) the
842+ template is applied to each symbol individually.
828843
829844 Note that when render() is called the *args and **kwargs are propagated
830845 recursively to any Renderable object render() method.
@@ -840,9 +855,18 @@ def render_as_readable(self, template='{symbol.key}', *args, **kwargs):
840855 """
841856 if isinstance (self , LicenseWithExceptionSymbol ):
842857 return self .render (
843- template = template , wrap_with_in_parens = False , * args , ** kwargs )
844- else :
845- return self .render (template = template , wrap_with_in_parens = True , * args , ** kwargs )
858+ template = template ,
859+ wrap_with_in_parens = False ,
860+ * args ,
861+ ** kwargs ,
862+ )
863+
864+ return self .render (
865+ template = template ,
866+ wrap_with_in_parens = True ,
867+ * args ,
868+ ** kwargs ,
869+ )
846870
847871
848872class BaseSymbol (Renderable , boolean .Symbol ):
@@ -862,6 +886,7 @@ def __contains__(self, other):
862886 """
863887 if not isinstance (other , BaseSymbol ):
864888 return False
889+
865890 if self == other :
866891 return True
867892
@@ -905,7 +930,8 @@ def __init__(self, key, aliases=tuple(), is_exception=False, *args, **kwargs):
905930 if not is_valid_license_key (key ):
906931 raise ExpressionError (
907932 'Invalid license key: the valid characters are: letters and numbers, '
908- 'underscore, dot, colon or hyphen signs and spaces: "%(key)s"' % locals ())
933+ f'underscore, dot, colon or hyphen signs and spaces: { key !r} '
934+ )
909935
910936 # normalize for spaces
911937 key = ' ' .join (key .split ())
@@ -1049,10 +1075,11 @@ def __lt__(self, other):
10491075@total_ordering
10501076class LicenseWithExceptionSymbol (BaseSymbol ):
10511077 """
1052- A LicenseWithExceptionSymbol represents a license "with" an exception as used in
1053- a license expression. It holds two LicenseSymbols objects: one for the left-hand
1054- license proper and one for the right-hand exception to this license and deals
1055- with the specifics of resolution, validation and representation.
1078+ A LicenseWithExceptionSymbol represents a license "with" an exception as
1079+ used in a license expression. It holds two LicenseSymbols objects: one for
1080+ the left-hand license proper and one for the right-hand exception to this
1081+ license and deals with the specifics of resolution, validation and
1082+ representation.
10561083 """
10571084
10581085 def __init__ (self , license_symbol , exception_symbol , strict = False , * args , ** kwargs ):
@@ -1180,8 +1207,11 @@ def render(self, template='{symbol.key}', *args, **kwargs):
11801207 rendered = arg .render (template , * args , ** kwargs )
11811208
11821209 else :
1183- print ('WARNING: object in expression is not renderable: falling back to plain string representation: %(arg)r.' )
11841210 # FIXME: CAN THIS REALLY HAPPEN since we only have symbols, or and AND?
1211+ print (
1212+ 'WARNING: object in expression is not renderable: '
1213+ f'falling back to plain string representation: { arg !r} .'
1214+ )
11851215 rendered = str (arg )
11861216
11871217 if arg .isliteral :
@@ -1199,7 +1229,8 @@ class AND(RenderableFunction, boolean.AND):
11991229
12001230 def __init__ (self , * args ):
12011231 if len (args ) < 2 :
1202- raise ExpressionError ('AND requires two or more licenses as in: MIT AND BSD' )
1232+ raise ExpressionError (
1233+ 'AND requires two or more licenses as in: MIT AND BSD' )
12031234 super (AND , self ).__init__ (* args )
12041235 self .operator = ' AND '
12051236
@@ -1211,7 +1242,8 @@ class OR(RenderableFunction, boolean.OR):
12111242
12121243 def __init__ (self , * args ):
12131244 if len (args ) < 2 :
1214- raise ExpressionError ('OR requires two or more licenses as in: MIT OR BSD' )
1245+ raise ExpressionError (
1246+ 'OR requires two or more licenses as in: MIT OR BSD' )
12151247 super (OR , self ).__init__ (* args )
12161248 self .operator = ' OR '
12171249
@@ -1389,22 +1421,23 @@ def combine_expressions(
13891421 licensing = Licensing (),
13901422):
13911423 """
1392- Return a combined LicenseExpression object with the `relation`,
1393- given a list of license `expressions` strings or LicenseExpression.
1394- If unique is True remove duplicates before combining expressions.
1424+ Return a combined LicenseExpression object with the `relation`, given a list
1425+ of license `expressions` strings or LicenseExpression. If unique is True
1426+ remove duplicates before combining expressions.
13951427
13961428 For example::
1397- >>> a = 'mit'
1398- >>> b = 'gpl'
1399- >>> str(combine_expressions([a, b]))
1400- 'mit AND gpl'
1401- >>> assert 'mit' == str(combine_expressions([a]))
1402- >>> combine_expressions([])
1403- >>> combine_expressions(None)
1404- >>> str(combine_expressions(('gpl', 'mit', 'apache',)))
1405- 'gpl AND mit AND apache'
1406- >>> str(combine_expressions(('gpl', 'mit', 'apache',), relation='OR'))
1407- 'gpl OR mit OR apache'
1429+
1430+ >>> a = 'mit'
1431+ >>> b = 'gpl'
1432+ >>> str(combine_expressions([a, b]))
1433+ 'mit AND gpl'
1434+ >>> assert 'mit' == str(combine_expressions([a]))
1435+ >>> combine_expressions([])
1436+ >>> combine_expressions(None)
1437+ >>> str(combine_expressions(('gpl', 'mit', 'apache',)))
1438+ 'gpl AND mit AND apache'
1439+ >>> str(combine_expressions(('gpl', 'mit', 'apache',), relation='OR'))
1440+ 'gpl OR mit OR apache'
14081441 """
14091442 if not expressions :
14101443 return
0 commit comments