pamda.pamda_curry
1import types, threading, ctypes 2from functools import update_wrapper 3import type_enforced 4 5 6class curry_obj: 7 def __init__( 8 self, 9 __fn__, 10 *__args__, 11 __flips__=[], 12 __fnExecute__=None, 13 __isThunk__=False, 14 __isTypeEnforced__=False, 15 **__kwargs__, 16 ): 17 update_wrapper(self, __fn__) 18 self.__fn__ = __fn__ 19 self.__fnExecute__ = ( 20 __fnExecute__ if __fnExecute__ is not None else __fn__ 21 ) 22 self.__args__ = __args__ 23 self.__kwargs__ = __kwargs__ 24 self.__isCurried__ = True 25 self.__isThunk__ = __isThunk__ 26 self.__isTypeEnforced__ = __isTypeEnforced__ 27 self.__flips__ = __flips__ 28 self.__fnArity__ = self.__getFnArity__() 29 self.__arity__ = self.__getArity__(__args__, __kwargs__) 30 self.__thread__ = None 31 self.__thread_results__ = None 32 self.__get_fn_arg_default_keys__() 33 34 def __call__(self, *args, **kwargs): 35 new_args = self.__args__ + args 36 new_kwargs = dict(**self.__kwargs__, **kwargs) 37 # Create a comprehensive set of assigned variable names to determine arity 38 assigned_vars = set( 39 self.__fn_arg_default_keys__ 40 + self.__fn_arg_keys__[: len(new_args)] 41 + list(new_kwargs.keys()) 42 ) 43 arity = self.__fnArity__ - len(assigned_vars) 44 if arity < 0: 45 self.__exception__("Too many arguments were supplied") 46 elif arity == 0: 47 if len(self.__flips__) > 0: 48 new_args = self.__unflipArgs__(new_args) 49 if (not self.__isThunk__) or (len(args) + len(kwargs) == 0): 50 results = self.__fnExecute__(*new_args, **new_kwargs) 51 if self.__thread__ != None: 52 self.__thread_results__ = results 53 return results 54 return curry_obj( 55 self.__fn__, 56 *new_args, 57 __flips__=self.__flips__, 58 __isThunk__=self.__isThunk__, 59 __isTypeEnforced__=self.__isTypeEnforced__, 60 __fnExecute__=self.__fnExecute__, 61 **new_kwargs, 62 ) 63 64 def __get__(self, instance, owner): 65 def bind(*args, **kwargs): 66 if instance is not None and self.__arity__ == self.__fnArity__: 67 return self.__call__(instance, *args, **kwargs) 68 else: 69 return self.__call__(*args, **kwargs) 70 71 update_wrapper(bind, self) 72 return bind 73 74 def __repr__(self): 75 return f"<curried {self.__fn__.__module__}.{self.__fn__.__qualname__} object at {hex(id(self))}>" 76 77 def __getArity__(self, args, kwargs): 78 return self.__fnArity__ - (len(args) + len(kwargs)) 79 80 def __getFnArity__(self): 81 if not isinstance(self.__fn__, (types.MethodType, types.FunctionType)): 82 self.__exception__( 83 "A non function was passed as a function and does not have any arity. See the stack trace above for more information." 84 ) 85 extra_method_input_count = ( 86 1 if isinstance(self.__fn__, (types.MethodType)) else 0 87 ) 88 return self.__fn__.__code__.co_argcount - extra_method_input_count 89 90 def __get_fn_arg_default_keys__(self): 91 """ 92 Get the default values of the passed function or method and store them in `self.__fn_defaults__`. 93 """ 94 self.__fn_var_keys__ = list(self.__fn__.__code__.co_varnames) 95 self.__fn_arg_keys__ = self.__fn_var_keys__[ 96 : self.__fn__.__code__.co_argcount 97 ] 98 if self.__fn__.__defaults__ is not None: 99 self.__fn_arg_default_keys__ = self.__fn_arg_keys__[ 100 -len(self.__fn__.__defaults__) : 101 ] 102 else: 103 self.__fn_arg_default_keys__ = [] 104 if self.__fn__.__kwdefaults__ is not None: 105 self.__fn_arg_default_keys__.extend( 106 list(self.__fn__.__kwdefaults__.keys()) 107 ) 108 109 def thunkify(self): 110 self.__isThunk__ = True 111 return self 112 113 def flip(self): 114 if self.__arity__ < 2: 115 self.__exception__( 116 "To `flip` a function, it must have an arity of at least 2 (take two or more inputs)" 117 ) 118 self.__flips__ = [len(self.__args__)] + self.__flips__ 119 return self 120 121 def __unflipArgs__(self, args): 122 args = list(args) 123 for flip in self.__flips__: 124 arg = args.pop(flip + 1) 125 args.insert(flip, arg) 126 return tuple(args) 127 128 def __exception__(self, message): 129 pre_message = ( 130 f"({self.__fn__.__module__}.{self.__fn__.__qualname__}_curried): " 131 ) 132 raise Exception(pre_message + message) 133 134 def typeEnforce(self): 135 if not self.__isTypeEnforced__: 136 self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__) 137 self.__isTypeEnforced__ = True 138 return self 139 140 def asyncRun(self, daemon=False): 141 if not self.__isThunk__ and self.__arity__ == 0: 142 self.__exception__( 143 f"To `asyncRun` a Function, it must be a thunk with arity 0" 144 ) 145 if self.__thread__ is not None: 146 self.__exception__( 147 "`asyncRun` has already been executed on this thunk" 148 ) 149 self.__thread_completed__ = False 150 self.__thread__ = threading.Thread(target=self) 151 self.__thread__.setDaemon(daemon) 152 self.__thread__.start() 153 return self 154 155 def asyncWait(self): 156 if self.__thread__ == None: 157 self.__exception__( 158 f"To `asyncWait` a Function, it must be `asyncRun` first" 159 ) 160 if not self.__thread_completed__: 161 self.__thread__.join() 162 self.__thread_completed__ = True 163 return self.__thread_results__ 164 165 def asyncKill(self): 166 if self.__thread__ == None: 167 self.__exception__( 168 f"To `asyncKill` a Function, it must be `asyncRun` first" 169 ) 170 if self.__thread_completed__: 171 return self.__thread_results__ 172 173 thread_id = self.__thread__.ident 174 if thread_id is None: 175 self.__exception__( 176 f"Cannot `asyncKill` a Function that does not have a thread id" 177 ) 178 179 # Use ctypes to set the async exception 180 try: 181 res = ctypes.pythonapi.PyThreadState_SetAsyncExc( 182 ctypes.c_long(thread_id), ctypes.py_object(SystemExit) 183 ) 184 except: 185 self.__exception__( 186 f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})" 187 ) 188 if res == 0: 189 self.__exception__( 190 f"Failed to `asyncKill`: Thread id not found ({thread_id})" 191 ) 192 elif res == 1: 193 # Success, thread killed join the thread to clean up resources 194 self.__thread_completed__ = True 195 self.__thread__.join() 196 return self.__thread_results__ 197 elif res > 1: 198 # Something is wrong, set it back to 0 199 ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) 200 self.__exception__( 201 f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal." 202 )
class
curry_obj:
7class curry_obj: 8 def __init__( 9 self, 10 __fn__, 11 *__args__, 12 __flips__=[], 13 __fnExecute__=None, 14 __isThunk__=False, 15 __isTypeEnforced__=False, 16 **__kwargs__, 17 ): 18 update_wrapper(self, __fn__) 19 self.__fn__ = __fn__ 20 self.__fnExecute__ = ( 21 __fnExecute__ if __fnExecute__ is not None else __fn__ 22 ) 23 self.__args__ = __args__ 24 self.__kwargs__ = __kwargs__ 25 self.__isCurried__ = True 26 self.__isThunk__ = __isThunk__ 27 self.__isTypeEnforced__ = __isTypeEnforced__ 28 self.__flips__ = __flips__ 29 self.__fnArity__ = self.__getFnArity__() 30 self.__arity__ = self.__getArity__(__args__, __kwargs__) 31 self.__thread__ = None 32 self.__thread_results__ = None 33 self.__get_fn_arg_default_keys__() 34 35 def __call__(self, *args, **kwargs): 36 new_args = self.__args__ + args 37 new_kwargs = dict(**self.__kwargs__, **kwargs) 38 # Create a comprehensive set of assigned variable names to determine arity 39 assigned_vars = set( 40 self.__fn_arg_default_keys__ 41 + self.__fn_arg_keys__[: len(new_args)] 42 + list(new_kwargs.keys()) 43 ) 44 arity = self.__fnArity__ - len(assigned_vars) 45 if arity < 0: 46 self.__exception__("Too many arguments were supplied") 47 elif arity == 0: 48 if len(self.__flips__) > 0: 49 new_args = self.__unflipArgs__(new_args) 50 if (not self.__isThunk__) or (len(args) + len(kwargs) == 0): 51 results = self.__fnExecute__(*new_args, **new_kwargs) 52 if self.__thread__ != None: 53 self.__thread_results__ = results 54 return results 55 return curry_obj( 56 self.__fn__, 57 *new_args, 58 __flips__=self.__flips__, 59 __isThunk__=self.__isThunk__, 60 __isTypeEnforced__=self.__isTypeEnforced__, 61 __fnExecute__=self.__fnExecute__, 62 **new_kwargs, 63 ) 64 65 def __get__(self, instance, owner): 66 def bind(*args, **kwargs): 67 if instance is not None and self.__arity__ == self.__fnArity__: 68 return self.__call__(instance, *args, **kwargs) 69 else: 70 return self.__call__(*args, **kwargs) 71 72 update_wrapper(bind, self) 73 return bind 74 75 def __repr__(self): 76 return f"<curried {self.__fn__.__module__}.{self.__fn__.__qualname__} object at {hex(id(self))}>" 77 78 def __getArity__(self, args, kwargs): 79 return self.__fnArity__ - (len(args) + len(kwargs)) 80 81 def __getFnArity__(self): 82 if not isinstance(self.__fn__, (types.MethodType, types.FunctionType)): 83 self.__exception__( 84 "A non function was passed as a function and does not have any arity. See the stack trace above for more information." 85 ) 86 extra_method_input_count = ( 87 1 if isinstance(self.__fn__, (types.MethodType)) else 0 88 ) 89 return self.__fn__.__code__.co_argcount - extra_method_input_count 90 91 def __get_fn_arg_default_keys__(self): 92 """ 93 Get the default values of the passed function or method and store them in `self.__fn_defaults__`. 94 """ 95 self.__fn_var_keys__ = list(self.__fn__.__code__.co_varnames) 96 self.__fn_arg_keys__ = self.__fn_var_keys__[ 97 : self.__fn__.__code__.co_argcount 98 ] 99 if self.__fn__.__defaults__ is not None: 100 self.__fn_arg_default_keys__ = self.__fn_arg_keys__[ 101 -len(self.__fn__.__defaults__) : 102 ] 103 else: 104 self.__fn_arg_default_keys__ = [] 105 if self.__fn__.__kwdefaults__ is not None: 106 self.__fn_arg_default_keys__.extend( 107 list(self.__fn__.__kwdefaults__.keys()) 108 ) 109 110 def thunkify(self): 111 self.__isThunk__ = True 112 return self 113 114 def flip(self): 115 if self.__arity__ < 2: 116 self.__exception__( 117 "To `flip` a function, it must have an arity of at least 2 (take two or more inputs)" 118 ) 119 self.__flips__ = [len(self.__args__)] + self.__flips__ 120 return self 121 122 def __unflipArgs__(self, args): 123 args = list(args) 124 for flip in self.__flips__: 125 arg = args.pop(flip + 1) 126 args.insert(flip, arg) 127 return tuple(args) 128 129 def __exception__(self, message): 130 pre_message = ( 131 f"({self.__fn__.__module__}.{self.__fn__.__qualname__}_curried): " 132 ) 133 raise Exception(pre_message + message) 134 135 def typeEnforce(self): 136 if not self.__isTypeEnforced__: 137 self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__) 138 self.__isTypeEnforced__ = True 139 return self 140 141 def asyncRun(self, daemon=False): 142 if not self.__isThunk__ and self.__arity__ == 0: 143 self.__exception__( 144 f"To `asyncRun` a Function, it must be a thunk with arity 0" 145 ) 146 if self.__thread__ is not None: 147 self.__exception__( 148 "`asyncRun` has already been executed on this thunk" 149 ) 150 self.__thread_completed__ = False 151 self.__thread__ = threading.Thread(target=self) 152 self.__thread__.setDaemon(daemon) 153 self.__thread__.start() 154 return self 155 156 def asyncWait(self): 157 if self.__thread__ == None: 158 self.__exception__( 159 f"To `asyncWait` a Function, it must be `asyncRun` first" 160 ) 161 if not self.__thread_completed__: 162 self.__thread__.join() 163 self.__thread_completed__ = True 164 return self.__thread_results__ 165 166 def asyncKill(self): 167 if self.__thread__ == None: 168 self.__exception__( 169 f"To `asyncKill` a Function, it must be `asyncRun` first" 170 ) 171 if self.__thread_completed__: 172 return self.__thread_results__ 173 174 thread_id = self.__thread__.ident 175 if thread_id is None: 176 self.__exception__( 177 f"Cannot `asyncKill` a Function that does not have a thread id" 178 ) 179 180 # Use ctypes to set the async exception 181 try: 182 res = ctypes.pythonapi.PyThreadState_SetAsyncExc( 183 ctypes.c_long(thread_id), ctypes.py_object(SystemExit) 184 ) 185 except: 186 self.__exception__( 187 f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})" 188 ) 189 if res == 0: 190 self.__exception__( 191 f"Failed to `asyncKill`: Thread id not found ({thread_id})" 192 ) 193 elif res == 1: 194 # Success, thread killed join the thread to clean up resources 195 self.__thread_completed__ = True 196 self.__thread__.join() 197 return self.__thread_results__ 198 elif res > 1: 199 # Something is wrong, set it back to 0 200 ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) 201 self.__exception__( 202 f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal." 203 )
curry_obj( __fn__, *__args__, __flips__=[], __fnExecute__=None, __isThunk__=False, __isTypeEnforced__=False, **__kwargs__)
8 def __init__( 9 self, 10 __fn__, 11 *__args__, 12 __flips__=[], 13 __fnExecute__=None, 14 __isThunk__=False, 15 __isTypeEnforced__=False, 16 **__kwargs__, 17 ): 18 update_wrapper(self, __fn__) 19 self.__fn__ = __fn__ 20 self.__fnExecute__ = ( 21 __fnExecute__ if __fnExecute__ is not None else __fn__ 22 ) 23 self.__args__ = __args__ 24 self.__kwargs__ = __kwargs__ 25 self.__isCurried__ = True 26 self.__isThunk__ = __isThunk__ 27 self.__isTypeEnforced__ = __isTypeEnforced__ 28 self.__flips__ = __flips__ 29 self.__fnArity__ = self.__getFnArity__() 30 self.__arity__ = self.__getArity__(__args__, __kwargs__) 31 self.__thread__ = None 32 self.__thread_results__ = None 33 self.__get_fn_arg_default_keys__()
def
asyncRun(self, daemon=False):
141 def asyncRun(self, daemon=False): 142 if not self.__isThunk__ and self.__arity__ == 0: 143 self.__exception__( 144 f"To `asyncRun` a Function, it must be a thunk with arity 0" 145 ) 146 if self.__thread__ is not None: 147 self.__exception__( 148 "`asyncRun` has already been executed on this thunk" 149 ) 150 self.__thread_completed__ = False 151 self.__thread__ = threading.Thread(target=self) 152 self.__thread__.setDaemon(daemon) 153 self.__thread__.start() 154 return self
def
asyncKill(self):
166 def asyncKill(self): 167 if self.__thread__ == None: 168 self.__exception__( 169 f"To `asyncKill` a Function, it must be `asyncRun` first" 170 ) 171 if self.__thread_completed__: 172 return self.__thread_results__ 173 174 thread_id = self.__thread__.ident 175 if thread_id is None: 176 self.__exception__( 177 f"Cannot `asyncKill` a Function that does not have a thread id" 178 ) 179 180 # Use ctypes to set the async exception 181 try: 182 res = ctypes.pythonapi.PyThreadState_SetAsyncExc( 183 ctypes.c_long(thread_id), ctypes.py_object(SystemExit) 184 ) 185 except: 186 self.__exception__( 187 f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})" 188 ) 189 if res == 0: 190 self.__exception__( 191 f"Failed to `asyncKill`: Thread id not found ({thread_id})" 192 ) 193 elif res == 1: 194 # Success, thread killed join the thread to clean up resources 195 self.__thread_completed__ = True 196 self.__thread__.join() 197 return self.__thread_results__ 198 elif res > 1: 199 # Something is wrong, set it back to 0 200 ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0) 201 self.__exception__( 202 f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal." 203 )