Package python-module-logilab-mtconverter-0 :: Package 8 :: Package 4 :: Module engine
[frames] | no frames]

Source Code for Module python-module-logilab-mtconverter-0.8.4.engine

  1  # copyright 2006-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. 
  2  # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr 
  3  # 
  4  # This file is part of logilab-mtconverter. 
  5  # 
  6  # logilab-mtconverter is free software: you can redistribute it and/or modify it 
  7  # under the terms of the GNU Lesser General Public License as published by the 
  8  # Free Software Foundation, either version 2.1 of the License, or (at your 
  9  # option) any later version. 
 10  # 
 11  # logilab-mtconverter is distributed in the hope that it will be useful, but 
 12  # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 13  # FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License 
 14  # for more details. 
 15  # 
 16  # You should have received a copy of the GNU Lesser General Public License along 
 17  # with logilab-mtconverter. If not, see <http://www.gnu.org/licenses/>. 
 18  """the transformation engine""" 
 19   
 20  from logilab.mtconverter import TransformError 
 21  from logilab.mtconverter.transform import TransformsChain 
 22   
 23   
24 -def split_mimetype(mimetype):
25 try: 26 main, sub = mimetype.split('/') 27 except ValueError: 28 raise TransformError('bad mime type %s' % mimetype) 29 if not (main and sub): 30 raise TransformError('bad mime type %s' % mimetype) 31 return main, sub
32
33 -class TransformEngine(object):
34 """mimetype oriented conversions engine""" 35
36 - def __init__(self):
37 self._mtmap = {} 38 self._mtmainmap = {} 39 self.transforms = {}
40
41 - def add_transform(self, transform):
42 """register a new transform""" 43 self._map_transform(transform)
44
45 - def remove_transform(self, name, *inputs):
46 """ unregister a transform 47 name is the name of a registered transform 48 """ 49 self._unmap_transform(self.transforms[name], *inputs)
50
51 - def has_input(self, mimetype):
52 """return True if the engine has a transformation taking the given 53 mimetype as input 54 """ 55 if mimetype in self._mtmap: 56 return True 57 if split_mimetype(mimetype)[0] in self._mtmainmap: 58 return True 59 return False
60
61 - def convert(self, trdata, targetmimetype):
62 """convert the given data structure into the given mime type 63 64 :param trdata: `TransformData` 65 :rtype: `TransformData` 66 """ 67 trdata.check_encoding() 68 # get a path to output mime type 69 # 70 # even if target mime type is the same as input mime type, try 71 # to find a path in case an identity transform is available 72 path = self.find_path(trdata.mimetype, targetmimetype) 73 if not path: 74 if trdata.mimetype == targetmimetype: 75 return trdata 76 raise TransformError('no transformation path from %s to %s' 77 % (trdata.mimetype, targetmimetype)) 78 if len(path) > 1: 79 transform = TransformsChain('aname', path) 80 else: 81 transform = path[0] 82 return transform.convert(trdata)
83
84 - def _map_transform(self, transform):
85 """map transform to internal structures""" 86 if not (transform.inputs and transform.output): 87 raise TransformError('transform is missing input or output') 88 if split_mimetype(transform.output)[1] == '*': 89 raise TransformError('bad output mime type, wildcard only allowed in inputs') 90 if transform.name in self.transforms: 91 raise TransformError('a transform named %s already exists' % transform.name) 92 for mt in transform.inputs: 93 main, sub = split_mimetype(mt) 94 if sub == '*': 95 inmap = self._mtmainmap.setdefault(main, {}) 96 else: 97 inmap = self._mtmap.setdefault(mt, {}) 98 try: 99 inmap[transform.output].append(transform) 100 except KeyError: 101 inmap[transform.output] = [transform] 102 self.transforms[transform.name] = transform
103
104 - def _unmap_transform(self, transform, *inputs):
105 """unmap transform from internal structures""" 106 if not inputs: 107 inputs = transform.inputs 108 for mt in inputs: 109 main, sub = split_mimetype(mt) 110 if sub == '*': 111 inmap = self._mtmainmap[main] 112 else: 113 inmap = self._mtmap[mt] 114 inmap[transform.output].remove(transform) 115 del self.transforms[transform.name]
116
117 - def find_path(self, orig, target, required_transforms=()):
118 """return the shortest path for transformation from orig mimetype to 119 target mimetype 120 """ 121 # naive algorithm : 122 # find all possible paths with required transforms 123 # take the shortest 124 # 125 # it should be enough since we should not have so much possible paths 126 # and I wouldn't like to get a 1000 transformations path 127 shortest, winner = 100, None 128 for path in self._get_paths(orig, target, required_transforms): 129 if len(path) < shortest: 130 winner = path 131 shortest = len(path) 132 return winner
133
134 - def _get_paths(self, orig, target, requirements, path=None, result=None):
135 """return a all path for transformation from orig mimetype to 136 target mimetype 137 """ 138 if path is None: 139 result = [] 140 path = [] 141 requirements = list(requirements) 142 # get main type, and check mime type at the same time 143 main = split_mimetype(orig)[0] 144 # search most specific first 145 outputs = self._mtmap.get(orig) 146 if outputs is not None: 147 self._search_outputs(outputs, target, requirements, path, result) 148 # then search generic wildcard transforms 149 outputs = self._mtmainmap.get(main) 150 if outputs is not None: 151 self._search_outputs(outputs, target, requirements, path, result) 152 # we are done 153 return result
154
155 - def _search_outputs(self, outputs, target, requirements, path, result):
156 path.append(None) 157 for outputmimetype, transforms in outputs.items(): 158 for transform in transforms: 159 required = False 160 name = transform.name 161 if name in requirements: 162 requirements.remove(name) 163 required = True 164 if transform in path: 165 # avoid infinite loop... 166 continue 167 path[-1] = transform 168 if outputmimetype == target: 169 if not requirements: 170 result.append(path[:]) 171 else: 172 self._get_paths(outputmimetype, target, requirements, path, result) 173 if required: 174 requirements.append(name) 175 path.pop()
176