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 thunkify(self):
110    def thunkify(self):
111        self.__isThunk__ = True
112        return self
def flip(self):
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
def typeEnforce(self):
135    def typeEnforce(self):
136        if not self.__isTypeEnforced__:
137            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
138            self.__isTypeEnforced__ = True
139        return self
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 asyncWait(self):
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__
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            )