|  | 
| 1 | 1 | from math import copysign, isnan | 
| 2 | 2 | 
 | 
| 3 | 3 | 
 | 
|  | 4 | +class ExtraAssertions: | 
|  | 5 | + | 
|  | 6 | +    def assertIsSubclass(self, cls, superclass, msg=None): | 
|  | 7 | +        if issubclass(cls, superclass): | 
|  | 8 | +            return | 
|  | 9 | +        standardMsg = f'{cls!r} is not a subclass of {superclass!r}' | 
|  | 10 | +        self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 11 | + | 
|  | 12 | +    def assertNotIsSubclass(self, cls, superclass, msg=None): | 
|  | 13 | +        if not issubclass(cls, superclass): | 
|  | 14 | +            return | 
|  | 15 | +        standardMsg = f'{cls!r} is a subclass of {superclass!r}' | 
|  | 16 | +        self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 17 | + | 
|  | 18 | +    def assertHasAttr(self, obj, name, msg=None): | 
|  | 19 | +        if not hasattr(obj, name): | 
|  | 20 | +            if isinstance(obj, types.ModuleType): | 
|  | 21 | +                standardMsg = f'module {obj.__name__!r} has no attribute {name!r}' | 
|  | 22 | +            elif isinstance(obj, type): | 
|  | 23 | +                standardMsg = f'type object {obj.__name__!r} has no attribute {name!r}' | 
|  | 24 | +            else: | 
|  | 25 | +                standardMsg = f'{type(obj).__name__!r} object has no attribute {name!r}' | 
|  | 26 | +            self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 27 | + | 
|  | 28 | +    def assertNotHasAttr(self, obj, name, msg=None): | 
|  | 29 | +        if hasattr(obj, name): | 
|  | 30 | +            if isinstance(obj, types.ModuleType): | 
|  | 31 | +                standardMsg = f'module {obj.__name__!r} has unexpected attribute {name!r}' | 
|  | 32 | +            elif isinstance(obj, type): | 
|  | 33 | +                standardMsg = f'type object {obj.__name__!r} has unexpected attribute {name!r}' | 
|  | 34 | +            else: | 
|  | 35 | +                standardMsg = f'{type(obj).__name__!r} object has unexpected attribute {name!r}' | 
|  | 36 | +            self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 37 | + | 
|  | 38 | +    def assertStartsWith(self, s, prefix, msg=None): | 
|  | 39 | +        if s.startswith(prefix): | 
|  | 40 | +            return | 
|  | 41 | +        standardMsg = f"{s!r} doesn't start with {prefix!r}" | 
|  | 42 | +        self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 43 | + | 
|  | 44 | +    def assertNotStartsWith(self, s, prefix, msg=None): | 
|  | 45 | +        if not s.startswith(prefix): | 
|  | 46 | +            return | 
|  | 47 | +        self.fail(self._formatMessage(msg, f"{s!r} starts with {prefix!r}")) | 
|  | 48 | + | 
|  | 49 | +    def assertEndsWith(self, s, suffix, msg=None): | 
|  | 50 | +        if s.endswith(suffix): | 
|  | 51 | +            return | 
|  | 52 | +        standardMsg = f"{s!r} doesn't end with {suffix!r}" | 
|  | 53 | +        self.fail(self._formatMessage(msg, standardMsg)) | 
|  | 54 | + | 
|  | 55 | +    def assertNotEndsWith(self, s, suffix, msg=None): | 
|  | 56 | +        if not s.endswith(suffix): | 
|  | 57 | +            return | 
|  | 58 | +        self.fail(self._formatMessage(msg, f"{s!r} ends with {suffix!r}")) | 
|  | 59 | + | 
|  | 60 | + | 
| 4 | 61 | class ExceptionIsLikeMixin: | 
| 5 | 62 |     def assertExceptionIsLike(self, exc, template): | 
| 6 | 63 |         """ | 
|  | 
0 commit comments