|
GnuCash 2.4.99
|
00001 # function_class.py -- Library for making python classes from a set 00002 # of functions. 00003 # 00004 # Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@parit.ca> 00005 # This program is free software; you can redistribute it and/or 00006 # modify it under the terms of the GNU General Public License as 00007 # published by the Free Software Foundation; either version 2 of 00008 # the License, or (at your option) any later version. 00009 # 00010 # This program is distributed in the hope that it will be useful, 00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of 00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00013 # GNU General Public License for more details. 00014 # 00015 # You should have received a copy of the GNU General Public License 00016 # along with this program; if not, contact: 00017 # Free Software Foundation Voice: +1-617-542-5942 00018 # 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 00019 # Boston, MA 02110-1301, USA gnu@gnu.org 00020 # 00021 # @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> 00022 00023 ## @file 00024 # @brief Library for making python classes from a set of functions. 00025 # @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> 00026 # @author Jeff Green, ParIT Worker Co-operative <jeff@parit.ca> 00027 # @ingroup python_bindings 00028 00029 INSTANCE_ARGUMENT = "instance" 00030 00031 class ClassFromFunctions(object): 00032 """Inherit this class to give yourself a python class that wraps a set of 00033 functions that together constitute the methods of the class. 00034 00035 The method functions must all have as a first argument an object 00036 holding the instance data. There must also be a function that 00037 returns a new instance of the class, the constructor. 00038 00039 Your subclass must define 00040 _module - The module where the method functions, including the 00041 constructor can be found 00042 _new_instance - The name of a function that serves as a constructor, 00043 returning the instance data. 00044 00045 To access the instance data, use the read-only property instance. 00046 00047 To add some functions from _module as methods, call classmethods like 00048 add_method and add_methods_with_prefix. 00049 """ 00050 def __new__(cls, *args, **kargs): 00051 # why reimpliment __new__? Because later on we're going to 00052 # use new to avoid creating new instances when existing instances 00053 # already exist with the same __instance value, or equivalent __instance 00054 # values, where this is desirable... 00055 return super(ClassFromFunctions, cls).__new__(cls) 00056 00057 def __init__(self, *args, **kargs): 00058 """Construct a new instance, using either the function 00059 self._module[self._new_instance] or using existing instance 00060 data. (specified with the keyword argument, instance) 00061 00062 Pass the arguments that should be passed on to 00063 self._module[self._new_instance] . Any arguments of that 00064 are instances of ClassFromFunctions will be switched with the instance 00065 data. (by calling the .instance property) 00066 """ 00067 if INSTANCE_ARGUMENT in kargs: 00068 self.__instance = kargs[INSTANCE_ARGUMENT] 00069 else: 00070 self.__instance = getattr(self._module, self._new_instance)( 00071 *process_list_convert_to_instance(args) ) 00072 00073 def get_instance(self): 00074 """Get the instance data. 00075 00076 You can also call the instance property 00077 """ 00078 return self.__instance 00079 00080 instance = property(get_instance) 00081 00082 # CLASS METHODS 00083 00084 @classmethod 00085 def add_method(cls, function_name, method_name): 00086 """Add the function, method_name to this class as a method named name 00087 """ 00088 def method_function(self, *meth_func_args): 00089 return getattr(self._module, function_name)( 00090 self.instance, 00091 *process_list_convert_to_instance(meth_func_args) ) 00092 00093 setattr(cls, method_name, method_function) 00094 setattr(method_function, "__name__", method_name) 00095 return method_function 00096 00097 @classmethod 00098 def ya_add_classmethod(cls, function_name, method_name): 00099 """Add the function, method_name to this class as a classmethod named name 00100 00101 Taken from function_class and slightly modified. 00102 """ 00103 def method_function(self, *meth_func_args): 00104 return getattr(self._module, function_name)( 00105 self, 00106 *process_list_convert_to_instance(meth_func_args) ) 00107 00108 setattr(cls, method_name, classmethod(method_function)) 00109 setattr(method_function, "__name__", method_name) 00110 return method_function 00111 00112 @classmethod 00113 def ya_add_method(cls, function_name, method_name): 00114 """Add the function, method_name to this class as a method named name 00115 00116 Taken from function_class and slightly modified. 00117 """ 00118 def method_function(self, *meth_func_args): 00119 return getattr(self._module, function_name)( 00120 self, 00121 *process_list_convert_to_instance(meth_func_args) ) 00122 00123 setattr(cls, method_name, method_function) 00124 setattr(method_function, "__name__", method_name) 00125 return method_function 00126 00127 @classmethod 00128 def add_methods_with_prefix(cls, prefix): 00129 """Add a group of functions with the same prefix 00130 """ 00131 for function_name, function_value, after_prefix in \ 00132 extract_attributes_with_prefix(cls._module, prefix): 00133 cls.add_method(function_name, after_prefix) 00134 00135 @classmethod 00136 def add_constructor_and_methods_with_prefix(cls, prefix, constructor): 00137 """Add a group of functions with the same prefix, and set the 00138 _new_instance attribute to prefix + constructor 00139 """ 00140 cls.add_methods_with_prefix(prefix) 00141 cls._new_instance = prefix + constructor 00142 00143 @classmethod 00144 def decorate_functions(cls, decorator, *args): 00145 for function_name in args: 00146 setattr( cls, function_name, 00147 decorator( getattr(cls, function_name) ) ) 00148 00149 def method_function_returns_instance(method_function, cls): 00150 """A function decorator that is used to decorate method functions that 00151 return instance data, to return instances instead. 00152 00153 You can't use this decorator with @, because this function has a second 00154 argument. 00155 """ 00156 assert( 'instance' == INSTANCE_ARGUMENT ) 00157 def new_function(*args): 00158 kargs = { INSTANCE_ARGUMENT : method_function(*args) } 00159 return cls( **kargs ) 00160 00161 return new_function 00162 00163 def method_function_returns_instance_list(method_function, cls): 00164 def new_function(*args): 00165 return [ cls( **{INSTANCE_ARGUMENT: item} ) 00166 for item in method_function(*args) ] 00167 return new_function 00168 00169 def methods_return_instance_lists(cls, function_dict): 00170 for func_name, instance_name in function_dict.iteritems(): 00171 setattr(cls, func_name, 00172 method_function_returns_instance_list( 00173 getattr(cls, func_name), instance_name)) 00174 00175 def default_arguments_decorator(function, *args): 00176 """Decorates a function to give it default, positional arguments 00177 00178 You can't use this decorator with @, because this function has more 00179 than one argument. 00180 """ 00181 def new_function(*function_args): 00182 new_argset = list(function_args) 00183 new_argset.extend( args[ len(function_args): ] ) 00184 return function( *new_argset ) 00185 return new_function 00186 00187 def return_instance_if_value_has_it(value): 00188 """Return value.instance if value is an instance of ClassFromFunctions, 00189 else return value 00190 """ 00191 if isinstance(value, ClassFromFunctions): 00192 return value.instance 00193 else: 00194 return value 00195 00196 def process_list_convert_to_instance( value_list ): 00197 """Return a list built from value_list, where if a value is in an instance 00198 of ClassFromFunctions, we put value.instance in the list instead. 00199 00200 Things that are not instances of ClassFromFunctions are returned to 00201 the new list unchanged. 00202 """ 00203 return [ return_instance_if_value_has_it(value) 00204 for value in value_list ] 00205 00206 def extract_attributes_with_prefix(obj, prefix): 00207 """Generator that iterates through the attributes of an object and 00208 for any attribute that matches a prefix, this yields 00209 the attribute name, the attribute value, and the text that appears 00210 after the prefix in the name 00211 """ 00212 for attr_name, attr_value in obj.__dict__.iteritems(): 00213 if attr_name.startswith(prefix): 00214 after_prefix = attr_name[ len(prefix): ] 00215 yield attr_name, attr_value, after_prefix 00216 00217 def methods_return_instance(cls, function_dict): 00218 """Iterates through a dictionary of function name strings and instance names 00219 and sets the function to return the associated instance 00220 """ 00221 for func_name, instance_name in function_dict.iteritems(): 00222 setattr(cls, func_name, 00223 method_function_returns_instance( getattr(cls, func_name), instance_name)) 00224
1.7.4