paul@0 | 1 | #!/usr/bin/env python |
paul@0 | 2 | |
paul@0 | 3 | """ |
paul@0 | 4 | Reference abstractions. |
paul@0 | 5 | |
paul@0 | 6 | Copyright (C) 2016 Paul Boddie <paul@boddie.org.uk> |
paul@0 | 7 | |
paul@0 | 8 | This program is free software; you can redistribute it and/or modify it under |
paul@0 | 9 | the terms of the GNU General Public License as published by the Free Software |
paul@0 | 10 | Foundation; either version 3 of the License, or (at your option) any later |
paul@0 | 11 | version. |
paul@0 | 12 | |
paul@0 | 13 | This program is distributed in the hope that it will be useful, but WITHOUT |
paul@0 | 14 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
paul@0 | 15 | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more |
paul@0 | 16 | details. |
paul@0 | 17 | |
paul@0 | 18 | You should have received a copy of the GNU General Public License along with |
paul@0 | 19 | this program. If not, see <http://www.gnu.org/licenses/>. |
paul@0 | 20 | """ |
paul@0 | 21 | |
paul@0 | 22 | class Reference: |
paul@0 | 23 | |
paul@0 | 24 | "A reference abstraction." |
paul@0 | 25 | |
paul@0 | 26 | def __init__(self, kind, origin=None, name=None): |
paul@0 | 27 | |
paul@0 | 28 | """ |
paul@0 | 29 | Initialise a reference using 'kind' to indicate the kind of object, |
paul@0 | 30 | 'origin' to indicate the actual origin of a referenced object, and a |
paul@0 | 31 | 'name' indicating an alias for the object in the program structure. |
paul@0 | 32 | """ |
paul@0 | 33 | |
paul@0 | 34 | if isinstance(kind, Reference): |
paul@0 | 35 | raise ValueError, (kind, origin) |
paul@0 | 36 | self.kind = kind |
paul@0 | 37 | self.origin = origin |
paul@0 | 38 | self.name = name |
paul@0 | 39 | |
paul@0 | 40 | def __repr__(self): |
paul@0 | 41 | return "Reference(%r, %r, %r)" % (self.kind, self.origin, self.name) |
paul@0 | 42 | |
paul@0 | 43 | def __str__(self): |
paul@0 | 44 | |
paul@0 | 45 | """ |
paul@0 | 46 | Serialise the reference as '<var>' or a description incorporating the |
paul@0 | 47 | kind and origin. |
paul@0 | 48 | """ |
paul@0 | 49 | |
paul@0 | 50 | if self.kind == "<var>": |
paul@0 | 51 | return self.kind |
paul@0 | 52 | else: |
paul@0 | 53 | return "%s:%s" % (self.kind, self.origin) |
paul@0 | 54 | |
paul@0 | 55 | def __hash__(self): |
paul@0 | 56 | |
paul@0 | 57 | "Hash instances using the kind and origin only." |
paul@0 | 58 | |
paul@0 | 59 | return hash((self.kind, self.get_origin())) |
paul@0 | 60 | |
paul@0 | 61 | def __cmp__(self, other): |
paul@0 | 62 | |
paul@0 | 63 | "Compare with 'other' using the kind and origin only." |
paul@0 | 64 | |
paul@0 | 65 | if isinstance(other, Reference): |
paul@0 | 66 | return cmp((self.kind, self.get_origin()), (other.kind, other.get_origin())) |
paul@0 | 67 | else: |
paul@0 | 68 | return cmp(str(self), other) |
paul@0 | 69 | |
paul@0 | 70 | def get_name(self): |
paul@0 | 71 | |
paul@0 | 72 | "Return the name used for this reference." |
paul@0 | 73 | |
paul@0 | 74 | return self.name |
paul@0 | 75 | |
paul@0 | 76 | def get_origin(self): |
paul@0 | 77 | |
paul@0 | 78 | "Return the origin of the reference." |
paul@0 | 79 | |
paul@0 | 80 | return self.kind != "<var>" and self.origin or None |
paul@0 | 81 | |
paul@0 | 82 | def get_kind(self): |
paul@0 | 83 | |
paul@0 | 84 | "Return the kind of object referenced." |
paul@0 | 85 | |
paul@0 | 86 | return self.kind |
paul@0 | 87 | |
paul@0 | 88 | def has_kind(self, kinds): |
paul@0 | 89 | |
paul@0 | 90 | """ |
paul@0 | 91 | Return whether the reference describes an object from the given 'kinds', |
paul@0 | 92 | where such kinds may be "<class>", "<function>", "<instance>", |
paul@0 | 93 | "<module>" or "<var>". |
paul@0 | 94 | """ |
paul@0 | 95 | |
paul@0 | 96 | if not isinstance(kinds, (list, tuple)): |
paul@0 | 97 | kinds = [kinds] |
paul@0 | 98 | return self.get_kind() in kinds |
paul@0 | 99 | |
paul@0 | 100 | def get_path(self): |
paul@0 | 101 | |
paul@0 | 102 | "Return the attribute names comprising the path to the origin." |
paul@0 | 103 | |
paul@0 | 104 | return self.get_origin().split(".") |
paul@0 | 105 | |
paul@0 | 106 | def static(self): |
paul@0 | 107 | |
paul@0 | 108 | "Return this reference if it refers to a static object, None otherwise." |
paul@0 | 109 | |
paul@0 | 110 | return not self.has_kind(["<var>", "<instance>"]) and self or None |
paul@0 | 111 | |
paul@0 | 112 | def final(self): |
paul@0 | 113 | |
paul@0 | 114 | "Return a reference to either a static object or None." |
paul@0 | 115 | |
paul@0 | 116 | static = self.static() |
paul@0 | 117 | return static and static.origin or None |
paul@0 | 118 | |
paul@0 | 119 | def instance_of(self): |
paul@0 | 120 | |
paul@0 | 121 | "Return a reference to an instance of the referenced class." |
paul@0 | 122 | |
paul@0 | 123 | return self.has_kind("<class>") and Reference("<instance>", self.origin) or None |
paul@0 | 124 | |
paul@0 | 125 | def as_var(self): |
paul@0 | 126 | |
paul@0 | 127 | """ |
paul@0 | 128 | Return a variable version of this reference. Any origin information is |
paul@0 | 129 | discarded since variable references are deliberately ambiguous. |
paul@0 | 130 | """ |
paul@0 | 131 | |
paul@0 | 132 | return Reference("<var>", None, self.name) |
paul@0 | 133 | |
paul@0 | 134 | def provided_by_module(self, module_name): |
paul@0 | 135 | |
paul@0 | 136 | "Return whether the reference is provided by 'module_name'." |
paul@0 | 137 | |
paul@0 | 138 | path = self.origin |
paul@0 | 139 | return not path or path.rsplit(".", 1)[0] == module_name |
paul@0 | 140 | |
paul@0 | 141 | def alias(self, name): |
paul@0 | 142 | |
paul@0 | 143 | "Alias this reference employing 'name'." |
paul@0 | 144 | |
paul@0 | 145 | return Reference(self.get_kind(), self.get_origin(), name) |
paul@0 | 146 | |
paul@0 | 147 | def decode_reference(s, name=None): |
paul@0 | 148 | |
paul@0 | 149 | "Decode 's', making a reference." |
paul@0 | 150 | |
paul@0 | 151 | if isinstance(s, Reference): |
paul@0 | 152 | return s.alias(name) |
paul@0 | 153 | |
paul@0 | 154 | # Null value. |
paul@0 | 155 | |
paul@0 | 156 | elif not s: |
paul@0 | 157 | return Reference("<var>", None, name) |
paul@0 | 158 | |
paul@0 | 159 | # Kind and origin. |
paul@0 | 160 | |
paul@0 | 161 | elif ":" in s: |
paul@0 | 162 | kind, origin = s.split(":") |
paul@0 | 163 | return Reference(kind, origin, name) |
paul@0 | 164 | |
paul@0 | 165 | # Kind-only, origin is indicated name. |
paul@0 | 166 | |
paul@0 | 167 | elif s[0] == "<": |
paul@0 | 168 | return Reference(s, name, name) |
paul@0 | 169 | |
paul@0 | 170 | # Module-only. |
paul@0 | 171 | |
paul@0 | 172 | else: |
paul@0 | 173 | return Reference("<module>", s, name) |
paul@0 | 174 | |
paul@0 | 175 | # vim: tabstop=4 expandtab shiftwidth=4 |