Source code for TASSELpy.utils.Overloading

#from TASSELpy.javaObj import javaObj,genericJavaObj
import TASSELpy.javaObj
from TASSELpy.utils.OrderedSet import lruOrderedSet
from TASSELpy.utils.caching import LRUcache
from functools import wraps,update_wrapper,partial
import numpy as np
import javabridge
import re

if not javabridge.get_env():
    from TASSELpy import TASSELbridge
    TASSELbridge.start()

java_imports = {'String':'java/lang/String'}
str_class = javabridge.get_env().find_class(java_imports['String'])
to_java_string = lambda x: javabridge.make_instance(java_imports['String'],
                                                    "(Ljava/lang/String;)V", x)
## Define numpy types that need to be converted to java arrays
[docs]def make_string_array(arr): java_arr = javabridge.get_env().make_object_array(len(arr),str_class) for i,s in enumerate(arr): javabridge.get_env().set_object_array_element(java_arr, i,javabridge.get_env().new_string_utf(s)) return java_arr
array_conversion_func_dict = {np.int8: javabridge.get_env().make_byte_array, np.int32: javabridge.get_env().make_int_array, np.int64: javabridge.get_env().make_long_array, np.string_: make_string_array} ## Dictionary that automatically inserts both k->v and v->k under set
[docs]class bidirectional_dict(dict): ## Instantiates bidirectionary dictionary # @param args A bunch of pairs to be (key,values)
[docs] def __init__(self, *args): super(bidirectional_dict,self).__init__() for k,v in args: self[k] = v
def __setitem__(self,k,v): super(bidirectional_dict,self).__setitem__(k,v) super(bidirectional_dict,self).__setitem__(v,k) ## Define a set of equivalent python types in signatures
eq_types = bidirectional_dict((int,np.int32),(long,np.int64), (str,unicode)) ## Define a set of types that, while actually equivalent in python, # should not be considered equivalent for signatures segregate_types = bidirectional_dict((int,np.int64)) ## Default-dict type dictionary to work with signatures
[docs]class signature_dict(dict): def __missing__(self, key): found = False ## Find the right key for k in self: if len(k) != len(key): continue use = True ## Check all arguments in key to see if everything # subclass instance of existing key for i,arg in enumerate(key): # Skip if NoneType if arg == type(None): continue # Check if the arg type should be segregated from the k[i] type if k[i] in segregate_types: if arg == segregate_types[k[i]]: use = False break # Check if k[i] a subclass of this type in key if not issubclass(arg,k[i]): if k[i] in eq_types: if not (arg == eq_types[k[i]]): use = False break else: use = False break # Set the value if appropriate if use: self[key] = self[k] found = True break if not found: raise KeyError("Arguments do not fit any signature") else: return self[key] ## Decorator class used to overload java wrapper functions # that are non-constructor members of an instantiated class
[docs]class javaOverload(object): """ Creates a function decorator for a javabridge function that is the member of a class Arguments: func_name -- The name of the java function args -- tuples of form (java_signature, (python arg types),post_process_func) e.g. ("(I)I",(int,),None) or ("(I)[L",(int,),lambda x: javabridge.get_env().get_long_array_elements(x)) """
[docs] def __init__(self, func_name, *args): """ Creates a function decorator for a javabridge function that is the member of a class Arguments: func_name -- The name of the java function args -- tuples of form (java_signature, (python arg types),post_process_func) e.g. ("(I)I",(int,),None) or ("(I)[L",(int,),lambda x: javabridge.get_env().get_long_array_elements(x)) """ self.func_name = func_name ## Create signature dictionary of {(python args) -> function} self.func_dict = signature_dict() self.return_func_dict = signature_dict() for sig, pyargs, py_return in args: pyargs = tuple(map(self._process_pyarg, pyargs)) self.func_dict[pyargs] = javabridge.make_method(func_name, sig) if py_return is None: self.return_func_dict[pyargs] = lambda x: x else: self.return_func_dict[pyargs] = self._process_py_return(py_return)
def _process_pyarg(self, x): if isinstance(x, tuple): #return getattr(__import__(x[0],globals(),locals(),[x[1]]),x[1]) return getattr(__import__(x[0],globals(),locals()),x[1]) else: return x def _process_py_return(self, x): if isinstance(x, tuple): #func = getattr(__import__(x[0],globals(),locals(),[x[1]]),x[1]).__init__ func = getattr(__import__(x[0],globals(),locals()),x[1]).__init__ return lambda y: func(obj = y) else: return x def __call__(self, f): @wraps(f) def wrapped_f(*args): if len(args) > 1: # Get the right function based on argument types key = tuple(map(type, args[1:])) # Convert any wrapped java items to their java objects args = list(args) args[1:] = map(lambda x: (x.o if isinstance(x,TASSELpy.javaObj.javaObj) else x), args[1:]) return_val = self.func_dict[key](*args) if self.func_name != 'getClass': return self.return_func_dict[key](return_val) else: # Special method for getClass to put in generic type return_obj = self.return_func_dict[key](return_val) if not hasattr(return_obj, 'generic_dict'): return return_obj else: return_obj.generic_dict['/@1/'] = type(args[0]) return return_obj else: return_val = self.func_dict[()](*args) return self.return_func_dict[()](return_val) return wrapped_f ## Decorator class used to overload constructors
[docs]class javaConstructorOverload(object): """ Creates a function decorator for a javabridge function that is a constructor for an instantiated class Arguments: class_name -- The name of the java class, as in path/to/class args -- tuples of form (java signature,(python arg types)) e.g. ("(I)V",(int,)) """
[docs] def __init__(self, class_name, *args): """ Creates a function decorator for a javabridge function that is a constructor for an instantiated class Arguments: class_name -- The name of the java class, as in path/to/class args -- tuples of form (java signature,(python arg types)) e.g. ("(I)V",(int,)) """ self.class_name = class_name ## Create signature dictionary of {(python args) -> function} self.func_dict = signature_dict() for sig, pyargs in args: pyargs = tuple(map(self._process_pyarg, pyargs)) self.func_dict[pyargs] = javabridge.make_new(self.class_name,sig)
def _process_pyarg(self, x): if isinstance(x, tuple): #return getattr(__import__(x[0],globals(),locals(),[x[1]]),x[1]) return getattr(__import__(x[0],globals(),locals()),x[1]) else: return x def __call__(self, f): @wraps(f) def wrapped_f(*args, **kwargs): if 'obj' in kwargs: ## If wrapping existing java object, put in # raw Java object as attribute so that methods can # find it if isinstance(kwargs['obj'], unicode): args[0].o = to_java_string(kwargs['obj']) else: args[0].o = kwargs['obj'] elif len(self.func_dict) == 0: ## Skip if there are no actual java functions being wrapped pass else: if len(args) > 1: # Get the right function based on argument types key = tuple(map(type, args[1:])) # Convert any wrapped java items to their java objects args = list(args) #args[1:] = map(lambda x: (x.o if not hasattr(x,'toPrimative') else x.toPrimative()) \ # if isinstance(x,TASSELpy.javaObj.javaObj) else x, # args[1:]) args[1:] = map(lambda x: (x.o if isinstance(x,TASSELpy.javaObj.javaObj) else x), args[1:]) self.func_dict[key](*args) else: self.func_dict[()](*args) if 'generic' in kwargs: ## Put in the generic type dictionary if necessary args[0].generic_dict = dict(zip(map(lambda x: '/@%d/' % x, xrange(1,len(kwargs['generic'])+1)), kwargs['generic'])) # Make the call from the function body f(*args, **kwargs) return wrapped_f
[docs]class javaStaticOverload(object): """ Creates a function decorator for a javabridge static function Arguments: class_name -- The name of the java class containing the method, as in path/to/class func_name -- The name of the java function args -- tuples of form (java signature, (python arg types), post process function) e.g. ("(I)I",(int,),None) or ("(I)[L",(int,),lambda x: javabridge.get_env().get_long_array_elements(x)) """
[docs] def __init__(self, class_name, func_name, *args): """ Creates a function decorator for a javabridge static function Arguments: class_name -- The name of the java class containing the method, as in path/to/class func_name -- The name of the java function args -- tuples of form (java signature, (python arg types), post process function) e.g. ("(I)I",(int,),None) or ("(I)[L",(int,),lambda x: javabridge.get_env().get_long_array_elements(x)) """ self.func_name = func_name self.class_name = class_name ## Create signature dictionary of {(python args) -> function} self.func_dict = signature_dict() self.return_func_dict = signature_dict() for sig, pyargs, py_return in args: self.func_dict[pyargs] = javabridge.make_static_call(self.class_name, self.func_name,sig) if py_return is None: self.return_func_dict[pyargs] = lambda x: x else: self.return_func_dict[pyargs] = py_return
def _arg_convert(self, x): if isinstance(x, TASSELpy.javaObj.javaObj): return x.o elif isinstance(x, str): return to_java_string(x) else: return x def __call__(self, f): def wrapped_f(*args): # Get the right function based on argument types key = tuple(map(type,args)) # Convert any wrapped java items to their java objects args = list(args) args = map(self._arg_convert, args) # Convert any numpy arrays to their java arrays if np.ndarray in key: args = map(lambda x: array_conversion_func_dict[x.dtype.type](x) \ if type(x) == np.ndarray else x, args) return_val = self.func_dict[key](*args) return self.return_func_dict[key](return_val) wrapped_f = update_wrapper(wrapped_f,f, assigned=('__doc__','__name__','__module__')) wrapped_f = staticmethod(wrapped_f) return wrapped_f ## Decorator class used to overload java wrapper functions # that are non-constructor members of an instantiated class # and return and/or accept a generic type
[docs]class javaGenericOverload(object): """ Creates a function decorator for a javabridge function that is the member of a class and returns and/or accepts a generic type Arguments: func_name -- The name of the java function args -- tuples of form (java_signature, (python arg types),post_process_func) Java signatures should have java/lang/Object where generic types go. In the Python arg types, a generic types specified as, say (type1,type2) in the generic argument of the constructor should be specified as a string "/@1/" or "/@2/", corresponding to type1 and type2 respectively. In the post_process_func, you can specify None or a function as usual in order to deal with pre-specified types. Alternatively, you can put in the string "/@1/" or whatever the corresponding string is for a given type in order to send the return object through the constructor of that type (e.g. type1(obj=x)). In the case of types that should receive generic arguments, you can specify a dictionary with 'type' and 'generic' keys. For instance, to send the return object to a wrapper class named MyClass that should receive type1 as its one and only generic type argument, you can put in the following dictionary: dict(type=MyClass, generic=("/@1/",)) """
[docs] def __init__(self, func_name, *args): """ Creates a function decorator for a javabridge function that is the member of a class Arguments: func_name -- The name of the java function args -- tuples of form (java_signature, (python arg types or None),post_process_func) For java signature, put /@/ sign where the actual type of the generic should be placed. For python args/return type put None where it should be Note that if the returned type is the generic, the post-process function will cast to the specified type unless otherwise specified e.g. ("(/@/)I",(None,),None) or ("(/@/)[L",(None,),lambda x: javabridge.get_env().get_long_array_elements(x)) """ self.func_name = func_name # Store function parameters self.func_params = args # Set the sig_set dictionary self.func_dict = LRUcache(lambda x: x) self.return_func_dict = LRUcache(lambda x: x)
def __call__(self, f): @wraps(f) def wrapped_f(*args): ## If the signature is not set, do it if args[0].o not in self.func_dict: self.func_dict[args[0].o] = signature_dict() self.return_func_dict[args[0].o] = signature_dict() ## Set up signature dictionary ## for sig, pyargs, py_return in self.func_params: ## Replace any instances of strings in the pyargs with the python type pyargs = tuple(map(lambda x: (args[0].generic_dict[x] if \ x in args[0].generic_dict else TASSELpy.javaObj.javaObj) if \ type(x) == str else x,pyargs)) # Set func_dict[args[0].o] entries self.func_dict[args[0].o][pyargs] = javabridge.make_method(self.func_name,sig) if type(py_return) == str: ## If return function is replaced with string, meaning # to instantiate generic type if hasattr(args[0].generic_dict[py_return],'generic_dict'): generic_tuple = tuple(map(lambda x: x[1], sorted(args[0].generic_dict[py_return].generic_dict.items()))) self.return_func_dict[args[0].o][pyargs] = lambda x: \ args[0].generic_dict[py_return](obj=x, generic=generic_tuple) if \ isinstance(x,javabridge.JB_Object) else \ (args[0].generic_dict[py_return](x) if x is not None else None) else: def da_return_func(x): if isinstance(x, javabridge.JB_Object): return args[0].generic_dict[py_return](obj=x) elif x is None: return None else: try: return args[0].generic_dict[py_return](x) except KeyError: obj = None if isinstance(x, str) or isinstance(x,unicode): obj = javabridge.make_instance('java/lang/String', '(Ljava/lang/String;)V',x) elif isinstance(x, int): obj = javabridge.make_instance('java/lang/Integer', '(Ljava/lang/Integer;)V',x) elif isinstance(x, float): obj = javabridge.make_instance('java/lang/Double', '(Ljava/lang/Double;)V',x) if obj is None: return obj else: return args[0].generic_dict[py_return](obj=obj) self.return_func_dict[args[0].o][pyargs] = da_return_func elif isinstance(py_return, dict): ## If this is a dictionary, specify the generic type # and make constructor call method if 'type' not in py_return: raise ValueError("Return type of object not given") elif 'generic' not in py_return: raise ValueError("Generic type(s) for return object not given") self.return_func_dict[args[0].o][pyargs] = \ lambda x: py_return['type'](obj=x, generic=tuple(map(lambda y: args[0].generic_dict[y] if \ isinstance(y,str) else y, py_return['generic']))) if \ isinstance(x,javabridge.JB_Object) else x elif py_return is None: ## If no return function specified, return raw output self.return_func_dict[args[0].o][pyargs] = lambda x: x else: ## If function specified, use that self.return_func_dict[args[0].o][pyargs] = py_return ## Run the function ## if len(args) > 1: # Get the right function based on argument types key = tuple(map(type, args[1:])) # Convert any wrapped java items to their java objects args = list(args) args[1:] = map(lambda x: (x.o if isinstance(x,TASSELpy.javaObj.javaObj) else x), args[1:]) return_val = self.func_dict[args[0].o][key](*args) return self.return_func_dict[args[0].o][key](return_val) else: return_val = self.func_dict[args[0].o][()](*args) return self.return_func_dict[args[0].o][()](return_val) return wrapped_f