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
 33    def __call__(self, *args, **kwargs):
 34        new_args = self.__args__ + args
 35        new_kwargs = dict(**self.__kwargs__, **kwargs)
 36        self.__arity__ = self.__getArity__(new_args, new_kwargs)
 37        if self.__arity__ < 0:
 38            self.__exception__("Too many arguments were supplied")
 39        if self.__arity__ == 0:
 40            if len(self.__flips__) > 0:
 41                new_args = self.__unflipArgs__(new_args)
 42            if (not self.__isThunk__) or (len(args) + len(kwargs) == 0):
 43                results = self.__fnExecute__(*new_args, **new_kwargs)
 44                if self.__thread__ != None:
 45                    self.__thread_results__ = results
 46                return results
 47        return curry_obj(
 48            self.__fn__,
 49            *new_args,
 50            __flips__=self.__flips__,
 51            __isThunk__=self.__isThunk__,
 52            __isTypeEnforced__=self.__isTypeEnforced__,
 53            __fnExecute__=self.__fnExecute__,
 54            **new_kwargs,
 55        )
 56
 57    def __get__(self, instance, owner):
 58        def bind(*args, **kwargs):
 59            if instance is not None and self.__arity__ == self.__fnArity__:
 60                return self.__call__(instance, *args, **kwargs)
 61            else:
 62                return self.__call__(*args, **kwargs)
 63
 64        return bind
 65
 66    def __repr__(self):
 67        return f"<curried {self.__fn__.__module__}.{self.__fn__.__qualname__} object at {hex(id(self))}>"
 68
 69    def __getArity__(self, args, kwargs):
 70        return self.__fnArity__ - (len(args) + len(kwargs))
 71
 72    def __getFnArity__(self):
 73        if not isinstance(self.__fn__, (types.MethodType, types.FunctionType)):
 74            self.__exception__(
 75                "A non function was passed as a function and does not have any arity. See the stack trace above for more information."
 76            )
 77        extra_method_input_count = (
 78            1 if isinstance(self.__fn__, (types.MethodType)) else 0
 79        )
 80        return self.__fn__.__code__.co_argcount - extra_method_input_count
 81
 82    def thunkify(self):
 83        self.__isThunk__ = True
 84        return self
 85
 86    def flip(self):
 87        if self.__arity__ < 2:
 88            self.__exception__(
 89                "To `flip` a function, it  must have an arity of at least 2 (take two or more inputs)"
 90            )
 91        self.__flips__ = [len(self.__args__)] + self.__flips__
 92        return self
 93
 94    def __unflipArgs__(self, args):
 95        args = list(args)
 96        for flip in self.__flips__:
 97            arg = args.pop(flip + 1)
 98            args.insert(flip, arg)
 99        return tuple(args)
