diff --git a/setup.py b/setup.py index 5f2a911..9156a9c 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ setup( name='license-expression', - version='0.96', + version='0.97', license='apache-2.0', description=desc, long_description=desc, diff --git a/src/license_expression/__init__.py b/src/license_expression/__init__.py index dca8054..3b9184a 100644 --- a/src/license_expression/__init__.py +++ b/src/license_expression/__init__.py @@ -27,7 +27,6 @@ The main entry point is the Licensing object. """ - from __future__ import absolute_import from __future__ import unicode_literals from __future__ import print_function @@ -36,10 +35,10 @@ try: # Python 2 unicode - str = unicode + str = unicode # NOQA except NameError: # Python 3 - unicode = str + unicode = str # NOQA import collections from copy import copy @@ -71,7 +70,6 @@ from license_expression._pyahocorasick import Output from license_expression._pyahocorasick import Result - # append new error codes to PARSE_ERRORS by monkey patching PARSE_EXPRESSION_NOT_UNICODE = 100 if PARSE_EXPRESSION_NOT_UNICODE not in PARSE_ERRORS: @@ -119,6 +117,7 @@ class ExpressionError(Exception): KEYWORDS = tuple(kw.value for kw in _KEYWORDS) KEYWORDS_STRIPPED = tuple(k.strip() for k in KEYWORDS) + class Licensing(boolean.BooleanAlgebra): """ Define a mini language to parse, validate and compare license expressions. @@ -157,6 +156,7 @@ class Licensing(boolean.BooleanAlgebra): >>> assert l.license_symbols(parsed)[1].as_exception """ + def __init__(self, symbols=tuple(), quiet=True): """ Initialize a Licensing with an optional `symbols` sequence of LicenseSymbol @@ -571,6 +571,7 @@ class Renderable(object): """ An interface for renderable objects. """ + def render(self, template='{symbol.key}', *args, **kwargs): """ Return a formatted string rendering for this expression using the `template` @@ -598,16 +599,29 @@ def decompose(self): """ raise NotImplementedError + def __contains__(self, other): + """ + Test if expr is contained in this symbol. + """ + if not isinstance(other, BaseSymbol): + return False + if self == other: + return True + + return any(mine == other for mine in self.decompose()) + # validate license keys is_valid_license_key = re.compile(r'^[-\w\s\.\+]+$', re.UNICODE).match + #FIXME: we need to implement comparison!!!! @total_ordering class LicenseSymbol(BaseSymbol): """ A LicenseSymbol represents a license as used in a license expression. """ + def __init__(self, key, aliases=tuple(), is_exception=False, *args, **kwargs): if not key: raise ExpressionError( @@ -708,6 +722,7 @@ class LicenseSymbolLike(LicenseSymbol): A LicenseSymbolLike object wraps a symbol-like object to expose a LicenseSymbol behavior. """ + def __init__(self, symbol_like, *args, **kwargs): if not self.symbol_like(symbol_like): raise ExpressionError( @@ -744,6 +759,7 @@ class LicenseWithExceptionSymbol(BaseSymbol): license proper and one for the right-hand exception to this license and deals with the specifics of resolution, validation and representation. """ + def __init__(self, license_symbol, exception_symbol, strict=False, *args, **kwargs): """ Initialize a new LicenseWithExceptionSymbol from a `license_symbol` and a @@ -861,6 +877,7 @@ class AND(RenderableFunction, boolean.AND): """ Custom representation for the AND operator to uppercase. """ + def __init__(self, *args): super(AND, self).__init__(*args) self.operator = ' AND ' @@ -870,6 +887,7 @@ class OR(RenderableFunction, boolean.OR): """ Custom representation for the OR operator to uppercase. """ + def __init__(self, *args): super(OR, self).__init__(*args) self.operator = ' OR ' diff --git a/src/license_expression/_pyahocorasick.py b/src/license_expression/_pyahocorasick.py index 3492a6c..4c73709 100644 --- a/src/license_expression/_pyahocorasick.py +++ b/src/license_expression/_pyahocorasick.py @@ -24,6 +24,7 @@ logger = logging.getLogger(__name__) + def logger_debug(*args): return logger.debug(' '.join(isinstance(a, str) and a or repr(a) for a in args)) @@ -42,6 +43,7 @@ class Trie(object): A Trie and Aho-Corasick automaton. This behaves more or less like a mapping of key->value. This is the main entry point. """ + def __init__(self, ignore_case=True): """ Initialize a new Trie. diff --git a/tests/test__pyahocorasick.py b/tests/test__pyahocorasick.py index 1d33921..7b346b6 100644 --- a/tests/test__pyahocorasick.py +++ b/tests/test__pyahocorasick.py @@ -26,6 +26,7 @@ class TestTrie(unittest.TestCase): + def testAddedWordShouldBeCountedAndAvailableForRetrieval(self): t = Trie() t.add('python', 'value') @@ -94,7 +95,6 @@ def testItemsShouldReturnAllItemsAlreadyAddedToTheTrie(self): self.assertIn(('pascal', 4), result) self.assertIn(('php', 5), result) - def testKeysShouldReturnAllKeysAlreadyAddedToTheTrie(self): t = Trie() @@ -111,7 +111,6 @@ def testKeysShouldReturnAllKeysAlreadyAddedToTheTrie(self): self.assertIn('pascal', result) self.assertIn('php', result) - def testValuesShouldReturnAllValuesAlreadyAddedToTheTrie(self): t = Trie() @@ -155,6 +154,7 @@ def get_test_automaton(): assert expected == result def test_iter_vs_scan(self): + def get_test_automaton(): words = "( AND ) OR".split() t = Trie() @@ -198,6 +198,7 @@ def get_test_automaton(): assert expected == result def test_scan_with_unmatched(self): + def get_test_automaton(): words = "( AND ) OR".split() t = Trie() diff --git a/tests/test_license_expression.py b/tests/test_license_expression.py index e4ef55f..2186e80 100644 --- a/tests/test_license_expression.py +++ b/tests/test_license_expression.py @@ -13,7 +13,6 @@ # CONDITIONS OF ANY KIND, either express or implied. See the License for the # specific language governing permissions and limitations under the License. - from __future__ import absolute_import from __future__ import print_function from __future__ import unicode_literals @@ -22,7 +21,6 @@ from unittest import TestCase import sys - from boolean.boolean import PARSE_UNBALANCED_CLOSING_PARENS from boolean.boolean import PARSE_INVALID_SYMBOL_SEQUENCE @@ -535,6 +533,22 @@ def test_simplify_and_equivalent_and_contains(self): assert l.contains(expr2, expr4) + def test_contains_works_with_plain_symbol(self): + l = Licensing() + assert not l.contains('mit', 'mit and LGPL-2.1') + assert l.contains('mit and LGPL-2.1', 'mit') + assert l.contains('mit', 'mit') + assert not l.contains(l.parse('mit'), l.parse('mit and LGPL-2.1')) + assert l.contains(l.parse('mit and LGPL-2.1'), l.parse('mit')) + + assert l.contains('mit with GPL', 'GPL') + assert l.contains('mit with GPL', 'mit') + assert l.contains('mit with GPL', 'mit with GPL') + assert not l.contains('mit with GPL', 'GPL with mit') + assert not l.contains('mit with GPL', 'GPL and mit') + assert not l.contains('GPL', 'mit with GPL') + assert l.contains('mit with GPL and GPL and BSD', 'GPL and BSD') + def test_create_from_python(self): # Expressions can be built from Python expressions, using bitwise operators # between Licensing objects, but use with caution. The behavior is not as