GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
function_class.py
Go to the documentation of this file.
1 # function_class.py -- Library for making python classes from a set
2 # of functions.
3 #
4 # Copyright (C) 2008 ParIT Worker Co-operative <[email protected]>
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License as
7 # published by the Free Software Foundation; either version 2 of
8 # the License, or (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, contact:
17 # Free Software Foundation Voice: +1-617-542-5942
18 # 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
19 # Boston, MA 02110-1301, USA [email protected]
20 #
21 # @author Mark Jenkins, ParIT Worker Co-operative <[email protected]>
22 
23 ## @file
24 # @brief Library for making python classes from a set of functions.
25 # @author Mark Jenkins, ParIT Worker Co-operative <[email protected]>
26 # @author Jeff Green, ParIT Worker Co-operative <[email protected]>
27 # @ingroup python_bindings
28 
29 INSTANCE_ARGUMENT = "instance"
30 
31 class ClassFromFunctions(object):
32  """Inherit this class to give yourself a python class that wraps a set of
33  functions that together constitute the methods of the class.
34 
35  The method functions must all have as a first argument an object
36  holding the instance data. There must also be a function that
37  returns a new instance of the class, the constructor.
38 
39  Your subclass must define
40  _module - The module where the method functions, including the
41  constructor can be found
42  _new_instance - The name of a function that serves as a constructor,
43  returning the instance data.
44 
45  To access the instance data, use the read-only property instance.
46 
47  To add some functions from _module as methods, call classmethods like
48  add_method and add_methods_with_prefix.
49  """
50  def __new__(cls, *args, **kargs):
51  # why reimpliment __new__? Because later on we're going to
52  # use new to avoid creating new instances when existing instances
53  # already exist with the same __instance value, or equivalent __instance
54  # values, where this is desirable...
55  return super(ClassFromFunctions, cls).__new__(cls)
56 
57  def __init__(self, *args, **kargs):
58  """Construct a new instance, using either the function
59  self._module[self._new_instance] or using existing instance
60  data. (specified with the keyword argument, instance)
61 
62  Pass the arguments that should be passed on to
63  self._module[self._new_instance] . Any arguments of that
64  are instances of ClassFromFunctions will be switched with the instance
65  data. (by calling the .instance property)
66  """
67  if INSTANCE_ARGUMENT in kargs:
68  self.__instance = kargs[INSTANCE_ARGUMENT]
69  else:
70  self.__instance = getattr(self._module, self._new_instance)(
71  *process_list_convert_to_instance(args) )
72 
73  def get_instance(self):
74  """Get the instance data.
75 
76  You can also call the instance property
77  """
78  return self.__instance
79 
80  instance = property(get_instance)
81 
82  # CLASS METHODS
83 
84  @classmethod
85  def add_method(cls, function_name, method_name):
86  """Add the function, method_name to this class as a method named name
87  """
88  def method_function(self, *meth_func_args):
89  return getattr(self._module, function_name)(
90  self.instance,
91  *process_list_convert_to_instance(meth_func_args) )
92 
93  setattr(cls, method_name, method_function)
94  setattr(method_function, "__name__", method_name)
95  return method_function
96 
97  @classmethod
98  def ya_add_classmethod(cls, function_name, method_name):
99  """Add the function, method_name to this class as a classmethod named name
100 
101  Taken from function_class and slightly modified.
102  """
103  def method_function(self, *meth_func_args):
104  return getattr(self._module, function_name)(
105  self,
106  *process_list_convert_to_instance(meth_func_args) )
107 
108  setattr(cls, method_name, classmethod(method_function))
109  setattr(method_function, "__name__", method_name)
110  return method_function
111 
112  @classmethod
113  def ya_add_method(cls, function_name, method_name):
114  """Add the function, method_name to this class as a method named name
115 
116  Taken from function_class and slightly modified.
117  """
118  def method_function(self, *meth_func_args):
119  return getattr(self._module, function_name)(
120  self,
121  *process_list_convert_to_instance(meth_func_args) )
122 
123  setattr(cls, method_name, method_function)
124  setattr(method_function, "__name__", method_name)
125  return method_function
126 
127  @classmethod
128  def add_methods_with_prefix(cls, prefix):
129  """Add a group of functions with the same prefix
130  """
131  for function_name, function_value, after_prefix in \
132  extract_attributes_with_prefix(cls._module, prefix):
133  cls.add_method(function_name, after_prefix)
134 
135  @classmethod
136  def add_constructor_and_methods_with_prefix(cls, prefix, constructor):
137  """Add a group of functions with the same prefix, and set the
138  _new_instance attribute to prefix + constructor
139  """
140  cls.add_methods_with_prefix(prefix)
141  cls._new_instance = prefix + constructor
142 
143  @classmethod
144  def decorate_functions(cls, decorator, *args):
145  for function_name in args:
146  setattr( cls, function_name,
147  decorator( getattr(cls, function_name) ) )
148 
149 def method_function_returns_instance(method_function, cls):
150  """A function decorator that is used to decorate method functions that
151  return instance data, to return instances instead.
152 
153  You can't use this decorator with @, because this function has a second
154  argument.
155  """
156  assert( 'instance' == INSTANCE_ARGUMENT )
157  def new_function(*args):
158  kargs = { INSTANCE_ARGUMENT : method_function(*args) }
159  if kargs['instance'] == None:
160  return None
161  else:
162  return cls( **kargs )
163 
164  return new_function
165 
166 def method_function_returns_instance_list(method_function, cls):
167  def new_function(*args):
168  return [ cls( **{INSTANCE_ARGUMENT: item} )
169  for item in method_function(*args) ]
170  return new_function
171 
172 def methods_return_instance_lists(cls, function_dict):
173  for func_name, instance_name in function_dict.iteritems():
174  setattr(cls, func_name,
175  method_function_returns_instance_list(
176  getattr(cls, func_name), instance_name))
177 
178 def default_arguments_decorator(function, *args):
179  """Decorates a function to give it default, positional arguments
180 
181  You can't use this decorator with @, because this function has more
182  than one argument.
183  """
184  def new_function(*function_args):
185  new_argset = list(function_args)
186  new_argset.extend( args[ len(function_args): ] )
187  return function( *new_argset )
188  return new_function
189 
190 def return_instance_if_value_has_it(value):
191  """Return value.instance if value is an instance of ClassFromFunctions,
192  else return value
193  """
194  if isinstance(value, ClassFromFunctions):
195  return value.instance
196  else:
197  return value
198 
199 def process_list_convert_to_instance( value_list ):
200  """Return a list built from value_list, where if a value is in an instance
201  of ClassFromFunctions, we put value.instance in the list instead.
202 
203  Things that are not instances of ClassFromFunctions are returned to
204  the new list unchanged.
205  """
206  return [ return_instance_if_value_has_it(value)
207  for value in value_list ]
208 
209 def extract_attributes_with_prefix(obj, prefix):
210  """Generator that iterates through the attributes of an object and
211  for any attribute that matches a prefix, this yields
212  the attribute name, the attribute value, and the text that appears
213  after the prefix in the name
214  """
215  for attr_name, attr_value in obj.__dict__.iteritems():
216  if attr_name.startswith(prefix):
217  after_prefix = attr_name[ len(prefix): ]
218  yield attr_name, attr_value, after_prefix
219 
220 def methods_return_instance(cls, function_dict):
221  """Iterates through a dictionary of function name strings and instance names
222  and sets the function to return the associated instance
223  """
224  for func_name, instance_name in function_dict.iteritems():
225  setattr(cls, func_name,
226  method_function_returns_instance( getattr(cls, func_name), instance_name))
227