Package conduit :: Module TypeConverter
[hide private]

Source Code for Module conduit.TypeConverter

  1  """ 
  2  Holds the TypeConverter class 
  3   
  4  Copyright: John Stowers, 2006 
  5  License: GPLv2 
  6  """ 
  7  import traceback 
  8  import logging 
  9  log = logging.getLogger("TypeConverter") 
 10   
 11  import conduit.Exceptions as Exceptions 
 12  import conduit.utils as Utils 
 13   
14 -class Converter:
15 _module_type_ = "converter"
16
17 -class TypeConverter:
18 """ 19 Maintains a dictionary of dictionaries, indexed by the type converted FROM which 20 maps to a list of types that can be converted TO 21 22 An example statically constructed conversion dictionary is:: 23 24 self.convertables = { 25 "from1" : 26 { 27 "to1":from1_to_to1_converter, 28 "to2":from1_to_to2_converter 29 }, 30 "from2" : 31 { 32 "to3":from2_to_to3_converter, 33 "to1":from2_to_to1_converter 34 }, 35 "from3" : 36 { 37 "to5":from3_to_to5_converter 38 } 39 } 40 41 42 @ivar convertables: The name of the contained module 43 @type convertables: C{dict of dicts}, see description 44 in L{conduit.TypeConverter.TypeConverter} 45 """ 46
47 - def __init__ (self, moduleManager):
48 """ 49 Builds the conversion dictionary 50 51 @param dynamic_modules: The dynamically loaded converters 52 """ 53 #dict of dict of conversion functions 54 self.convertables = {} 55 56 moduleManager.make_modules_callable("converter") 57 dynamic_modules = moduleManager.get_modules_by_type("converter") 58 for d in dynamic_modules: 59 self._add_converter(d)
60
61 - def _add_converter(self, converterWrapper):
62 conv = getattr(converterWrapper.module,"conversions", {}) 63 for c in conv: 64 try: 65 #Conversions are described as fromtype,totype 66 fromtype = c.split(',')[0] 67 totype = c.split(',')[1] 68 #if the from source doesnt yet exist add an inner dict 69 #containing the FROM type and conversion function 70 if not self.convertables.has_key(fromtype): 71 self.convertables[fromtype] = {totype:conv[c]} 72 #Otherwise we already have an inner dict so can go 73 #ahead and insert a new TO type and conversion function 74 else: 75 self.convertables[fromtype][totype] = conv[c] 76 except IndexError: 77 log.warn("Conversion dict (%s) wrong format. Should be fromtype,totype" % c) 78 except KeyError, err: 79 log.warn("Could not add conversion function from %s to %s\n%s" % (fromtype,totype,err)) 80 except Exception: 81 log.warn("BAD PROGRAMMER")
82
83 - def _retain_info_in_conversion(self, fromdata, todata):
84 """ 85 Retains the original datatype properties through a type conversion. 86 Properties retained include; 87 - gnome-open'able URI 88 - modification time 89 - original UID 90 Call this function from a typeconverter 91 """ 92 if todata == None: 93 log.warn("Conversion from %s returned None" % (fromdata)) 94 else: 95 todata.set_mtime(fromdata.get_mtime()) 96 todata.set_open_URI(fromdata.get_open_URI()) 97 todata.set_UID(fromdata.get_UID()) 98 return todata
99
100 - def _get_conversions(self, from_type, to_type):
101 """ 102 Returns the conversions required fromtype -> totype. Considers if fromtype and/or 103 totype are super/subclasses of each other. The args string is always taken from the 104 destination, i.e. the totype. 105 106 Does not check if the conversion actually exists. 107 108 @returns: list of (fromtype, totype, args) tuples 109 """ 110 conversions = [] 111 args = {} 112 fromType = from_type 113 toType = to_type 114 115 #remove the args string of present at source 116 try: 117 fromType = from_type.split("?")[0] 118 except ValueError: pass 119 120 #args string is only considered for the destination 121 try: 122 toType,argString = to_type.split("?") 123 args = Utils.decode_conversion_args(argString) 124 except ValueError: pass 125 126 if fromType != toType: 127 #check first for and explicit conversion 128 if self._conversion_exists(fromType, toType): 129 conversions.append( (fromType, toType, args) ) 130 else: 131 froms = fromType.split("/") 132 tos = toType.split("/") 133 if froms[0] == tos[0]: 134 #same base type, so only convert parent -> child e.g. 135 #file/audio -> file = file -> file 136 #file -> file/audio = file -> file/audio 137 conversions.append( (froms[0],"/".join(tos),args) ) 138 else: 139 #different base type, e.g. 140 #foo/bar -> bar/baz 141 if len(tos) > 1: 142 #Two conversions are needed, a main type, and a subtype 143 #conversion. remains is any necessary subtype conversion 144 conversions.append( (froms[0], tos[0], {}) ) 145 conversions.append( (tos[0],"/".join(tos),args) ) 146 else: 147 #Just a main type conversion is needed (remember the args) 148 conversions.append( (froms[0], tos[0], args) ) 149 else: 150 conversions.append( (fromType, toType, args) ) 151 152 return conversions
153
154 - def _conversion_exists(self, from_type, to_type):
155 if self.convertables.has_key(from_type): 156 if self.convertables[from_type].has_key(to_type): 157 return True 158 return False
159
160 - def _convert(self, conversions, data):
161 if len(conversions) > 0: 162 from_type, to_type, args = conversions[0] 163 message = "Converting" 164 if from_type == to_type: 165 message = "Transcoding" 166 #No conversion needed, or module does not supply transcode. 167 if args == {} or not self._conversion_exists(from_type, to_type): 168 #recurse 169 log.debug("Skipping %s -> %s" % (from_type, to_type)) 170 return self._convert(conversions[1:],data) 171 172 log.debug("%s %s -> %s (args: %s)" % (message, from_type, to_type, args)) 173 try: 174 #recurse 175 return self._convert( 176 conversions[1:], 177 self.convertables[from_type][to_type](data, **args) 178 ) 179 except Exception: 180 log.debug(traceback.format_exc()) 181 raise Exceptions.ConversionError(from_type, to_type) 182 else: 183 return data
184
185 - def conversion_exists(self, from_type, to_type):
186 """ 187 Checks if all conversion(s) exists to convert from from_type 188 into to_type 189 """ 190 for f,t,a in self._get_conversions(from_type, to_type): 191 if f != t and not self._conversion_exists(f,t): 192 log.debug("Conversions %s -> %s doesnt exist" % (f,t)) 193 return False 194 return True
195
196 - def convert(self, from_type, to_type, data):
197 """ 198 Converts a L{conduit.DataType.DataType} (or derived) of type 199 from_type into to_type and returns that newly converted type. If no 200 conversion is needed, the original data is returned. 201 202 @param from_type: The name of the type converted from 203 @type from_type: C{string} 204 @param to_type: The name of the type to convert to 205 @type to_type: C{string} 206 @param data: The DataType to convert 207 @type data: L{conduit.DataType.DataType} 208 """ 209 conversions = self._get_conversions(from_type, to_type) 210 log.debug("Convert %s -> %s using %s" % (from_type, to_type, conversions)) 211 212 if self.conversion_exists(from_type, to_type): 213 #recursively perform the needed conversions 214 newdata = self._convert(conversions, data) 215 else: 216 raise Exceptions.ConversionDoesntExistError(from_type, to_type) 217 218 return self._retain_info_in_conversion(fromdata=data, todata=newdata)
219
220 - def get_convertables_list(self):
221 """ 222 Returns a list of 2-tuples specifying conversions (from->to) 223 """ 224 l = [] 225 for froms in self.convertables: 226 for tos in self.convertables[froms]: 227 l.append( (froms, tos) ) 228 return l
229