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