100
101    def __exception__(self, message):
102        pre_message = (
103            f"({self.__fn__.__module__}.{self.__fn__.__qualname__}_curried): "
104        )
105        raise Exception(pre_message + message)
106
107    def typeEnforce(self):
108        if not self.__isTypeEnforced__:
109            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
110            self.__isTypeEnforced__ = True
111        return self
112
113    def asyncRun(self, daemon=False):
114        if not self.__isThunk__ and self.__arity__ == 0:
115            self.__exception__(
116                f"To `asyncRun` a Function, it must be a thunk with arity 0"
117            )
118        if self.__thread__ is not None:
119            self.__exception__(
120                "`asyncRun` has already been executed on this thunk"
121            )
122        self.__thread_completed__ = False
123        self.__thread__ = threading.Thread(target=self)
124        self.__thread__.setDaemon(daemon)
125        self.__thread__.start()
126        return self
127
128    def asyncWait(self):
129        if self.__thread__ == None:
130            self.__exception__(
131                f"To `asyncWait` a Function, it must be `asyncRun` first"
132            )
133        if not self.__thread_completed__:
134            self.__thread__.join()
135            self.__thread_completed__ = True
136        return self.__thread_results__
137
138    def asyncKill(self):
139        if self.__thread__ == None:
140            self.__exception__(
141                f"To `asyncKill` a Function, it must be `asyncRun` first"
142            )
143        if self.__thread_completed__:
144            return self.__thread_results__
145
146        thread_id = self.__thread__.ident
147        if thread_id is None:
148            self.__exception__(
149                f"Cannot `asyncKill` a Function that does not have a thread id"
150            )
151
152        # Use ctypes to set the async exception
153        try:
154            res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
155                ctypes.c_long(thread_id), ctypes.py_object(SystemExit)
156            )
157        except:
158            self.__exception__(
159                f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})"
160            )
161        if res == 0:
162            self.__exception__(
163                f"Failed to `asyncKill`: Thread id not found ({thread_id})"
164            )
165        elif res == 1:
166            # Success, thread killed join the thread to clean up resources
167            self.__thread_completed__ = True
168            self.__thread__.join()
169            return self.__thread_results__
170        elif res > 1:
171            # Something is wrong, set it back to 0
172            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
173            self.__exception__(
174                f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal."
175            )
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
 34    def __call__(self, *args, **kwargs):
 35        new_args = self.__args__ + args
 36        new_kwargs = dict(**self.__kwargs__, **kwargs)
 37        self.__arity__ = self.__getArity__(new_args, new_kwargs)
 38        if self.__arity__ < 0:
 39            self.__exception__("Too many arguments were supplied")
 40        if self.__arity__ == 0:
 41            if len(self.__flips__) > 0:
 42                new_args = self.__unflipArgs__(new_args)
 43            if (not self.__isThunk__) or (len(args) + len(kwargs) == 0):
 44                results = self.__fnExecute__(*new_args, **new_kwargs)
 45                if self.__thread__ != None:
 46                    self.__thread_results__ = results
 47                return results
 48        return curry_obj(
 49            self.__fn__,
 50            *new_args,
 51            __flips__=self.__flips__,
 52            __isThunk__=self.__isThunk__,
 53            __isTypeEnforced__=self.__isTypeEnforced__,
 54            __fnExecute__=self.__fnExecute__,
 55            **new_kwargs,
 56        )
 57
 58    def __get__(self, instance, owner):
 59        def bind(*args, **kwargs):
 60            if instance is not None and self.__arity__ == self.__fnArity__:
 61                return self.__call__(instance, *args, **kwargs)
 62            else:
 63                return self.__call__(*args, **kwargs)
 64
 65        return bind
 66
 67    def __repr__(self):
 68        return f"<curried {self.__fn__.__module__}.{self.__fn__.__qualname__} object at {hex(id(self))}>"
 69
 70    def __getArity__(self, args, kwargs):
 71        return self.__fnArity__ - (len(args) + len(kwargs))
 72
 73    def __getFnArity__(self):
 74        if not isinstance(self.__fn__, (types.MethodType, types.FunctionType)):
 75            self.__exception__(
 76                "A non function was passed as a function and does not have any arity. See the stack trace above for more information."
 77            )
 78        extra_method_input_count = (
 79            1 if isinstance(self.__fn__, (types.MethodType)) else 0
 80        )
 81        return self.__fn__.__code__.co_argcount - extra_method_input_count
 82
 83    def thunkify(self):
 84        self.__isThunk__ = True
 85        return self
 86
 87    def flip(self):
 88        if self.__arity__ < 2:
 89            self.__exception__(
 90                "To `flip` a function, it  must have an arity of at least 2 (take two or more inputs)"
 91            )
 92        self.__flips__ = [len(self.__args__)] + self.__flips__
 93        return self
 94
 95    def __unflipArgs__(self, args):
 96        args = list(args)
 97        for flip in self.__flips__:
 98            arg = args.pop(flip + 1)
 99            args.insert(flip, arg)
