Alex Light | eb7c144 | 2015-08-31 13:17:42 -0700 | [diff] [blame] | 1 | #!/usr/bin/python3 |
| 2 | # |
| 3 | # Copyright (C) 2015 The Android Open Source Project |
| 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
| 17 | """ |
| 18 | Common mixins and abstract base classes (ABCs) useful for writing test generators in python |
| 19 | """ |
| 20 | |
| 21 | import abc |
| 22 | import collections.abc |
| 23 | import functools |
| 24 | |
| 25 | class Named(metaclass=abc.ABCMeta): |
| 26 | """ |
| 27 | An abc that defines a get_name method. |
| 28 | """ |
| 29 | |
| 30 | @abc.abstractmethod |
| 31 | def get_name(self): |
| 32 | """ |
| 33 | Returns a unique name to use as the identity for implementing comparisons. |
| 34 | """ |
| 35 | pass |
| 36 | |
| 37 | class FileLike(metaclass=abc.ABCMeta): |
| 38 | """ |
| 39 | An abc that defines get_file_name and get_file_extension methods. |
| 40 | """ |
| 41 | |
| 42 | @abc.abstractmethod |
| 43 | def get_file_name(self): |
| 44 | """Returns the filename this object represents""" |
| 45 | pass |
| 46 | |
| 47 | @abc.abstractmethod |
| 48 | def get_file_extension(self): |
| 49 | """Returns the file extension of the file this object represents""" |
| 50 | pass |
| 51 | |
| 52 | @functools.lru_cache(maxsize=None) |
| 53 | def get_file_extension_mixin(ext): |
| 54 | """ |
| 55 | Gets a mixin that defines get_file_name(self) in terms of get_name(self) with the |
| 56 | given file extension. |
| 57 | """ |
| 58 | |
| 59 | class FExt(object): |
| 60 | """ |
| 61 | A mixin defining get_file_name(self) in terms of get_name(self) |
| 62 | """ |
| 63 | |
| 64 | def get_file_name(self): |
| 65 | return self.get_name() + ext |
| 66 | |
| 67 | def get_file_extension(self): |
| 68 | return ext |
| 69 | |
| 70 | # Register the ABCs |
| 71 | Named.register(FExt) |
| 72 | FileLike.register(FExt) |
| 73 | |
| 74 | return FExt |
| 75 | |
| 76 | class SmaliFileMixin(get_file_extension_mixin(".smali")): |
| 77 | """ |
| 78 | A mixin that defines that the file this class belongs to is get_name() + ".smali". |
| 79 | """ |
| 80 | pass |
| 81 | |
| 82 | class NameComparableMixin(object): |
| 83 | """ |
| 84 | A mixin that defines the object comparison and related functionality in terms |
| 85 | of a get_name(self) function. |
| 86 | """ |
| 87 | |
| 88 | def __lt__(self, other): |
| 89 | return self.get_name() < other.get_name() |
| 90 | |
| 91 | def __gt__(self, other): |
| 92 | return self.get_name() > other.get_name() |
| 93 | |
| 94 | def __eq__(self, other): |
| 95 | return self.get_name() == other.get_name() |
| 96 | |
| 97 | def __le__(self, other): |
| 98 | return self.get_name() <= other.get_name() |
| 99 | |
| 100 | def __ge__(self, other): |
| 101 | return self.get_name() >= other.get_name() |
| 102 | |
| 103 | def __ne__(self, other): |
| 104 | return self.get_name() != other.get_name() |
| 105 | |
| 106 | def __hash__(self): |
| 107 | return hash(self.get_name()) |
| 108 | |
| 109 | Named.register(NameComparableMixin) |
| 110 | collections.abc.Hashable.register(NameComparableMixin) |
| 111 | |
| 112 | class DumpMixin(metaclass=abc.ABCMeta): |
| 113 | """ |
| 114 | A mixin to add support for dumping the string representation of an object to a |
| 115 | file. Requires the get_file_name(self) method be defined. |
| 116 | """ |
| 117 | |
| 118 | @abc.abstractmethod |
| 119 | def __str__(self): |
| 120 | """ |
| 121 | Returns the data to be printed to a file by dump. |
| 122 | """ |
| 123 | pass |
| 124 | |
| 125 | def dump(self, directory): |
| 126 | """ |
| 127 | Dump this object to a file in the given directory |
| 128 | """ |
| 129 | out_file = directory / self.get_file_name() |
| 130 | if out_file.exists(): |
| 131 | out_file.unlink() |
| 132 | with out_file.open('w') as out: |
| 133 | print(str(self), file=out) |
| 134 | |
| 135 | FileLike.register(DumpMixin) |