|  | #!/usr/bin/env python3 | 
|  | def __safe_call(f, a): | 
|  | """Call a function and capture all exceptions.""" | 
|  | try: | 
|  | return f(a) | 
|  | except Exception as e: | 
|  | return "{}@{}({})".format(a.__class__.__name__, id(a), e) | 
|  |  | 
|  |  | 
|  | def __safe_error(msg, a, b): | 
|  | """Generate the error message for assert_XX without causing an error.""" | 
|  | return "{} ({}) {} {} ({})".format( | 
|  | __safe_call(str, a), | 
|  | __safe_call(repr, a), | 
|  | msg, | 
|  | __safe_call(str, b), | 
|  | __safe_call(repr, b), | 
|  | ) | 
|  |  | 
|  |  | 
|  | def assert_eq(a, b, msg=None): | 
|  | """Assert equal with better error message.""" | 
|  | assert a == b, msg or __safe_error("!=", a, b) | 
|  |  | 
|  |  | 
|  | def assert_not_in(needle, haystack, msg=None): | 
|  | """Assert equal with better error message.""" | 
|  | assert needle not in haystack, msg or __safe_error( | 
|  | "already in", needle, haystack | 
|  | ) | 
|  |  | 
|  |  | 
|  | def assert_is(a, b): | 
|  | """Assert is with better error message.""" | 
|  | assert a is b, __safe_error("is not", a, b) | 
|  |  | 
|  |  | 
|  | def assert_type( | 
|  | obj, cls, msg="{obj} ({obj!r}) should be a {cls}, not {objcls}" | 
|  | ): | 
|  | """Raise a type error if obj is not an instance of cls.""" | 
|  | if not isinstance(obj, cls): | 
|  | raise TypeError(msg.format(obj=obj, objcls=type(obj), cls=cls)) | 
|  |  | 
|  |  | 
|  | def assert_type_or_none(obj, classes): | 
|  | """Raise a type error if obj is not an instance of cls or None.""" | 
|  | if obj is not None: | 
|  | assert_type(obj, classes) | 
|  |  | 
|  |  | 
|  | def assert_len_eq(lists): | 
|  | """Check all lists in a list are equal length""" | 
|  | # Sanity check | 
|  | max_len = max(len(p) for p in lists) | 
|  | for i, p in enumerate(lists): | 
|  | assert len( | 
|  | p | 
|  | ) == max_len, "Length check failed!\nl[{}] has {} elements != {} ({!r})\n{!r}".format( | 
|  | i, len(p), max_len, p, lists | 
|  | ) |