assertpy
Advanced tools
+213
-72
@@ -39,6 +39,10 @@ # Copyright (c) 2015-2017, Activision Publishing, Inc. | ||
| import inspect | ||
| from contextlib import contextmanager | ||
| import math | ||
| import contextlib | ||
| __version__ = '0.11' | ||
| __version__ = '0.12' | ||
| __tracebackhide__ = True # clean tracebacks via py.test integration | ||
| contextlib.__tracebackhide__ = True # monkey patch contextlib with clean py.teest tracebacks | ||
| if sys.version_info[0] == 3: | ||
@@ -58,3 +62,3 @@ str_types = (str,) | ||
| @contextmanager | ||
| @contextlib.contextmanager | ||
| def soft_assertions(): | ||
@@ -252,3 +256,3 @@ global _soft_ctx | ||
| if items[0] not in self.val: | ||
| if type(self.val) is dict: | ||
| if self._check_dict_like(self.val, return_as_bool=True): | ||
| self._err('Expected <%s> to contain key <%s>, but did not.' % (self.val, items[0])) | ||
@@ -258,8 +262,11 @@ else: | ||
| else: | ||
| missing = [] | ||
| for i in items: | ||
| if i not in self.val: | ||
| if type(self.val) is dict: | ||
| self._err('Expected <%s> to contain keys %s, but did not contain key <%s>.' % (self.val, items, i)) | ||
| else: | ||
| self._err('Expected <%s> to contain items %s, but did not contain <%s>.' % (self.val, items, i)) | ||
| missing.append(i) | ||
| if missing: | ||
| if self._check_dict_like(self.val, return_as_bool=True): | ||
| self._err('Expected <%s> to contain keys %s, but did not contain key%s %s.' % (self.val, self._fmt_items(items), '' if len(missing) == 0 else 's', self._fmt_items(missing))) | ||
| else: | ||
| self._err('Expected <%s> to contain items %s, but did not contain %s.' % (self.val, self._fmt_items(items), self._fmt_items(missing))) | ||
| return self | ||
@@ -275,5 +282,8 @@ | ||
| else: | ||
| found = [] | ||
| for i in items: | ||
| if i in self.val: | ||
| self._err('Expected <%s> to not contain items %s, but did contain <%s>.' % (self.val, items, i)) | ||
| found.append(i) | ||
| if found: | ||
| self._err('Expected <%s> to not contain items %s, but did contain %s.' % (self.val, self._fmt_items(items), self._fmt_items(found))) | ||
| return self | ||
@@ -286,5 +296,8 @@ | ||
| else: | ||
| extra = [] | ||
| for i in self.val: | ||
| if i not in items: | ||
| self._err('Expected <%s> to contain only %s, but did contain <%s>.' % (self.val, items, i)) | ||
| extra.append(i) | ||
| if extra: | ||
| self._err('Expected <%s> to contain only %s, but did contain %s.' % (self.val, self._fmt_items(items), self._fmt_items(extra))) | ||
| return self | ||
@@ -306,3 +319,3 @@ | ||
| raise TypeError('val is not iterable') | ||
| self._err('Expected <%s> to contain sequence %s, but did not.' % (self.val, items)) | ||
| self._err('Expected <%s> to contain sequence %s, but did not.' % (self.val, self._fmt_items(items))) | ||
@@ -353,3 +366,3 @@ def contains_duplicates(self): | ||
| return self | ||
| self._err('Expected <%s> to be in %s, but was not.' % (self.val, items)) | ||
| self._err('Expected <%s> to be in %s, but was not.' % (self.val, self._fmt_items(items))) | ||
@@ -363,3 +376,3 @@ def is_not_in(self, *items): | ||
| if self.val == i: | ||
| self._err('Expected <%s> to not be in %s, but was.' % (self.val, items)) | ||
| self._err('Expected <%s> to not be in %s, but was.' % (self.val, self._fmt_items(items))) | ||
| return self | ||
@@ -388,6 +401,15 @@ | ||
| def _validate_number(self): | ||
| """Raise TypeError if val is not numeric.""" | ||
| if isinstance(self.val, numbers.Number) is False: | ||
| raise TypeError('val is not numeric') | ||
| def _validate_real(self): | ||
| """Raise TypeError if val is not real number.""" | ||
| if isinstance(self.val, numbers.Real) is False: | ||
| raise TypeError('val is not real number') | ||
| def is_zero(self): | ||
| """Asserts that val is numeric and equal to zero.""" | ||
| if isinstance(self.val, numbers.Number) is False: | ||
| raise TypeError('val is not numeric') | ||
| self._validate_number() | ||
| return self.is_equal_to(0) | ||
@@ -397,6 +419,37 @@ | ||
| """Asserts that val is numeric and not equal to zero.""" | ||
| if isinstance(self.val, numbers.Number) is False: | ||
| raise TypeError('val is not numeric') | ||
| self._validate_number() | ||
| return self.is_not_equal_to(0) | ||
| def is_nan(self): | ||
| """Asserts that val is real number and NaN (not a number).""" | ||
| self._validate_number() | ||
| self._validate_real() | ||
| if not math.isnan(self.val): | ||
| self._err('Expected <%s> to be <NaN>, but was not.' % self.val) | ||
| return self | ||
| def is_not_nan(self): | ||
| """Asserts that val is real number and not NaN (not a number).""" | ||
| self._validate_number() | ||
| self._validate_real() | ||
| if math.isnan(self.val): | ||
| self._err('Expected not <NaN>, but was.') | ||
| return self | ||
| def is_inf(self): | ||
| """Asserts that val is real number and Inf (infinity).""" | ||
| self._validate_number() | ||
| self._validate_real() | ||
| if not math.isinf(self.val): | ||
| self._err('Expected <%s> to be <Inf>, but was not.' % self.val) | ||
| return self | ||
| def is_not_inf(self): | ||
| """Asserts that val is real number and not Inf (infinity).""" | ||
| self._validate_number() | ||
| self._validate_real() | ||
| if math.isinf(self.val): | ||
| self._err('Expected not <Inf>, but was.') | ||
| return self | ||
| def is_greater_than(self, other): | ||
@@ -521,17 +574,37 @@ """Asserts that val is numeric and is greater than other.""" | ||
| """Asserts that val is string and contains the given item or items.""" | ||
| if not isinstance(self.val, str_types): | ||
| raise TypeError('val is not a string') | ||
| if len(items) == 0: | ||
| raise ValueError('one or more args must be given') | ||
| elif len(items) == 1: | ||
| if not isinstance(items[0], str_types): | ||
| raise TypeError('given arg must be a string') | ||
| if items[0].lower() not in self.val.lower(): | ||
| self._err('Expected <%s> to case-insensitive contain item <%s>, but did not.' % (self.val, items[0])) | ||
| else: | ||
| if isinstance(self.val, str_types): | ||
| if len(items) == 1: | ||
| if not isinstance(items[0], str_types): | ||
| raise TypeError('given arg must be a string') | ||
| if items[0].lower() not in self.val.lower(): | ||
| self._err('Expected <%s> to case-insensitive contain item <%s>, but did not.' % (self.val, items[0])) | ||
| else: | ||
| missing = [] | ||
| for i in items: | ||
| if not isinstance(i, str_types): | ||
| raise TypeError('given args must all be strings') | ||
| if i.lower() not in self.val.lower(): | ||
| missing.append(i) | ||
| if missing: | ||
| self._err('Expected <%s> to case-insensitive contain items %s, but did not contain %s.' % (self.val, self._fmt_items(items), self._fmt_items(missing))) | ||
| elif isinstance(self.val, collections.Iterable): | ||
| missing = [] | ||
| for i in items: | ||
| if not isinstance(i, str_types): | ||
| raise TypeError('given args must all be strings') | ||
| if i.lower() not in self.val.lower(): | ||
| self._err('Expected <%s> to case-insensitive contain items %s, but did not contain <%s>.' % (self.val, items, i)) | ||
| found = False | ||
| for v in self.val: | ||
| if not isinstance(v, str_types): | ||
| raise TypeError('val items must all be strings') | ||
| if i.lower() == v.lower(): | ||
| found = True | ||
| break | ||
| if not found: | ||
| missing.append(i) | ||
| if missing: | ||
| self._err('Expected <%s> to case-insensitive contain items %s, but did not contain %s.' % (self.val, self._fmt_items(items), self._fmt_items(missing))) | ||
| else: | ||
| raise TypeError('val is not a string or iterable') | ||
| return self | ||
@@ -553,3 +626,3 @@ | ||
| raise ValueError('val must not be empty') | ||
| first = next(i for i in self.val) | ||
| first = next(iter(self.val)) | ||
| if first != prefix: | ||
@@ -674,2 +747,3 @@ self._err('Expected %s to start with <%s>, but did not.' % (self.val, prefix)) | ||
| missing = [] | ||
| if hasattr(self.val, 'keys') and callable(getattr(self.val, 'keys')) and hasattr(self.val, '__getitem__'): | ||
@@ -685,5 +759,7 @@ # flatten superset dicts | ||
| if i not in superdict: | ||
| self._err('Expected <%s> to be subset of %s, but key <%s> was missing.' % (self.val, superdict, i)) | ||
| if self.val[i] != superdict[i]: | ||
| self._err('Expected <%s> to be subset of %s, but key <%s> value <%s> was not equal to <%s>.' % (self.val, superdict, i, self.val[i], superdict[i])) | ||
| missing.append({i: self.val[i]}) # bad key | ||
| elif self.val[i] != superdict[i]: | ||
| missing.append({i: self.val[i]}) # bad val | ||
| if missing: | ||
| self._err('Expected <%s> to be subset of %s, but %s %s missing.' % (self.val, self._fmt_items(superdict), self._fmt_items(missing), 'was' if len(missing) == 1 else 'were')) | ||
| else: | ||
@@ -701,3 +777,5 @@ # flatten supersets | ||
| if i not in superset: | ||
| self._err('Expected <%s> to be subset of %s, but <%s> was missing.' % (self.val, superset, i)) | ||
| missing.append(i) | ||
| if missing: | ||
| self._err('Expected <%s> to be subset of %s, but %s %s missing.' % (self.val, self._fmt_items(superset), self._fmt_items(missing), 'was' if len(missing) == 1 else 'were')) | ||
@@ -722,5 +800,8 @@ return self | ||
| raise ValueError('one or more value args must be given') | ||
| missing = [] | ||
| for v in values: | ||
| if v not in self.val.values(): | ||
| self._err('Expected <%s> to contain value <%s>, but did not.' % (self.val, v)) | ||
| missing.append(v) | ||
| if missing: | ||
| self._err('Expected <%s> to contain values %s, but did not contain %s.' % (self.val, self._fmt_items(values), self._fmt_items(missing))) | ||
| return self | ||
@@ -733,9 +814,9 @@ | ||
| raise ValueError('one or more value args must be given') | ||
| elif len(values) == 1: | ||
| if values[0] in self.val.values(): | ||
| self._err('Expected <%s> to not contain value <%s>, but did.' % (self.val, values[0])) | ||
| else: | ||
| found = [] | ||
| for v in values: | ||
| if v in self.val.values(): | ||
| self._err('Expected <%s> to not contain values %s, but did contain <%s>.' % (self.val, values, v)) | ||
| found.append(v) | ||
| if found: | ||
| self._err('Expected <%s> to not contain values %s, but did contain %s.' % (self.val, self._fmt_items(values), self._fmt_items(found))) | ||
| return self | ||
@@ -748,2 +829,3 @@ | ||
| raise ValueError('one or more entry args must be given') | ||
| missing = [] | ||
| for e in entries: | ||
@@ -754,7 +836,9 @@ if type(e) is not dict: | ||
| raise ValueError('given entry args must contain exactly one key-value pair') | ||
| k = list(e.keys())[0] | ||
| k = next(iter(e)) | ||
| if k not in self.val: | ||
| self._err('Expected <%s> to contain entry %s, but did not contain key <%s>.' % (self.val, e, k)) | ||
| missing.append(e) # bad key | ||
| elif self.val[k] != e[k]: | ||
| self._err('Expected <%s> to contain entry %s, but key <%s> did not contain value <%s>.' % (self.val, e, k, e[k])) | ||
| missing.append(e) # bad val | ||
| if missing: | ||
| self._err('Expected <%s> to contain entries %s, but did not contain %s.' % (self.val, self._fmt_items(entries), self._fmt_items(missing))) | ||
| return self | ||
@@ -767,2 +851,3 @@ | ||
| raise ValueError('one or more entry args must be given') | ||
| found = [] | ||
| for e in entries: | ||
@@ -773,5 +858,7 @@ if type(e) is not dict: | ||
| raise ValueError('given entry args must contain exactly one key-value pair') | ||
| k = list(e.keys())[0] | ||
| k = next(iter(e)) | ||
| if k in self.val and e[k] == self.val[k]: | ||
| self._err('Expected <%s> to not contain entry %s, but did.' % (self.val, e)) | ||
| found.append(e) | ||
| if found: | ||
| self._err('Expected <%s> to not contain entries %s, but did contain %s.' % (self.val, self._fmt_items(entries), self._fmt_items(found))) | ||
| return self | ||
@@ -872,3 +959,3 @@ | ||
| ### collection of objects assertions ### | ||
| def extracting(self, *names): | ||
| def extracting(self, *names, **kwargs): | ||
| """Asserts that val is collection, then extracts the named properties or named zero-arg methods into a list (or list of tuples if multiple names are given).""" | ||
@@ -881,23 +968,55 @@ if not isinstance(self.val, collections.Iterable): | ||
| raise ValueError('one or more name args must be given') | ||
| def _extract(x, name): | ||
| if self._check_dict_like(x, check_values=False, return_as_bool=True): | ||
| if name in x: | ||
| return x[name] | ||
| else: | ||
| raise ValueError('item keys %s did not contain key <%s>' % (list(x.keys()), name)) | ||
| elif hasattr(x, name): | ||
| attr = getattr(x, name) | ||
| if callable(attr): | ||
| try: | ||
| return attr() | ||
| except TypeError: | ||
| raise ValueError('val method <%s()> exists, but is not zero-arg method' % name) | ||
| else: | ||
| return attr | ||
| else: | ||
| raise ValueError('val does not have property or zero-arg method <%s>' % name) | ||
| def _filter(x): | ||
| if 'filter' in kwargs: | ||
| if isinstance(kwargs['filter'], str_types): | ||
| return bool(_extract(x, kwargs['filter'])) | ||
| elif self._check_dict_like(kwargs['filter'], check_values=False, return_as_bool=True): | ||
| for k in kwargs['filter']: | ||
| if isinstance(k, str_types): | ||
| if _extract(x, k) != kwargs['filter'][k]: | ||
| return False | ||
| return True | ||
| elif callable(kwargs['filter']): | ||
| return kwargs['filter'](x) | ||
| return False | ||
| return True | ||
| def _sort(x): | ||
| if 'sort' in kwargs: | ||
| if isinstance(kwargs['sort'], str_types): | ||
| return _extract(x, kwargs['sort']) | ||
| elif isinstance(kwargs['sort'], collections.Iterable): | ||
| items = [] | ||
| for k in kwargs['sort']: | ||
| if isinstance(k, str_types): | ||
| items.append(_extract(x, k)) | ||
| return tuple(items) | ||
| elif callable(kwargs['sort']): | ||
| return kwargs['sort'](x) | ||
| return 0 | ||
| extracted = [] | ||
| for i in self.val: | ||
| items = [] | ||
| for name in names: | ||
| if type(i) is dict: | ||
| if name in i: | ||
| items.append(i[name]) | ||
| else: | ||
| raise ValueError('item keys %s did not contain key <%s>' % (list(i.keys()), name)) | ||
| elif hasattr(i, name): | ||
| attr = getattr(i, name) | ||
| if callable(attr): | ||
| try: | ||
| items.append(attr()) | ||
| except TypeError: | ||
| raise ValueError('val method <%s()> exists, but is not zero-arg method' % name) | ||
| else: | ||
| items.append(attr) | ||
| else: | ||
| raise ValueError('val does not have property or zero-arg method <%s>' % name) | ||
| extracted.append(tuple(items) if len(items) > 1 else items[0]) | ||
| for i in sorted(self.val, key=lambda x: _sort(x)): | ||
| if _filter(i): | ||
| items = [_extract(i, name) for name in names] | ||
| extracted.append(tuple(items) if len(items) > 1 else items[0]) | ||
| return AssertionBuilder(extracted, self.description, self.kind) | ||
@@ -951,5 +1070,5 @@ | ||
| def raises(self, ex): | ||
| """Asserts that val is a function that when invoked raises the given error.""" | ||
| if not inspect.isfunction(self.val): | ||
| raise TypeError('val must be function') | ||
| """Asserts that val is callable and that when called raises the given error.""" | ||
| if not callable(self.val): | ||
| raise TypeError('val must be callable') | ||
| if not issubclass(ex, BaseException): | ||
@@ -960,3 +1079,3 @@ raise TypeError('given arg must be exception') | ||
| def when_called_with(self, *some_args, **some_kwargs): | ||
| """Asserts the val function when invoked with the given args and kwargs raises the expected exception.""" | ||
| """Asserts the val callable when invoked with the given args and kwargs raises the expected exception.""" | ||
| if not self.expected: | ||
@@ -998,2 +1117,10 @@ raise TypeError('expected exception not set, raises() must be called first') | ||
| def _fmt_items(self, i): | ||
| if len(i) == 0: | ||
| return '<>' | ||
| elif len(i) == 1: | ||
| return '<%s>' % i[0] | ||
| else: | ||
| return '<%s>' % str(i).lstrip('([').rstrip(',])') | ||
| def _fmt_args_kwargs(self, *some_args, **some_kwargs): | ||
@@ -1016,13 +1143,27 @@ """Helper to convert the given args and kwargs into a string.""" | ||
| def _check_dict_like(self, d, check_keys=True, check_values=True, check_getitem=True, name='val'): | ||
| def _check_dict_like(self, d, check_keys=True, check_values=True, check_getitem=True, name='val', return_as_bool=False): | ||
| if not isinstance(d, collections.Iterable): | ||
| raise TypeError('%s <%s> is not dict-like: not iterable' % (name, type(d).__name__)) | ||
| if return_as_bool: | ||
| return False | ||
| else: | ||
| raise TypeError('%s <%s> is not dict-like: not iterable' % (name, type(d).__name__)) | ||
| if check_keys: | ||
| if not hasattr(d, 'keys') or not callable(getattr(d, 'keys')): | ||
| raise TypeError('%s <%s> is not dict-like: missing keys()' % (name, type(d).__name__)) | ||
| if return_as_bool: | ||
| return False | ||
| else: | ||
| raise TypeError('%s <%s> is not dict-like: missing keys()' % (name, type(d).__name__)) | ||
| if check_values: | ||
| if not hasattr(d, 'values') or not callable(getattr(d, 'values')): | ||
| raise TypeError('%s <%s> is not dict-like: missing values()' % (name, type(d).__name__)) | ||
| if return_as_bool: | ||
| return False | ||
| else: | ||
| raise TypeError('%s <%s> is not dict-like: missing values()' % (name, type(d).__name__)) | ||
| if check_getitem: | ||
| if not hasattr(d, '__getitem__'): | ||
| raise TypeError('%s <%s> is not dict-like: missing [] accessor' % (name, type(d).__name__)) | ||
| if return_as_bool: | ||
| return False | ||
| else: | ||
| raise TypeError('%s <%s> is not dict-like: missing [] accessor' % (name, type(d).__name__)) | ||
| if return_as_bool: | ||
| return True |
+2
-2
| Metadata-Version: 1.1 | ||
| Name: assertpy | ||
| Version: 0.11 | ||
| Version: 0.12 | ||
| Summary: Assertion library for python unit testing with a fluent API | ||
@@ -9,3 +9,3 @@ Home-page: https://github.com/ActivisionGameScience/assertpy | ||
| License: BSD | ||
| Download-URL: https://codeload.github.com/ActivisionGameScience/assertpy/tar.gz/0.11 | ||
| Download-URL: https://codeload.github.com/ActivisionGameScience/assertpy/tar.gz/0.12 | ||
| Description: assertpy | ||
@@ -12,0 +12,0 @@ ======== |
Alert delta unavailable
Currently unable to show alert delta for PyPI packages.
55058
10.93%1067
13.75%