def make_safe_property(method, name, should_be_property=True): if not should_be_property: return method(None) def deleter(method, self, *args, **kw): if isinstance(self, type): # if the attribute has to be deleted from a class object # we cannot use ``del self.__dict__[name]`` directly because we cannot # modify a mappingproxy object. Thus, we have to delete it in our # proxy __dict__. del overwritten_object_handlers[(id(self), method.__name__)] else: # if the attribute has to be deleted from an instance object # we are able to directly delete it from the object's __dict__. del self.__dict__[name] def setter(method, self, other): if isinstance(self, type): # if the attribute has to be set to a class object # we cannot use ``self.__dict__[name] = other`` directly because we cannot # modify a mappingproxy object. Thus, we have to set it in our # proxy __dict__. overwritten_object_handlers[(id(self), method.__name__)] = other else: # if the attribute has to be set to an instance object # we are able to directly set it in the object's __dict__. self.__dict__[name] = other return builtins.property( fget=method, fset=partial(setter, method), fdel=partial(deleter, method), )
def make_safe_property(method, name, should_be_property=True): if not should_be_property: return method(None) def deleter(self, *args, **kw): pass def setter(self, other): pass return builtins.property(fget=method, fset=setter, fdel=deleter)
def make_safe_property(method, name, should_be_property=True): if not should_be_property: return method(None) def deleter(self, *args, **kw): pass def setter(self, other): pass return builtins.property( fget=method, fset=setter, fdel=deleter, )
def make_safe_property(method, name, should_be_property=True): if not should_be_property: return method(None) def deleter(method, self, *args, **kw): del overwritten_object_handlers[(id(self), method.__name__)] def setter(method, self, other): overwritten_object_handlers[(id(self), method.__name__)] = other return builtins.property( fget=method, fset=partial(setter, method), fdel=partial(deleter, method), )
class AssertionBuilder(object): def __init__(self, name=None, negative=False, obj=None, callable_args=None, callable_kw=None): self._name = name self.negative = negative self.obj = obj self._callable_args = callable_args or [] self._callable_kw = callable_kw or {} self._that = AssertionHelper(self.obj) def __call__(self, obj): self.obj = obj if isinstance(obj, self.__class__): self.obj = obj.obj self._callable_args = obj._callable_args self._callable_kw = obj._callable_kw self._that = AssertionHelper(self.obj) return self def __getattr__(self, attr): special_case = False special_case = attr in (POSITIVES + NEGATIVES) negative = attr in NEGATIVES if special_case: return AssertionBuilder(attr, negative=negative, obj=self.obj, callable_args=self._callable_args, callable_kw=self._callable_kw) return super(AssertionBuilder, self).__getattribute__(attr) @assertionproperty def callable(self): if self.negative: assert not callable(self.obj), ( 'expected `{0}` to not be callable but it is'.format( safe_repr(self.obj))) else: assert callable(self.obj), ('expected {0} to be callable'.format( safe_repr(self.obj))) return True @assertionproperty def be(self): return IdentityAssertion(self) being = be @assertionproperty def not_be(self): return IdentityAssertion(self.should_not) not_being = not_be @assertionproperty def not_have(self): return self.should_not @assertionproperty def to_not(self): return self.should_not @assertionproperty def to(self): return self which = to @assertionproperty def when(self): return self @assertionproperty def have(self): return self @assertionproperty def with_value(self): return self def property(self, name): has_it = hasattr(self.obj, name) if self.negative: assert not has_it, ('%r should not have the property `%s`, ' 'but it is %r' % (self.obj, name, getattr(self.obj, name))) return True assert has_it, ("%r should have the property `%s` but does not" % (self.obj, name)) return expect(getattr(self.obj, name)) def key(self, name): has_it = name in self.obj if self.negative: assert not has_it, ('%r should not have the key `%s`, ' 'but it is %r' % (self.obj, name, self.obj[name])) return True assert has_it, ("%r should have the key `%s` but does not" % (self.obj, name)) return expect(self.obj[name]) @assertionproperty def empty(self): representation = safe_repr(self.obj) length = len(self.obj) if self.negative: assert length > 0, ( "expected `{0}` to not be empty".format(representation)) else: assert length == 0, ( "expected `{0}` to be empty but it has {1} items".format( representation, length)) return True @assertionproperty def ok(self): if self.negative: msg = 'expected `{0}` to be falsy'.format(self.obj) assert not bool(self.obj), msg else: msg = 'expected `{0}` to be truthy'.format(self.obj) assert bool(self.obj), msg return True truthy = ok true = ok @assertionproperty def falsy(self): if self.negative: msg = 'expected `{0}` to be truthy'.format(self.obj) assert bool(self.obj), msg else: msg = 'expected `{0}` to be falsy'.format(self.obj) assert not bool(self.obj), msg return True false = falsy @assertionproperty def none(self): if self.negative: assert self.obj is not None, ( r"expected `{0}` to not be None".format(self.obj)) else: assert self.obj is None, (r"expected `{0}` to be None".format( self.obj)) return True @assertionmethod def within_range(self, start, end): start = ensure_type('within_range', int, start) end = ensure_type('within_range', int, end) subject = ensure_type('within_range', int, self.obj) is_within_range = subject >= start and subject <= end if self.negative: if is_within_range: raise AssertionError( 'expected {0} to NOT be within {1} and {2}'.format( subject, start, end)) return not is_within_range else: if not is_within_range: raise AssertionError( 'expected {0} to be within {1} and {2}'.format( subject, start, end)) return is_within_range @assertionmethod def within(self, first, *rest): if isinstance(first, Iterable): collection_should = AssertionHelper(first) if self.negative: return collection_should.does_not_contain(self.obj) else: return collection_should.contains(self.obj) elif len(rest) == 1: return self.within_range(first, rest[0]) else: if self.negative: ppath = '{0}.should_not.be.within'.format(self.obj) else: ppath = '{0}.should.be.within'.format(self.obj) raise AssertionError( ('{0}({1}, {2}) must be called with either a iterable:\n' '{0}([1, 2, 3, 4])\n' 'or with a range of numbers:' '{0}(1, 3000)').format(ppath, first, ", ".join([repr(x) for x in rest]))) @assertionmethod def equal(self, what, epsilon=None): try: comparison = DeepComparison(self.obj, what, epsilon).compare() error = False except AssertionError as e: error = e comparison = None if isinstance(comparison, DeepExplanation): error = comparison.get_assertion(self.obj, what) if self.negative: if error: return True msg = '%s should differ from %s, but is the same thing' raise AssertionError(msg % (safe_repr(self.obj), safe_repr(what))) else: if not error: return True raise error eql = equal equals = equal equal_to = equal @assertionmethod def different_of(self, what): differ = difflib.Differ() source = self.obj.strip().splitlines(True) destination = what.strip().splitlines(True) result = differ.compare(source, destination) difference = "".join(result) if self.negative: if self.obj != what: assert not difference, "Difference:\n\n{0}".format(difference) else: if self.obj == what: raise AssertionError("{0} should be different of {1}".format( self.obj, what)) return True @assertionmethod def an(self, klass): if isinstance(klass, type): class_name = klass.__name__ elif isinstance(klass, string_types): class_name = klass.strip() else: class_name = text_type(klass) is_vowel = class_name[0] in 'aeiou' if isinstance(klass, string_types): if '.' in klass: items = klass.split('.') first = items.pop(0) if not items: items = [first] first = '_abcoll' else: if sys.version_info <= (3, 0, 0): first = '__builtin__' else: first = 'builtins' items = [klass] klass = reduce(getattr, items, __import__(first)) suffix = is_vowel and "n" or "" if self.negative: assert not isinstance( self.obj, klass), ('expected `{0}` to not be a{1} {2}'.format( self.obj, suffix, class_name)) else: assert isinstance(self.obj, klass), ('expected `{0}` to be a{1} {2}'.format( self.obj, suffix, class_name)) return True a = an @assertionmethod def greater_than(self, dest): if self.negative: msg = "expected `{0}` to not be greater than `{1}`".format( self.obj, dest) assert not self.obj > dest, msg else: msg = "expected `{0}` to be greater than `{1}`".format( self.obj, dest) assert self.obj > dest, msg return True @assertionmethod def greater_than_or_equal_to(self, dest): if self.negative: msg = "expected `{0}` to not be greater than or equal to `{1}`".format( self.obj, dest) assert not self.obj >= dest, msg else: msg = "expected `{0}` to be greater than or equal to `{1}`".format( self.obj, dest) assert self.obj >= dest, msg return True @assertionmethod def lower_than(self, dest): if self.negative: msg = "expected `{0}` to not be lower than `{1}`".format( self.obj, dest) assert not self.obj < dest, msg else: msg = "expected `{0}` to be lower than `{1}`".format( self.obj, dest) assert self.obj < dest, msg return True @assertionmethod def lower_than_or_equal_to(self, dest): if self.negative: msg = "expected `{0}` to not be lower than or equal to `{1}`".format( self.obj, dest) assert not self.obj <= dest, msg else: msg = "expected `{0}` to be lower than or equal to `{1}`".format( self.obj, dest) assert self.obj <= dest, msg return True @assertionmethod def below(self, num): if self.negative: msg = "{0} should not be below {1}".format(self.obj, num) assert not self.obj < num, msg else: msg = "{0} should be below {1}".format(self.obj, num) assert self.obj < num, msg return True @assertionmethod def above(self, num): if self.negative: msg = "{0} should not be above {1}".format(self.obj, num) assert not self.obj > num, msg else: msg = "{0} should be above {1}".format(self.obj, num) assert self.obj > num, msg return True @assertionmethod def length_of(self, num): if self.negative: return self._that.len_is_not(num) return self._that.len_is(num) def called_with(self, *args, **kw): self._callable_args = args self._callable_kw = kw return self called = builtins.property(called_with) @assertionmethod def throw(self, *args, **kw): _that = AssertionHelper(self.obj, with_args=self._callable_args, and_kwargs=self._callable_kw) if self.negative: msg = ("{0} called with args {1} and kwargs {2} should " "not raise {3} but raised {4}") exc = args and args[0] or Exception try: self.obj(*self._callable_args, **self._callable_kw) return True except Exception as e: err = msg.format( self.obj, self._that._callable_args, self._that._callable_kw, exc, e, ) raise AssertionError(err) return _that.raises(*args, **kw) thrown = throw raised = thrown @assertionmethod def return_value(self, value): return_value = self.obj(*self._callable_args, **self._callable_kw) return this(return_value).should.equal(value) returned_the_value = return_value @assertionmethod def look_like(self, value): if self.negative: try: self._that.looks_like(value) except AssertionError: return True else: msg = '%r should not look like %r but does' raise AssertionError(msg % (self.obj, value)) return self._that.looks_like(value) @assertionmethod def contain(self, what): obj = self.obj if self.negative: return expect(what).to.not_be.within(obj) else: return expect(what).to.be.within(obj) @assertionmethod def match(self, regex, *args): obj_repr = repr(self.obj) assert isinstance(self.obj, basestring), ( "{0} should be a string in order to compare using .match()".format( obj_repr)) matched = re.search(regex, self.obj, *args) modifiers_map = { re.I: "i", re.L: "l", re.M: "m", re.S: "s", re.U: "u", } modifiers = "".join([modifiers_map.get(x, "") for x in args]) regex_representation = '/{0}/{1}'.format(regex, modifiers) if self.negative: assert matched is None, ( "{0} should not match the regular expression {1}".format( obj_repr, regex_representation)) else: assert matched is not None, ( "{0} doesn't match the regular expression {1}".format( obj_repr, regex_representation)) return True
def assertionproperty(func): return builtins.property(assertionmethod(func))
class AssertionBuilder(object): def __init__(self, name=None, negative=False, obj=None): self._name = name self.negative = negative self.obj = obj self._callable_args = [] self._callable_kw = {} self._that = AssertionHelper(self.obj) def __call__(self, obj): self.obj = obj if isinstance(obj, self.__class__): self.obj = obj.obj self._that = AssertionHelper(self.obj) return self def __getattr__(self, attr): special_case = False special_case = attr in (POSITIVES + NEGATIVES) negative = attr in NEGATIVES if special_case: return AssertionBuilder(attr, negative=negative, obj=self.obj) return super(AssertionBuilder, self).__getattribute__(attr) @assertionproperty def callable(self): if self.negative: assert not callable(self.obj), ( 'expected `{0}` to not be callable but it is'.format( safe_repr(self.obj))) else: assert callable(self.obj), ('expected {0} to be callable'.format( safe_repr(self.obj))) return True @assertionproperty def be(self): import os if os.getenv("DEBUG"): import ipdb ipdb.set_trace() return IdentityAssertion(self) being = be @assertionproperty def not_be(self): return IdentityAssertion(self.should_not) not_being = not_be @assertionproperty def not_have(self): return self.should_not @assertionproperty def to_not(self): return self.should_not @assertionproperty def to(self): return self @assertionproperty def when(self): return self @assertionproperty def have(self): return self @assertionproperty def with_value(self): return self def property(self, name): has_it = hasattr(self.obj, name) if self.negative: assert not has_it, ('%r should not have the property `%s`, ' 'but it is %r' % (self.obj, name, getattr(self.obj, name))) return True assert has_it, ("%r should have the property `%s` but does not" % (self.obj, name)) return expect(getattr(self.obj, name)) def key(self, name): has_it = name in self.obj if self.negative: assert not has_it, ('%r should not have the key `%s`, ' 'but it is %r' % (self.obj, name, self.obj[name])) return True assert has_it, ("%r should have the key `%s` but does not" % (self.obj, name)) return expect(self.obj[name]) @assertionproperty def empty(self): representation = safe_repr(self.obj) length = len(self.obj) if self.negative: assert length > 0, ( u"expected `{0}` to not be empty".format(representation)) else: assert length == 0, ( u"expected `{0}` to be empty but it has {1} items".format( representation, length)) return True @assertionproperty def ok(self): if self.negative: msg = u'expected `{0}` to be falsy'.format(self.obj) assert not bool(self.obj), msg else: msg = u'expected `{0}` to be truthy'.format(self.obj) assert bool(self.obj), msg return True truthy = ok true = ok @assertionproperty def falsy(self): if self.negative: msg = u'expected `{0}` to be truthy'.format(self.obj) assert bool(self.obj), msg else: msg = u'expected `{0}` to be falsy'.format(self.obj) assert not bool(self.obj), msg return True false = falsy @assertionproperty def none(self): if self.negative: assert self.obj is not None, ( r"expected `{0}` to not be None".format(self.obj)) else: assert self.obj is None, (r"expected `{0}` to be None".format( self.obj)) return True @assertionmethod def within(self, first, *rest): if isinstance(first, Iterable): collection_should = AssertionHelper(first) else: args = [first] + list(rest) collection_should = AssertionHelper(list(range(*args))) if self.negative: return collection_should.does_not_contain(self.obj) else: return collection_should.contains(self.obj) @assertionmethod def equal(self, what): try: comparison = DeepComparison(self.obj, what).compare() error = False except AssertionError as e: error = e comparison = None if isinstance(comparison, DeepExplanation): error = comparison.get_assertion(self.obj, what) if self.negative: if error: return True msg = '%s should differ to %s, but is the same thing' raise AssertionError(msg % (safe_repr(self.obj), safe_repr(what))) else: if not error: return True raise error eql = equal equals = equal equal_to = equal @assertionmethod def an(self, klass): if isinstance(klass, type): class_name = klass.__name__ elif isinstance(klass, string_types): class_name = klass.strip() else: class_name = text_type(klass) is_vowel = class_name[0] in 'aeiou' if isinstance(klass, string_types): if '.' in klass: items = klass.split('.') first = items.pop(0) if not items: items = [first] first = '_abcoll' else: if sys.version_info <= (3, 0, 0): first = u'__builtin__' else: first = u'builtins' items = [klass] klass = reduce(getattr, items, __import__(first)) suffix = is_vowel and "n" or "" if self.negative: assert not isinstance( self.obj, klass), ('expected `{0}` to not be a{1} {2}'.format( self.obj, suffix, class_name)) else: assert isinstance(self.obj, klass), ('expected `{0}` to be a{1} {2}'.format( self.obj, suffix, class_name)) return True a = an @assertionmethod def greater_than(self, dest): if self.negative: msg = u"expected `{0}` to not be greater than `{1}`".format( self.obj, dest) assert not self.obj > dest, msg else: msg = u"expected `{0}` to be greater than `{1}`".format( self.obj, dest) assert self.obj > dest, msg return True @assertionmethod def greater_than_or_equal_to(self, dest): if self.negative: msg = u"expected `{0}` to not be greater than or equal to `{1}`".format( self.obj, dest) assert not self.obj >= dest, msg else: msg = u"expected `{0}` to be greater than or equal to `{1}`".format( self.obj, dest) assert self.obj >= dest, msg return True @assertionmethod def lower_than(self, dest): if self.negative: msg = u"expected `{0}` to not be lower than `{1}`".format( self.obj, dest) assert not self.obj < dest, msg else: msg = u"expected `{0}` to be lower than `{1}`".format( self.obj, dest) assert self.obj < dest, msg return True @assertionmethod def lower_than_or_equal_to(self, dest): if self.negative: msg = u"expected `{0}` to not be lower than or equal to `{1}`".format( self.obj, dest) assert not self.obj <= dest, msg else: msg = u"expected `{0}` to be lower than or equal to `{1}`".format( self.obj, dest) assert self.obj <= dest, msg return True @assertionmethod def below(self, num): if self.negative: msg = u"{0} should not be below {1}".format(self.obj, num) assert not self.obj < num, msg else: msg = u"{0} should be below {1}".format(self.obj, num) assert self.obj < num, msg return True @assertionmethod def above(self, num): if self.negative: msg = u"{0} should not be above {1}".format(self.obj, num) assert not self.obj > num, msg else: msg = u"{0} should be above {1}".format(self.obj, num) assert self.obj > num, msg return True @assertionmethod def length_of(self, num): if self.negative: return self._that.len_is_not(num) return self._that.len_is(num) @assertionmethod def called_with(self, *args, **kw): self._callable_args = args self._callable_kw = kw return self called = builtins.property(called_with) @assertionmethod def throw(self, *args, **kw): _that = AssertionHelper(self.obj, with_args=self._callable_args, and_kwargs=self._callable_kw) if self.negative: msg = (u"{0} called with args {1} and kwargs {2} should " "not raise {3} but raised {4}") exc = args and args[0] or Exception try: self.obj(*self._callable_args, **self._callable_kw) return True except Exception as e: err = msg.format( self.obj, self._that._callable_args, self._that._callable_kw, exc, e, ) raise AssertionError(err) return _that.raises(*args, **kw) @assertionmethod def return_value(self, value): return_value = self.obj(*self._callable_args, **self._callable_kw) return this(return_value).should.equal(value) @assertionmethod def look_like(self, value): if self.negative: try: self._that.looks_like(value) except AssertionError: return True else: msg = '%r should not look like %r but does' raise AssertionError(msg % (self.obj, value)) return self._that.looks_like(value) @assertionmethod def contain(self, what): if self.negative: return expect(what).to.not_be.within(self.obj) else: return expect(what).to.be.within(self.obj) @assertionmethod def match(self, regex, *args): obj_repr = repr(self.obj) assert isinstance(self.obj, basestring), ( "{0} should be a string in order to compare using .match()".format( obj_repr)) matched = re.search(regex, self.obj, *args) modifiers_map = { re.I: "i", re.L: "l", re.M: "m", re.S: "s", re.U: "u", } modifiers = "".join([modifiers_map.get(x, "") for x in args]) regex_representation = '/{0}/{1}'.format(regex, modifiers) if self.negative: assert matched is None, ( "{0} should not match the regular expression {1}".format( obj_repr, regex_representation)) else: assert matched is not None, ( "{0} doesn't match the regular expression {1}".format( obj_repr, regex_representation)) return True