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        return bind
 72
 73    def __repr__(self):
 74        return f"<curried {self.__fn__.__module__}.{self.__fn__.__qualname__} object at {hex(id(self))}>"
 75
 76    def __getArity__(self, args, kwargs):
 77        return self.__fnArity__ - (len(args) + len(kwargs))
 78
 79    def __getFnArity__(self):
 80        if not isinstance(self.__fn__, (types.MethodType, types.FunctionType)):
 81            self.__exception__(
 82                "A non function was passed as a function and does not have any arity. See the stack trace above for more information."
 83            )
 84        extra_method_input_count = (
 85            1 if isinstance(self.__fn__, (types.MethodType)) else 0
 86        )
 87        return self.__fn__.__code__.co_argcount - extra_method_input_count
 88
 89    def __get_fn_arg_default_keys__(self):
 90        """
 91        Get the default values of the passed function or method and store them in `self.__fn_defaults__`.
 92        """
 93        self.__fn_var_keys__ = list(self.__fn__.__code__.co_varnames)
 94        self.__fn_arg_keys__ = self.__fn_var_keys__[
 95            : self.__fn__.__code__.co_argcount
 96        ]
 97        if self.__fn__.__defaults__ is not None:
 98            self.__fn_arg_default_keys__ = self.__fn_arg_keys__[
 99                -len(self.__fn__.__defaults__) :
100            ]
101        else:
102            self.__fn_arg_default_keys__ = []
103        if self.__fn__.__kwdefaults__ is not None:
104            self.__fn_arg_default_keys__.extend(
105                list(self.__fn__.__kwdefaults__.keys())
106            )
107
108    def thunkify(self):
109        self.__isThunk__ = True
110        return self
111
112    def flip(self):
113        if self.__arity__ < 2:
114            self.__exception__(
115                "To `flip` a function, it  must have an arity of at least 2 (take two or more inputs)"
116            )
117        self.__flips__ = [len(self.__args__)] + self.__flips__
118        return self
119
120    def __unflipArgs__(self, args):
121        args = list(args)
122        for flip in self.__flips__:
123            arg = args.pop(flip + 1)
124            args.insert(flip, arg)
125        return tuple(args)
126
127    def __exception__(self, message):
128        pre_message = (
129            f"({self.__fn__.__module__}.{self.__fn__.__qualname__}_curried): "
130        )
131        raise Exception(pre_message + message)
132
133    def typeEnforce(self):
134        if not self.__isTypeEnforced__:
135            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
136            self.__isTypeEnforced__ = True
137        return self
138
139    def asyncRun(self, daemon=False):
140        if not self.__isThunk__ and self.__arity__ == 0:
141            self.__exception__(
142                f"To `asyncRun` a Function, it must be a thunk with arity 0"
143            )
144        if self.__thread__ is not None:
145            self.__exception__(
146                "`asyncRun` has already been executed on this thunk"
147            )
148        self.__thread_completed__ = False
149        self.__thread__ = threading.Thread(target=self)
150        self.__thread__.setDaemon(daemon)
151        self.__thread__.start()
152        return self
153
154    def asyncWait(self):
155        if self.__thread__ == None:
156            self.__exception__(
157                f"To `asyncWait` a Function, it must be `asyncRun` first"
158            )
159        if not self.__thread_completed__:
160            self.__thread__.join()
161            self.__thread_completed__ = True
162        return self.__thread_results__
163
164    def asyncKill(self):
165        if self.__thread__ == None:
166            self.__exception__(
167                f"To `asyncKill` a Function, it must be `asyncRun` first"
168            )
169        if self.__thread_completed__:
170            return self.__thread_results__
171
172        thread_id = self.__thread__.ident
173        if thread_id is None:
174            self.__exception__(
175                f"Cannot `asyncKill` a Function that does not have a thread id"
176            )
177
178        # Use ctypes to set the async exception
179        try:
180            res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
181                ctypes.c_long(thread_id), ctypes.py_object(SystemExit)
182            )
183        except:
184            self.__exception__(
185                f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})"
186            )
187        if res == 0:
188            self.__exception__(
189                f"Failed to `asyncKill`: Thread id not found ({thread_id})"
190            )
191        elif res == 1:
192            # Success, thread killed join the thread to clean up resources
193            self.__thread_completed__ = True
194            self.__thread__.join()
195            return self.__thread_results__
196        elif res > 1:
197            # Something is wrong, set it back to 0
198            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
199            self.__exception__(
200                f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal."
201            )
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        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            )
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):
109    def thunkify(self):
110        self.__isThunk__ = True
111        return self
def flip(self):
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
def typeEnforce(self):
134    def typeEnforce(self):
135        if not self.__isTypeEnforced__:
136            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
137            self.__isTypeEnforced__ = True
138        return self
def asyncRun(self, daemon=False):
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
def asyncWait(self):
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__
def asyncKill(self):
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            )