def wrap_function(cache_store, func_): attr_name = "_cache_for_" + func_.__name__ func_args = get_function_arguments(func_) if len(func_args) > 0 and func_args[0] == "self": using_self = True func = lambda self, *args: func_(self, *args) else: using_self = False func = lambda self, *args: func_(*args) def output(*args, **kwargs): if kwargs: Log.error("Sorry, caching only works with ordered parameter, not keyword arguments") with cache_store.locker: if using_self: self = args[0] args = args[1:] else: self = cache_store now = Date.now() try: _cache = getattr(self, attr_name) except Exception: _cache = {} setattr(self, attr_name, _cache) if Random.int(100) == 0: # REMOVE OLD CACHE _cache = {k: v for k, v in _cache.items() if v.timeout == None or v.timeout > now} setattr(self, attr_name, _cache) timeout, key, value, exception = _cache.get(args, (Null, Null, Null, Null)) if now >= timeout: value = func(self, *args) with cache_store.locker: _cache[args] = CacheElement(now + cache_store.timeout, args, value, None) return value if value == None: if exception == None: try: value = func(self, *args) with cache_store.locker: _cache[args] = CacheElement(now + cache_store.timeout, args, value, None) return value except Exception as e: e = Except.wrap(e) with cache_store.locker: _cache[args] = CacheElement(now + cache_store.timeout, args, None, e) raise e else: raise exception else: return value return output
def __call__(self, *args, **kwargs): settings = wrap(kwargs).settings params = get_function_arguments(self.constructor)[1:] func_defaults = get_function_defaults(self.constructor) if not func_defaults: defaults = {} else: defaults = {k: v for k, v in zip(reversed(params), reversed(func_defaults))} ordered_params = dict(zip(params, args)) output = self.class_(**params_pack(params, ordered_params, kwargs, settings, defaults)) return DataObject(output)
def __call__(self, *args, **kwargs): settings = wrap(kwargs).settings params = get_function_arguments(self.constructor)[1:] func_defaults = get_function_defaults(self.constructor) if not func_defaults: defaults = {} else: defaults = {k: v for k, v in zip(reversed(params), reversed(func_defaults))} ordered_params = dict(zip(params, args)) output = self.class_(**params_pack(params, ordered_params, kwargs, settings, defaults)) return DataObject(output)
def w_settings(*args, **kwargs): settings = kwargs.get("kwargs") params = get_function_arguments(func) if not get_function_defaults(func): defaults = {} else: defaults = { k: v for k, v in zip(reversed(params), reversed(get_function_defaults(func))) } ordered_params = dict(zip(params, args)) return func(**params_pack(params, ordered_params, kwargs, settings, defaults))
def override(func): """ THIS DECORATOR WILL PUT ALL PARAMETERS INTO THE `kwargs` PARAMETER AND THEN PUT ALL `kwargs` PARAMETERS INTO THE FUNCTION PARAMETERS. THIS HAS THE BENEFIT OF HAVING ALL PARAMETERS IN ONE PLACE (kwargs), PLUS ALL PARAMETERS ARE EXPLICIT FOR CLARITY. OF COURSE, THIS MEANS PARAMETER ASSIGNMENT MAY NOT BE UNIQUE: VALUES CAN COME FROM EXPLICIT CALL PARAMETERS, OR FROM THE kwargs PARAMETER. IN THESE CASES, PARAMETER VALUES ARE CHOSEN IN THE FOLLOWING ORDER: 1) EXPLICT CALL PARAMETERS 2) PARAMETERS FOUND IN kwargs 3) DEFAULT VALUES ASSIGNED IN FUNCTION DEFINITION """ func_name = get_function_name(func) params = get_function_arguments(func) if not get_function_defaults(func): defaults = {} else: defaults = { k: v for k, v in zip(reversed(params), reversed(get_function_defaults(func))) } def raise_error(e, a, k): packed = set_default(dict(zip(params, a)), k) err = text(e) e = Except.wrap(e) if err.startswith(func_name) and ( "takes at least" in err or "takes exactly " in err or "required positional argument" in err ): missing = [p for p in params if str(p) not in packed] given = [p for p in params if str(p) in packed] if not missing: raise e else: get_logger().error( "Problem calling {{func_name}}: Expecting parameter {{missing}}, given {{given}}", func_name=func_name, missing=missing, given=given, stack_depth=2, cause=e, ) raise e if KWARGS not in params: # ADDING A kwargs PARAMETER TO SOME REGULAR METHOD def wo_kwargs(*args, **kwargs): settings = kwargs.get(KWARGS, {}) ordered_params = dict(zip(params, args)) a, k = params_pack(params, defaults, settings, kwargs, ordered_params) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(wo_kwargs, func) elif func_name in ("__init__", "__new__") or params[0] in ("self", "cls"): def w_bound_method(*args, **kwargs): if len(args) == 2 and len(kwargs) == 0 and is_data(args[1]): # ASSUME SECOND UNNAMED PARAM IS kwargs a, k = params_pack( params, defaults, args[1], {params[0]: args[0]}, kwargs ) elif KWARGS in kwargs and is_data(kwargs[KWARGS]): # PUT args INTO kwargs a, k = params_pack( params, defaults, kwargs[KWARGS], dict_zip(params, args), kwargs ) else: a, k = params_pack(params, defaults, dict_zip(params, args), kwargs) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(w_bound_method, func) else: def w_kwargs(*args, **kwargs): if len(args) == 1 and len(kwargs) == 0 and is_data(args[0]): # ASSUME SINGLE PARAMETER IS kwargs a, k = params_pack(params, defaults, args[0]) elif KWARGS in kwargs and is_data(kwargs[KWARGS]): # PUT args INTO kwargs a, k = params_pack( params, defaults, kwargs[KWARGS], dict_zip(params, args), kwargs ) else: # PULL kwargs OUT INTO PARAMS a, k = params_pack(params, defaults, dict_zip(params, args), kwargs) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(w_kwargs, func)
def override(func): """ THIS DECORATOR WILL PUT ALL PARAMETERS INTO THE `kwargs` PARAMETER AND THEN PUT ALL `kwargs` PARAMETERS INTO THE FUNCTION PARAMETERS. THIS HAS THE BENEFIT OF HAVING ALL PARAMETERS IN ONE PLACE (kwargs), PLUS ALL PARAMETERS ARE EXPLICIT FOR CLARITY. OF COURSE, THIS MEANS PARAMETER ASSIGNMENT MAY NOT BE UNIQUE: VALUES CAN COME FROM EXPLICIT CALL PARAMETERS, OR FROM THE kwargs PARAMETER. IN THESE CASES, PARAMETER VALUES ARE CHOSEN IN THE FOLLOWING ORDER: 1) EXPLICT CALL PARAMETERS 2) PARAMETERS FOUND IN kwargs 3) DEFAULT VALUES ASSIGNED IN FUNCTION DEFINITION """ func_name = get_function_name(func) params = get_function_arguments(func) if not get_function_defaults(func): defaults = {} else: defaults = {k: v for k, v in zip(reversed(params), reversed(get_function_defaults(func)))} def raise_error(e, packed): err = text_type(e) e = Except.wrap(e) if err.startswith(func_name) and ("takes at least" in err or "required positional argument" in err): missing = [p for p in params if str(p) not in packed] given = [p for p in params if str(p) in packed] if not missing: raise e else: get_logger().error( "Problem calling {{func_name}}: Expecting parameter {{missing}}, given {{given}}", func_name=func_name, missing=missing, given=given, stack_depth=2, cause=e ) raise e if "kwargs" not in params: # WE ASSUME WE ARE ONLY ADDING A kwargs PARAMETER TO SOME REGULAR METHOD def wo_kwargs(*args, **kwargs): settings = kwargs.get("kwargs") ordered_params = dict(zip(params, args)) packed = params_pack(params, ordered_params, kwargs, settings, defaults) try: return func(**packed) except TypeError as e: raise_error(e, packed) return wo_kwargs elif func_name in ("__init__", "__new__"): def w_constructor(*args, **kwargs): if "kwargs" in kwargs: packed = params_pack(params, dict_zip(params[1:], args[1:]), kwargs, kwargs["kwargs"], defaults) elif len(args) == 2 and len(kwargs) == 0 and is_data(args[1]): # ASSUME SECOND UNNAMED PARAM IS kwargs packed = params_pack(params, args[1], defaults) else: # DO NOT INCLUDE self IN kwargs packed = params_pack(params, dict_zip(params[1:], args[1:]), kwargs, defaults) try: return func(args[0], **packed) except TypeError as e: packed['self'] = args[0] # DO NOT SAY IS MISSING raise_error(e, packed) return w_constructor elif params[0] == "self": def w_bound_method(*args, **kwargs): if len(args) == 2 and len(kwargs) == 0 and is_data(args[1]): # ASSUME SECOND UNNAMED PARAM IS kwargs packed = params_pack(params, args[1], defaults) elif "kwargs" in kwargs and is_data(kwargs["kwargs"]): # PUT args INTO kwargs packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), kwargs["kwargs"], defaults) else: packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), defaults) try: return func(args[0], **packed) except TypeError as e: raise_error(e, packed) return w_bound_method else: def w_kwargs(*args, **kwargs): if len(args) == 1 and len(kwargs) == 0 and is_data(args[0]): # ASSUME SINGLE PARAMETER IS kwargs packed = params_pack(params, args[0], defaults) elif "kwargs" in kwargs and is_data(kwargs["kwargs"]): # PUT args INTO kwargs packed = params_pack(params, kwargs, dict_zip(params, args), kwargs["kwargs"], defaults) else: # PULL kwargs OUT INTO PARAMS packed = params_pack(params, kwargs, dict_zip(params, args), defaults) try: return func(**packed) except TypeError as e: raise_error(e, packed) return w_kwargs
def output(func): func_name = get_function_name(func) params = get_function_arguments(func) if not get_function_defaults(func): defaults = {} else: defaults = { k: v for k, v in zip(reversed(params), reversed(get_function_defaults(func))) } def raise_error(e, a, k): packed = set_default(dict(zip(params, a)), k) err = text(e) e = Except.wrap(e) if err.startswith(func_name) and ( "takes at least" in err or "takes exactly " in err or "required positional argument" in err): missing = [p for p in params if str(p) not in packed] given = [p for p in params if str(p) in packed] if not missing: raise e else: get_logger().error( "Problem calling {{func_name}}: Expecting parameter {{missing}}, given {{given}}", func_name=func_name, missing=missing, given=given, stack_depth=2, cause=e, ) raise e if kwargs not in params: # ADDING A kwargs PARAMETER TO SOME REGULAR METHOD def wo_kwargs(*given_args, **given_kwargs): settings = given_kwargs.get(kwargs, {}) ordered_params = dict(zip(params, given_args)) a, k = params_pack(params, defaults, settings, given_kwargs, ordered_params) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(wo_kwargs, func) elif func_name in ("__init__", "__new__") or params[0] in ("self", "cls"): def w_bound_method(*given_args, **given_kwargs): if len(given_args) == 2 and len(given_kwargs) == 0 and is_data( given_args[1]): # ASSUME SECOND UNNAMED PARAM IS kwargs a, k = params_pack(params, defaults, given_args[1], {params[0]: given_args[0]}, given_kwargs) elif kwargs in given_kwargs and is_data(given_kwargs[kwargs]): # PUT args INTO given_kwargs a, k = params_pack(params, defaults, given_kwargs[kwargs], dict_zip(params, given_args), given_kwargs) else: a, k = params_pack(params, defaults, dict_zip(params, given_args), given_kwargs) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(w_bound_method, func) else: def w_kwargs(*given_args, **given_kwargs): if len(given_args) == 1 and len(given_kwargs) == 0 and is_data( given_args[0]): # ASSUME SINGLE PARAMETER IS kwargs a, k = params_pack(params, defaults, given_args[0]) elif kwargs in given_kwargs and is_data(given_kwargs[kwargs]): # PUT given_args INTO given_kwargs a, k = params_pack(params, defaults, given_kwargs[kwargs], dict_zip(params, given_args), given_kwargs) else: # PULL kwargs OUT INTO PARAMS a, k = params_pack(params, defaults, dict_zip(params, given_args), given_kwargs) try: return func(*a, **k) except TypeError as e: raise_error(e, a, k) return update_wrapper(w_kwargs, func)
def override(func): """ THIS DECORATOR WILL PUT ALL PARAMETERS INTO THE `kwargs` PARAMETER AND THEN PUT ALL `kwargs` PARAMETERS INTO THE FUNCTION PARAMETERS. THIS HAS THE BENEFIT OF HAVING ALL PARAMETERS IN ONE PLACE (kwargs), PLUS ALL PARAMETERS ARE EXPLICIT FOR CLARITY. OF COURSE, THIS MEANS PARAMETER ASSIGNMENT MAY NOT BE UNIQUE: VALUES CAN COME FROM EXPLICIT CALL PARAMETERS, OR FROM THE kwargs PARAMETER. IN THESE CASES, PARAMETER VALUES ARE CHOSEN IN THE FOLLOWING ORDER: 1) EXPLICT CALL PARAMETERS 2) PARAMETERS FOUND IN kwargs 3) DEFAULT VALUES ASSIGNED IN FUNCTION DEFINITION """ func_name = get_function_name(func) params = get_function_arguments(func) if not get_function_defaults(func): defaults = {} else: defaults = { k: v for k, v in zip(reversed(params), reversed(get_function_defaults(func))) } def raise_error(e, packed): err = text_type(e) e = Except.wrap(e) if err.startswith(func_name) and ("takes at least" in err or "required positional argument" in err): missing = [p for p in params if str(p) not in packed] given = [p for p in params if str(p) in packed] get_logger().error( "Problem calling {{func_name}}: Expecting parameter {{missing}}, given {{given}}", func_name=func_name, missing=missing, given=given, stack_depth=2) get_logger().error("Error dispatching call", e) if "kwargs" not in params: # WE ASSUME WE ARE ONLY ADDING A kwargs PARAMETER TO SOME REGULAR METHOD def wo_kwargs(*args, **kwargs): settings = kwargs.get("kwargs") ordered_params = dict(zip(params, args)) packed = params_pack(params, ordered_params, kwargs, settings, defaults) try: return func(**packed) except TypeError as e: raise_error(e, packed) return wo_kwargs elif func_name in ("__init__", "__new__"): def w_constructor(*args, **kwargs): if "kwargs" in kwargs: packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), kwargs["kwargs"], defaults) elif len(args) == 2 and len(kwargs) == 0 and isinstance( args[1], Mapping): # ASSUME SECOND UNNAMED PARAM IS kwargs packed = params_pack(params, args[1], defaults) else: # DO NOT INCLUDE self IN kwargs packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), defaults) try: return func(args[0], **packed) except TypeError as e: raise_error(e, packed) return w_constructor elif params[0] == "self": def w_bound_method(*args, **kwargs): if len(args) == 2 and len(kwargs) == 0 and isinstance( args[1], Mapping): # ASSUME SECOND UNNAMED PARAM IS kwargs packed = params_pack(params, args[1], defaults) elif "kwargs" in kwargs and isinstance(kwargs["kwargs"], Mapping): # PUT args INTO kwargs packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), kwargs["kwargs"], defaults) else: packed = params_pack(params, kwargs, dict_zip(params[1:], args[1:]), defaults) try: return func(args[0], **packed) except TypeError as e: raise_error(e, packed) return w_bound_method else: def w_kwargs(*args, **kwargs): if len(args) == 1 and len(kwargs) == 0 and isinstance( args[0], Mapping): # ASSUME SINGLE PARAMETER IS kwargs packed = params_pack(params, args[0], defaults) elif "kwargs" in kwargs and isinstance(kwargs["kwargs"], Mapping): # PUT args INTO kwargs packed = params_pack(params, kwargs, dict_zip(params, args), kwargs["kwargs"], defaults) else: # PULL kwargs OUT INTO PARAMS packed = params_pack(params, kwargs, dict_zip(params, args), defaults) try: return func(**packed) except TypeError as e: raise_error(e, packed) return w_kwargs
def register_ops(self, module_vars): global JX if self.name != "JX": self.ops = copy(JX.ops) # A COPY, IF ONLY TO KNOW IT WAS REPLACED double_dispatch_methods = tuple( sorted(set(self.ops[1].lookups.keys()))) else: num_ops = 1 + max(obj.get_id() for obj in module_vars.values() if isinstance(obj, type) and hasattr(obj, ID)) self.ops = [None] * num_ops # FIND ALL DOUBLE-DISPATCH METHODS double_dispatch_methods = set() for _, new_op in list(module_vars.items()): if is_Expression(new_op): for name, member in vars(new_op).items(): try: args = get_function_arguments(member) if args[:2] == ("self", "lang"): double_dispatch_methods.add(name) except Exception as cause: pass double_dispatch_methods = tuple(sorted(double_dispatch_methods)) for _, new_op in list(module_vars.items()): if is_Expression(new_op): op_id = new_op.get_id() jx_op = JX.ops[op_id] # LET EACH LANGUAGE POINT TO OP CLASS self.ops[op_id] = new_op new_op.lang = self # ENSURE THE partial_eval IS REGISTERED if jx_op is None: for dd_method in double_dispatch_methods: member = extract_method(new_op, dd_method) args = get_function_arguments(member) if args[:2] != ("self", "lang"): Log.error( "{{module}}.{{clazz}}.{{name}} is expecting (self, lang) parameters, minimum", module=new_op.__module__, clazz=new_op.__name__, name=dd_method) new_op.lookups[dd_method] = [member] elif jx_op.__name__ != new_op.__name__: Log.error("Logic error") else: new_op.lookups = jx_op.lookups for dd_method in double_dispatch_methods: member = extract_method(new_op, dd_method) jx_op.lookups[dd_method] += [member] # COPY OTHER DEFINED METHODS others = list(vars(new_op).items()) for n, v in others: if v is not None: o = getattr(jx_op, n, None) if o is None: setattr(jx_op, n, v) if self.name == 'JX': # FINALLY, SWAP OUT THE BASE METHODS for dd_method in double_dispatch_methods: existing = getattr(BaseExpression, dd_method, None) if existing: # USE BaseExpression WHEN AVAILABLE setattr(Expression, dd_method, existing) else: # MAKE A DISPATCHER, IF NOT ONE ALREADY setattr(Expression, dd_method, get_dispatcher_for(dd_method)) else: # ENSURE THE ALL OPS ARE DEFINED ON THE NEW LANGUAGE for base_op, new_op in transpose(JX.ops, self.ops): if base_op and new_op is base_op: # MISSED DEFINITION, ADD ONE new_op = type(base_op.__name__, (base_op, ), {}) self.ops[new_op.get_id()] = new_op setattr(new_op, "lookups", base_op.lookups) for n, v in base_op.lookups.items(): v += v[-1:] # ENSURE THIS LANGUAGE INSTANCE POINTS TO ALL THE OPS BY NAME for o in self.ops[1:]: setattr(self, o.__name__, o)