<s> from collections import defaultdict <EOL> class UnrecognisedOperationError ( LookupError ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> class UnavailableOperationError ( LookupError ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> class UnroutableOperationError ( LookupError ) : <EOL> """<STR_LIT>""" <EOL> pass <EOL> class WillowRegistry ( object ) : <EOL> def __init__ ( self ) : <EOL> self . _registered_image_classes = set ( ) <EOL> self . _unavailable_image_classes = dict ( ) <EOL> self . _registered_operations = defaultdict ( dict ) <EOL> self . _registered_converters = dict ( ) <EOL> self . _registered_converter_costs = dict ( ) <EOL> def register_operation ( self , image_class , operation_name , func ) : <EOL> self . _registered_operations [ image_class ] [ operation_name ] = func <EOL> def register_converter ( self , from_image_class , to_image_class , func , cost = None ) : <EOL> self . _registered_converters [ from_image_class , to_image_class ] = func <EOL> if cost is not None : <EOL> self . _registered_converter_costs [ from_image_class , to_image_class ] = cost <EOL> def register_image_class ( self , image_class ) : <EOL> self . _registered_image_classes . add ( image_class ) <EOL> try : <EOL> image_class . check ( ) <EOL> except Exception as e : <EOL> self . _unavailable_image_classes [ image_class ] = e <EOL> for attr in dir ( image_class ) : <EOL> val = getattr ( image_class , attr ) <EOL> if hasattr ( val , '<STR_LIT>' ) : <EOL> self . register_operation ( image_class , val . __name__ , val ) <EOL> elif hasattr ( val , '<STR_LIT>' ) : <EOL> self . register_converter ( image_class , val . _willow_converter_to [ <NUM_LIT:0> ] , val , cost = val . _willow_converter_to [ <NUM_LIT:1> ] ) <EOL> elif hasattr ( val , '<STR_LIT>' ) : <EOL> for converter_from , cost in val . _willow_converter_from : <EOL> self . register_converter ( converter_from , image_class , val , cost = cost ) <EOL> def register_plugin ( self , plugin ) : <EOL> image_classes = getattr ( plugin , '<STR_LIT>' , [ ] ) <EOL> operations = getattr ( plugin , '<STR_LIT>' , [ ] ) <EOL> converters = getattr ( plugin , '<STR_LIT>' , [ ] ) <EOL> for image_class in image_classes : <EOL> self . register_image_class ( image_class ) <EOL> for operation in operations : <EOL> self . register_operation ( operation [ <NUM_LIT:0> ] , operation [ <NUM_LIT:1> ] , operation [ <NUM_LIT:2> ] ) <EOL> for converter in converters : <EOL> self . register_converter ( converter [ <NUM_LIT:0> ] , converter [ <NUM_LIT:1> ] , converter [ <NUM_LIT:2> ] ) <EOL> def get_operation ( self , image_class , operation_name ) : <EOL> return self . _registered_operations [ image_class ] [ operation_name ] <EOL> def operation_exists ( self , operation_name ) : <EOL> for image_class_operations in self . _registered_operations . values ( ) : <EOL> if operation_name in image_class_operations : <EOL> return True <EOL> return False <EOL> def get_converter ( self , from_image_class , to_image_class ) : <EOL> return self . _registered_converters [ from_image_class , to_image_class ] <EOL> def get_converter_cost ( self , from_image_class , to_image_class ) : <EOL> return self . _registered_converter_costs . get ( ( from_image_class , to_image_class ) , <NUM_LIT:100> ) <EOL> def get_image_classes ( self , with_operation = None , available = None ) : <EOL> image_classes = self . _registered_image_classes . copy ( ) <EOL> if with_operation : <EOL> image_classes = set ( filter ( lambda image_class : image_class in self . _registered_operations and with_operation in self . _registered_operations [ image_class ] , image_classes ) ) <EOL> if not image_classes : <EOL> raise UnrecognisedOperationError ( "<STR_LIT>" . format ( with_operation ) ) <EOL> if available : <EOL> available_image_classes = image_classes - set ( self . _unavailable_image_classes . keys ( ) ) <EOL> if not available_image_classes : <EOL> raise UnavailableOperationError ( '<STR_LIT:\n>' . join ( [ <EOL> "<STR_LIT>" . format ( with_operation ) <EOL> ] + [ <EOL> "<STR_LIT>" . format ( <EOL> image_class_name = image_class . __name__ , <EOL> error_message = str ( self . _unavailable_image_classes . get ( image_class , "<STR_LIT>" ) ) <EOL> ) <EOL> for image_class in image_classes <EOL> ] ) ) <EOL> return available_image_classes <EOL> else : <EOL> return image_classes <EOL> def get_converters_from ( self , from_image_class ) : <EOL> """<STR_LIT>""" <EOL> for ( c_from , c_to ) , converter in self . _registered_converters . items ( ) : <EOL> if c_from is from_image_class : <EOL> yield converter , c_to <EOL> def find_all_paths ( self , start , end , path = [ ] , seen_classes = set ( ) ) : <EOL> """<STR_LIT>""" <EOL> if start == end : <EOL> return [ path ] <EOL> if start in seen_classes : <EOL> return [ ] <EOL> if not start in self . _registered_image_classes or start in self . _unavailable_image_classes : <EOL> return [ ] <EOL> paths = [ ] <EOL> for converter , next_class in self . get_converters_from ( start ) : <EOL> if next_class not in path : <EOL> newpaths = self . find_all_paths ( <EOL> next_class , end , <EOL> path + [ ( converter , next_class ) ] , <EOL> seen_classes . union ( { start } ) ) <EOL> paths . extend ( newpaths ) <EOL> return paths <EOL> def get_path_cost ( self , start , path ) : <EOL> """<STR_LIT>""" <EOL> last_class = start <EOL> total_cost = <NUM_LIT:0> <EOL> for converter , next_class in path : <EOL> total_cost += self . get_converter_cost ( last_class , next_class ) <EOL> last_class = next_class <EOL> return total_cost <EOL> def find_shortest_path ( self , start , end ) : <EOL> """<STR_LIT>""" <EOL> current_path = None <EOL> current_cost = None <EOL> for path in self . find_all_paths ( start , end ) : <EOL> cost = self . get_path_cost ( start , path ) <EOL> if current_cost is None or cost < current_cost : <EOL> current_cost = cost <EOL> current_path = path <EOL> return current_path , current_cost <EOL> def find_closest_image_class ( self , start , image_classes ) : <EOL> """<STR_LIT>""" <EOL> current_class = None <EOL> current_path = None <EOL> current_cost = None <EOL> for image_class in image_classes : <EOL> path , cost = self . find_shortest_path ( start , image_class ) <EOL> if current_cost is None or cost < current_cost : <EOL> current_class = image_class <EOL> current_cost = cost <EOL> current_path = path <EOL> return current_class , current_path , current_cost <EOL> def find_operation ( self , from_class , operation_name ) : <EOL> """<STR_LIT>""" <EOL> try : <EOL> func = self . get_operation ( from_class , operation_name ) <EOL> cls = from_class <EOL> path = [ ] <EOL> cost = <NUM_LIT:0> <EOL> except LookupError : <EOL> image_classes = self . get_image_classes ( <EOL> with_operation = operation_name , <EOL> available = True ) <EOL> cls , path , cost = self . find_closest_image_class ( from_class , image_classes ) <EOL> if path is None : <EOL> raise UnroutableOperationError ( <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" . format ( <EOL> operation_name , <EOL> '<STR_LIT:U+002CU+0020>' . join ( image_class . __name__ for image_class in image_classes ) , <EOL> from_class . __name__ <EOL> ) ) <EOL> func = self . get_operation ( cls , operation_name ) <EOL> return func , cls , path , cost <EOL> registry = WillowRegistry ( ) </s>
