diff --git a/src/contracts/__init__.py b/src/contracts/__init__.py index eea09362..813e7a76 100644 --- a/src/contracts/__init__.py +++ b/src/contracts/__init__.py @@ -1,4 +1,4 @@ -__version__ = '1.8.12' +__version__ = '1.8.13' import logging diff --git a/src/contracts/interface.py b/src/contracts/interface.py index 2d28f787..276651e4 100644 --- a/src/contracts/interface.py +++ b/src/contracts/interface.py @@ -12,10 +12,10 @@ class Where(object): All parsed elements contain a reference to a :py:class:`Where` object so that we can output pretty error messages. - - + + Character should be >= len(string) (possibly outside the string). - Character_end should be >= character (so that you can splice with + Character_end should be >= character (so that you can splice with string[character:character_end]) """ @@ -38,7 +38,7 @@ def __init__(self, string, character, character_end=None): # or (character <= character_end - 1)): # character += 1 # else: - # break + # break self.line, self.col = line_and_col(character, string) if character_end is not None: @@ -93,7 +93,7 @@ def __str__(self): # mark = 'here or nearby' def format_where(w, context_before=3, mark=None, arrow=True, use_unicode=True, no_mark_arrow_if_longer_than=3): - s = u'' + s = '' if w.filename: s += 'In file %r:\n' % w.filename lines = w.string.split('\n') @@ -108,7 +108,7 @@ def format_where(w, context_before=3, mark=None, arrow=True, for i in range(start, w.line + 1): # suppress empty lines if one_written or lines[i].strip(): - s += (u"%s%s\n" % (pattern % (i + 1), lines[i])) + s += ("%s%s\n".format(pattern % (i + 1), lines[i],)) one_written = True fill = len(pattern % maxi) @@ -119,29 +119,29 @@ def format_where(w, context_before=3, mark=None, arrow=True, space_before = Where(w.string, char0, char0_end) nindent = printable_length_where(space_before) - space = u' ' * fill + u' ' * nindent + space = ' ' * fill + ' ' * nindent if w.col_end is not None: if w.line == w.line_end: num_highlight = printable_length_where(w) - s += space + u'~' * num_highlight + '\n' - space += u' ' * (num_highlight / 2) + s += space + '~' * num_highlight + '\n' + space += ' ' * (num_highlight / 2) else: # cannot highlight if on different lines num_highlight = None pass else: num_highlight = None - # Do not add the arrow and the mark if we have a long underline string + # Do not add the arrow and the mark if we have a long underline string disable_mark_arrow = (num_highlight is not None) and (no_mark_arrow_if_longer_than < num_highlight) if not disable_mark_arrow: if arrow: if use_unicode: - s += space + u'↑\n' + s += space + '↑\n' else: - s += space + u'^\n' - s += space + u'|\n' + s += space + '^\n' + s += space + '|\n' if mark is not None: s += space + mark @@ -173,7 +173,7 @@ def line_and_col(loc, strg): from .utils import check_isinstance check_isinstance(loc, int) check_isinstance(strg, six.string_types) - # first find the line + # first find the line lines = strg.split('\n') if loc == len(strg): @@ -433,8 +433,8 @@ def check_contract(self, context, value, silent): # @UnusedVariable context. This is the function that subclasses must implement. If silent = False, do not bother with creating detailed error messages yet. - This is for performance optimization. - + This is for performance optimization. + :param context: The context in which expressions are evaluated. :type context: """ @@ -548,7 +548,6 @@ def clipped_repr(x, clip): s = "%s%s" % (s[:cut], clip_tag) return s - # TODO: add checks for these functions @@ -620,7 +619,7 @@ def describe_value_multiline(x): try: # This fails for classes final = "{}\n{}".format(desc, x.__repr__()) - except: # XXX + except: # XXX final = "%s\n%s" % (desc, x) return final diff --git a/src/contracts/library/map.py b/src/contracts/library/map.py index bae047dc..d045dda7 100644 --- a/src/contracts/library/map.py +++ b/src/contracts/library/map.py @@ -1,7 +1,11 @@ from ..interface import Contract, ContractNotRespected from ..syntax import (W, contract_expression, O, S, add_contract, add_keyword, Keyword) -import collections + +try: + import collections.abc as collectionsAbc # python 3.6+ +except ImportError: + import collections as collectionsAbc class Map(Contract): @@ -13,7 +17,7 @@ def __init__(self, length=None, key_c=None, value_c=None, where=None): self.value_c = value_c def check_contract(self, context, value, silent): - if not isinstance(value, collections.Mapping): + if not isinstance(value, collectionsAbc.Mapping): error = 'Expected a Mapping, got %r.' % value.__class__.__name__ raise ContractNotRespected(contract=self, error=error, value=value, context=context) @@ -57,7 +61,7 @@ def parse_action(s, loc, tokens): length_spec = S('[') - contract_expression('length') - S(']') kv_spec = ('(' - O(contract_expression('key')) + ':' - + O(contract_expression('value')) - ')') + +O(contract_expression('value')) - ')') dict_contract = Keyword('map') + O(length_spec) + O(kv_spec) dict_contract.setParseAction(Map.parse_action) diff --git a/src/contracts/library/miscellaneous_aliases.py b/src/contracts/library/miscellaneous_aliases.py index 8d9844da..1717c5c2 100644 --- a/src/contracts/library/miscellaneous_aliases.py +++ b/src/contracts/library/miscellaneous_aliases.py @@ -1,12 +1,16 @@ -import collections - +try: + import collections.abc as collectionsAbc # python 3.6+ +except ImportError: + import collections as collectionsAbc def ist(C): + def f(x): f.__name__ = 'isinstance_of_%s' % C.__name__ if not isinstance(x, C): raise ValueError('Value is not an instance of %s.' % C.__name__) + return f @@ -14,33 +18,32 @@ def m_new_contract(name, f): from contracts.library.extensions import CheckCallable from contracts.library.extensions import Extension Extension.registrar[name] = CheckCallable(f) - -m_new_contract('Container', ist(collections.Container)) -# todo: Iterable(x) -m_new_contract('Iterable', ist(collections.Iterable)) - -m_new_contract('Hashable', ist(collections.Hashable)) +m_new_contract('Container', ist(collectionsAbc.Container)) +# todo: Iterable(x) +m_new_contract('Iterable', ist(collectionsAbc.Iterable)) +m_new_contract('Hashable', ist(collectionsAbc.Hashable)) -m_new_contract('Iterator', ist(collections.Iterator)) -m_new_contract('Sized', ist(collections.Sized)) -m_new_contract('Callable', ist(collections.Callable)) -m_new_contract('Sequence', ist(collections.Sequence)) -m_new_contract('Set', ist(collections.Set)) -m_new_contract('MutableSequence', ist(collections.MutableSequence)) -m_new_contract('MutableSet', ist(collections.MutableSet)) -m_new_contract('Mapping', ist(collections.Mapping)) -m_new_contract('MutableMapping', ist(collections.MutableMapping)) -#new_contract('MappingView', ist(collections.MappingView)) -#new_contract('ItemsView', ist(collections.ItemsView)) -#new_contract('ValuesView', ist(collections.ValuesView)) +m_new_contract('Iterator', ist(collectionsAbc.Iterator)) +m_new_contract('Sized', ist(collectionsAbc.Sized)) +m_new_contract('Callable', ist(collectionsAbc.Callable)) +m_new_contract('Sequence', ist(collectionsAbc.Sequence)) +m_new_contract('Set', ist(collectionsAbc.Set)) +m_new_contract('MutableSequence', ist(collectionsAbc.MutableSequence)) +m_new_contract('MutableSet', ist(collectionsAbc.MutableSet)) +m_new_contract('Mapping', ist(collectionsAbc.Mapping)) +m_new_contract('MutableMapping', ist(collectionsAbc.MutableMapping)) +# new_contract('MappingView', ist(collections.MappingView)) +# new_contract('ItemsView', ist(collections.ItemsView)) +# new_contract('ValuesView', ist(collections.ValuesView)) # Not a lambda to have better messages -def is_None(x): +def is_None(x): return x is None + m_new_contract('None', is_None) m_new_contract('NoneType', is_None) diff --git a/src/contracts/library/seq.py b/src/contracts/library/seq.py index 9d8586e7..5e46cdc6 100644 --- a/src/contracts/library/seq.py +++ b/src/contracts/library/seq.py @@ -1,7 +1,12 @@ from ..interface import Contract, ContractNotRespected from ..syntax import (add_contract, W, contract_expression, O, S, add_keyword, Keyword) -import collections + +try: + import collections.abc as collectionsAbc # python 3.6+ +except ImportError: + import collections as collectionsAbc + from past.builtins import xrange try: @@ -38,7 +43,7 @@ def check_contract(self, context, value, silent): return - if not isinstance(value, collections.Sequence): + if not isinstance(value, collectionsAbc.Sequence): error = 'Expected a Sequence, got %r.' % value.__class__.__name__ raise ContractNotRespected(self, error, value, context) diff --git a/src/contracts/library/sets.py b/src/contracts/library/sets.py index dc37279c..26d9d0f4 100644 --- a/src/contracts/library/sets.py +++ b/src/contracts/library/sets.py @@ -1,7 +1,10 @@ from ..interface import Contract, ContractNotRespected, describe_type from ..syntax import (Keyword, O, S, W, add_contract, add_keyword, contract_expression) -import collections +try: + import collections.abc as collectionsAbc # python 3.6+ +except ImportError: + import collections as collectionsAbc class ASet(Contract): @@ -13,7 +16,7 @@ def __init__(self, length_contract=None, self.elements_contract = elements_contract def check_contract(self, context, value, silent): - if not isinstance(value, collections.Set): + if not isinstance(value, collectionsAbc.Set): error = 'Expected a set, got %r.' % describe_type(value) raise ContractNotRespected(self, error, value, context) diff --git a/src/contracts/main.py b/src/contracts/main.py index 7722cb3e..70d07023 100644 --- a/src/contracts/main.py +++ b/src/contracts/main.py @@ -146,10 +146,10 @@ def tmp_wrap(f): # do not change name (see above) try: return contracts_decorate(f, **kwargs) except ContractSyntaxError as e: - msg = u"Cannot decorate function %s:" % f.__name__ + msg = "Cannot decorate function %s:".format(f.__name__) from .utils import indent import traceback - msg += u'\n\n' + indent(traceback.format_exc(), u' ') + msg += '\n\n' + indent(traceback.format_exc(), ' ') raise ContractSyntaxError(msg, e.where) # erase the stack except ContractDefinitionError as e: @@ -596,7 +596,7 @@ def new_contract_impl(identifier, condition): loc = e.loc if loc >= len(identifier): loc -= 1 - where = Where(identifier, character=loc) #line=e.lineno, column=e.col) + where = Where(identifier, character=loc) # line=e.lineno, column=e.col) # msg = 'Error in parsing string: %s' % e msg = ('The given identifier %r does not correspond to my idea ' 'of what an identifier should look like;\n%s\n%s' @@ -659,7 +659,7 @@ def new_contract_impl(identifier, condition): assert c == expected, \ 'Expected %r, got %r.' % (c, expected) # pragma: no cover except ContractSyntaxError: # pragma: no cover - #assert False, 'Cannot parse %r: %s' % (identifier, e) + # assert False, 'Cannot parse %r: %s' % (identifier, e) raise return contract diff --git a/src/contracts/testing/test_unicode_literals.py b/src/contracts/testing/test_unicode_literals.py index 30b21862..a0896e43 100644 --- a/src/contracts/testing/test_unicode_literals.py +++ b/src/contracts/testing/test_unicode_literals.py @@ -7,10 +7,11 @@ import unittest + class TestParsingNumbers(unittest.TestCase): def test_unicode_literal(self): - r = parse_contract_string(u'int') + r = parse_contract_string('int') print(r) def test_unicode_literal2(self): @@ -19,5 +20,4 @@ def test_unicode_literal2(self): def f(x): pass - f('') diff --git a/src/contracts/utils.py b/src/contracts/utils.py index df1ffa1f..22b7157b 100644 --- a/src/contracts/utils.py +++ b/src/contracts/utils.py @@ -19,17 +19,17 @@ def indent(s, prefix, first=None): if not isinstance(s, six.string_types): - s = u'{}'.format(s) + s = '{}'.format(s) assert isinstance(prefix, six.string_types) try: lines = s.split('\n') except UnicodeDecodeError: - print(type(s)) # XXX - print(s) # XXX + print(type(s)) # XXX + print(s) # XXX lines = [s] if not lines: - return u'' + return '' if first is None: first = prefix @@ -40,8 +40,8 @@ def indent(s, prefix, first=None): first = ' ' * (m - len(first)) + first # differnet first prefix - res = [u'%s%s' % (prefix, line.rstrip()) for line in lines] - res[0] = u'%s%s' % (first, lines[0].rstrip()) + res = ['%s%s'.format(prefix, line.rstrip()) for line in lines] + res[0] = '%s%s'.format(first, lines[0].rstrip()) return '\n'.join(res) @@ -117,7 +117,7 @@ def _get_str(x, informal): def format_list_long(l, informal=False): """ - - My + - My first - Second """ @@ -163,9 +163,9 @@ def raise_wrapped(etype, e, msg, compact=False, **kwargs): """ Raises an exception of type etype by wrapping another exception "e" with its backtrace and adding the objects in kwargs as formatted by format_obs. - + if compact = False, write the whole traceback, otherwise just str(e). - + exc = output of sys.exc_info() """ @@ -202,6 +202,7 @@ def raise_wrapped_make(etype, e, msg, compact=False, **kwargs): return etype(s) + def _format_exc(msg, **kwargs): check_isinstance(msg, six.text_type) s = msg @@ -212,7 +213,7 @@ def _format_exc(msg, **kwargs): def raise_desc(etype, msg, args_first=False, **kwargs): """ - + Example: raise_desc(ValueError, "I don't know", a=a, b=b) """ @@ -230,7 +231,6 @@ def raise_desc(etype, msg, args_first=False, **kwargs): raise etype(s) - # # # @@ -269,9 +269,9 @@ def raise_desc(etype, msg, args_first=False, **kwargs): # n = n+1 # return list - # from decorator import decorator # @UnresolvedImport + def ignore_typeerror(f): """ Recasts TypeError as Exception; otherwise pyparsing gets confused. """