100        return tuple(args)
101
102    def __exception__(self, message):
103        pre_message = (
104            f"({self.__fn__.__module__}.{self.__fn__.__qualname__}_curried): "
105        )
106        raise Exception(pre_message + message)
107
108    def typeEnforce(self):
109        if not self.__isTypeEnforced__:
110            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
111            self.__isTypeEnforced__ = True
112        return self
113
114    def asyncRun(self, daemon=False):
115        if not self.__isThunk__ and self.__arity__ == 0:
116            self.__exception__(
117                f"To `asyncRun` a Function, it must be a thunk with arity 0"
118            )
119        if self.__thread__ is not None:
120            self.__exception__(
121                "`asyncRun` has already been executed on this thunk"
122            )
123        self.__thread_completed__ = False
124        self.__thread__ = threading.Thread(target=self)
125        self.__thread__.setDaemon(daemon)
126        self.__thread__.start()
127        return self
128
129    def asyncWait(self):
130        if self.__thread__ == None:
131            self.__exception__(
132                f"To `asyncWait` a Function, it must be `asyncRun` first"
133            )
134        if not self.__thread_completed__:
135            self.__thread__.join()
136            self.__thread_completed__ = True
137        return self.__thread_results__
138
139    def asyncKill(self):
140        if self.__thread__ == None:
141            self.__exception__(
142                f"To `asyncKill` a Function, it must be `asyncRun` first"
143            )
144        if self.__thread_completed__:
145            return self.__thread_results__
146
147        thread_id = self.__thread__.ident
148        if thread_id is None:
149            self.__exception__(
150                f"Cannot `asyncKill` a Function that does not have a thread id"
151            )
152
153        # Use ctypes to set the async exception
154        try:
155            res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
156                ctypes.c_long(thread_id), ctypes.py_object(SystemExit)
157            )
158        except:
159            self.__exception__(
160                f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})"
161            )
162        if res == 0:
163            self.__exception__(
164                f"Failed to `asyncKill`: Thread id not found ({thread_id})"
165            )
166        elif res == 1:
167            # Success, thread killed join the thread to clean up resources
168            self.__thread_completed__ = True
169            self.__thread__.join()
170            return self.__thread_results__
171        elif res > 1:
172            # Something is wrong, set it back to 0
173            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
174            self.__exception__(
175                f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal."
176            )
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
def thunkify(self):
83    def thunkify(self):
84        self.__isThunk__ = True
85        return self
def flip(self):
87    def flip(self):
88        if self.__arity__ < 2:
89            self.__exception__(
90                "To `flip` a function, it  must have an arity of at least 2 (take two or more inputs)"
91            )
92        self.__flips__ = [len(self.__args__)] + self.__flips__
93        return self
def typeEnforce(self):
108    def typeEnforce(self):
109        if not self.__isTypeEnforced__:
110            self.__fnExecute__ = type_enforced.Enforcer(self.__fnExecute__)
111            self.__isTypeEnforced__ = True
112        return self
def asyncRun(self, daemon=False):
114    def asyncRun(self, daemon=False):
115        if not self.__isThunk__ and self.__arity__ == 0:
116            self.__exception__(
117                f"To `asyncRun` a Function, it must be a thunk with arity 0"
118            )
119        if self.__thread__ is not None:
120            self.__exception__(
121                "`asyncRun` has already been executed on this thunk"
122            )
123        self.__thread_completed__ = False
124        self.__thread__ = threading.Thread(target=self)
125        self.__thread__.setDaemon(daemon)
126        self.__thread__.start()
127        return self
def asyncWait(self):
129    def asyncWait(self):
130        if self.__thread__ == None:
131            self.__exception__(
132                f"To `asyncWait` a Function, it must be `asyncRun` first"
133            )
134        if not self.__thread_completed__:
135            self.__thread__.join()
136            self.__thread_completed__ = True
137        return self.__thread_results__
def asyncKill(self):
139    def asyncKill(self):
140        if self.__thread__ == None:
141            self.__exception__(
142                f"To `asyncKill` a Function, it must be `asyncRun` first"
143            )
144        if self.__thread_completed__:
145            return self.__thread_results__
146
147        thread_id = self.__thread__.ident
148        if thread_id is None:
149            self.__exception__(
150                f"Cannot `asyncKill` a Function that does not have a thread id"
151            )
152
153        # Use ctypes to set the async exception
154        try:
155            res = ctypes.pythonapi.PyThreadState_SetAsyncExc(
156                ctypes.c_long(thread_id), ctypes.py_object(SystemExit)
157            )
158        except:
159            self.__exception__(
160                f"Failed to `asyncKill`: Something failed when seting the Exit state for thread id ({thread_id})"
161            )
162        if res == 0:
163            self.__exception__(
164                f"Failed to `asyncKill`: Thread id not found ({thread_id})"
165            )
166        elif res == 1:
167            # Success, thread killed join the thread to clean up resources
168            self.__thread_completed__ = True
169            self.__thread__.join()
170            return self.__thread_results__
171        elif res > 1:
172            # Something is wrong, set it back to 0
173            ctypes.pythonapi.PyThreadState_SetAsyncExc(thread_id, 0)
174            self.__exception__(
175                f"Failed to `asyncKill`: ctypes returned multiple results for thread id ({thread_id}). This should not happen. Returned thread state back to normal."
176            )