diff options
Diffstat (limited to '')
-rw-r--r-- | office/gnucash/missing-py/function_class.py | 227 |
1 files changed, 227 insertions, 0 deletions
diff --git a/office/gnucash/missing-py/function_class.py b/office/gnucash/missing-py/function_class.py new file mode 100644 index 0000000000..f628667a4f --- /dev/null +++ b/office/gnucash/missing-py/function_class.py @@ -0,0 +1,227 @@ +# function_class.py -- Library for making python classes from a set +# of functions. +# +# Copyright (C) 2008 ParIT Worker Co-operative <paritinfo@parit.ca> +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, contact: +# Free Software Foundation Voice: +1-617-542-5942 +# 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 +# Boston, MA 02110-1301, USA gnu@gnu.org +# +# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> + +## @file +# @brief Library for making python classes from a set of functions. +# @author Mark Jenkins, ParIT Worker Co-operative <mark@parit.ca> +# @author Jeff Green, ParIT Worker Co-operative <jeff@parit.ca> +# @ingroup python_bindings + +INSTANCE_ARGUMENT = "instance" + +class ClassFromFunctions(object): + """Inherit this class to give yourself a python class that wraps a set of + functions that together constitute the methods of the class. + + The method functions must all have as a first argument an object + holding the instance data. There must also be a function that + returns a new instance of the class, the constructor. + + Your subclass must define + _module - The module where the method functions, including the + constructor can be found + _new_instance - The name of a function that serves as a constructor, + returning the instance data. + + To access the instance data, use the read-only property instance. + + To add some functions from _module as methods, call classmethods like + add_method and add_methods_with_prefix. + """ + def __new__(cls, *args, **kargs): + # why reimpliment __new__? Because later on we're going to + # use new to avoid creating new instances when existing instances + # already exist with the same __instance value, or equivalent __instance + # values, where this is desirable... + return super(ClassFromFunctions, cls).__new__(cls) + + def __init__(self, *args, **kargs): + """Construct a new instance, using either the function + self._module[self._new_instance] or using existing instance + data. (specified with the keyword argument, instance) + + Pass the arguments that should be passed on to + self._module[self._new_instance] . Any arguments of that + are instances of ClassFromFunctions will be switched with the instance + data. (by calling the .instance property) + """ + if INSTANCE_ARGUMENT in kargs: + self.__instance = kargs[INSTANCE_ARGUMENT] + else: + self.__instance = getattr(self._module, self._new_instance)( + *process_list_convert_to_instance(args) ) + + def get_instance(self): + """Get the instance data. + + You can also call the instance property + """ + return self.__instance + + instance = property(get_instance) + + # CLASS METHODS + + @classmethod + def add_method(cls, function_name, method_name): + """Add the function, method_name to this class as a method named name + """ + def method_function(self, *meth_func_args): + return getattr(self._module, function_name)( + self.instance, + *process_list_convert_to_instance(meth_func_args) ) + + setattr(cls, method_name, method_function) + setattr(method_function, "__name__", method_name) + return method_function + + @classmethod + def ya_add_classmethod(cls, function_name, method_name): + """Add the function, method_name to this class as a classmethod named name + + Taken from function_class and slightly modified. + """ + def method_function(self, *meth_func_args): + return getattr(self._module, function_name)( + self, + *process_list_convert_to_instance(meth_func_args) ) + + setattr(cls, method_name, classmethod(method_function)) + setattr(method_function, "__name__", method_name) + return method_function + + @classmethod + def ya_add_method(cls, function_name, method_name): + """Add the function, method_name to this class as a method named name + + Taken from function_class and slightly modified. + """ + def method_function(self, *meth_func_args): + return getattr(self._module, function_name)( + self, + *process_list_convert_to_instance(meth_func_args) ) + + setattr(cls, method_name, method_function) + setattr(method_function, "__name__", method_name) + return method_function + + @classmethod + def add_methods_with_prefix(cls, prefix): + """Add a group of functions with the same prefix + """ + for function_name, function_value, after_prefix in \ + extract_attributes_with_prefix(cls._module, prefix): + cls.add_method(function_name, after_prefix) + + @classmethod + def add_constructor_and_methods_with_prefix(cls, prefix, constructor): + """Add a group of functions with the same prefix, and set the + _new_instance attribute to prefix + constructor + """ + cls.add_methods_with_prefix(prefix) + cls._new_instance = prefix + constructor + + @classmethod + def decorate_functions(cls, decorator, *args): + for function_name in args: + setattr( cls, function_name, + decorator( getattr(cls, function_name) ) ) + +def method_function_returns_instance(method_function, cls): + """A function decorator that is used to decorate method functions that + return instance data, to return instances instead. + + You can't use this decorator with @, because this function has a second + argument. + """ + assert( 'instance' == INSTANCE_ARGUMENT ) + def new_function(*args): + kargs = { INSTANCE_ARGUMENT : method_function(*args) } + if kargs['instance'] == None: + return None + else: + return cls( **kargs ) + + return new_function + +def method_function_returns_instance_list(method_function, cls): + def new_function(*args): + return [ cls( **{INSTANCE_ARGUMENT: item} ) + for item in method_function(*args) ] + return new_function + +def methods_return_instance_lists(cls, function_dict): + for func_name, instance_name in function_dict.iteritems(): + setattr(cls, func_name, + method_function_returns_instance_list( + getattr(cls, func_name), instance_name)) + +def default_arguments_decorator(function, *args): + """Decorates a function to give it default, positional arguments + + You can't use this decorator with @, because this function has more + than one argument. + """ + def new_function(*function_args): + new_argset = list(function_args) + new_argset.extend( args[ len(function_args): ] ) + return function( *new_argset ) + return new_function + +def return_instance_if_value_has_it(value): + """Return value.instance if value is an instance of ClassFromFunctions, + else return value + """ + if isinstance(value, ClassFromFunctions): + return value.instance + else: + return value + +def process_list_convert_to_instance( value_list ): + """Return a list built from value_list, where if a value is in an instance + of ClassFromFunctions, we put value.instance in the list instead. + + Things that are not instances of ClassFromFunctions are returned to + the new list unchanged. + """ + return [ return_instance_if_value_has_it(value) + for value in value_list ] + +def extract_attributes_with_prefix(obj, prefix): + """Generator that iterates through the attributes of an object and + for any attribute that matches a prefix, this yields + the attribute name, the attribute value, and the text that appears + after the prefix in the name + """ + for attr_name, attr_value in obj.__dict__.iteritems(): + if attr_name.startswith(prefix): + after_prefix = attr_name[ len(prefix): ] + yield attr_name, attr_value, after_prefix + +def methods_return_instance(cls, function_dict): + """Iterates through a dictionary of function name strings and instance names + and sets the function to return the associated instance + """ + for func_name, instance_name in function_dict.iteritems(): + setattr(cls, func_name, + method_function_returns_instance( getattr(cls, func_name), instance_name)) + |