def __init__(self, **kwds): for key, val in kwds.items(): setattr(self, key, val) # Sanity checks required = [ 'compute', ] for key in required: if not getattr(self, key): raise ValueError( "{} needs to be defined during instantiation.".format(key)) # Extract information from the `compute` function. # The signature self._sig = signature(self.compute) # The input parameter names self._parameters = tuple(self._sig.parameters.keys()) # self._input_params = [p for p in self._sig.parameters.values() if p.default is p.empty] # self._nvar = len(self._input_params) # Copy the docstring and signature self.__call__ = wraps(self.compute)(self.__call__.__func__) if self.doc_template is not None: self.__call__.__doc__ = self.doc_template.format(i=self) # Fill in missing metadata from the doc meta = parse_doc(self.compute.__doc__) for key in ['abstract', 'title', 'long_name', 'notes', 'references']: setattr(self, key, getattr(self, key) or meta.get(key, ''))
def __init__(self, **kwds): """Run checks and organizes the metadata.""" # keywords of kwds that are class attributes have already been set in __new__ self.check_identifier(self.identifier) if self.missing == "from_context" and self.missing_options is not None: raise ValueError( "Cannot set `missing_options` with `missing` method being from context." ) # Validate hard-coded missing options kls = MISSING_METHODS[self.missing] self._missing = kls.execute if self.missing_options: kls.validate(**self.missing_options) # Validation is done : register the instance. super().__init__() # The `compute` signature self._sig = signature(self.compute) # The input parameters' name self._parameters = tuple(self._sig.parameters.keys()) # Copy the docstring and signature self.__call__ = wraps(self.compute)(self.__call__)
def sym_to_num(fun, *args): """ Convert sympy function to numpy function, with the output shape broadcasted to the shape of the sum of all arguments. This is required in case one or more arguments are not used explicity in the formula. """ f = fu.wraps(fun)(sp.lambdify(args, fun(*args), modules=("numpy", ))) @fu.wraps(fun) def wrapper(*inner_args): """ Reshape output to always match broadcasted sum of inputs, even if they are not all explicitly used in the function. """ array_args = map(np.array, inner_args) shape = np.shape(sum(array_args)) ans = f(*inner_args) return np.broadcast_to(ans, shape) return wrapper
def test_wraps_hide_wrapped(): new_func = wraps(wrappable_func, injected='b')(lambda a: wrappable_func(a, b=1)) new_sig = inspect.signature(new_func, follow_wrapped=True) assert list(new_sig.parameters.keys()) == ['a', 'b'] new_func = wraps(wrappable_func, injected='b', hide_wrapped=True)(lambda a: wrappable_func(a, b=1)) new_sig = inspect.signature(new_func, follow_wrapped=True) assert list(new_sig.parameters.keys()) == ['a'] new_func = wraps(wrappable_func, injected='b')(lambda a: wrappable_func(a, b=1)) new_new_func = wraps(new_func, injected='a', hide_wrapped=True)(lambda: new_func(a=1)) new_new_sig = inspect.signature(new_new_func, follow_wrapped=True) assert len(new_new_sig.parameters) == 0
def test_wraps_inner_kwarg_only(): """from https://github.com/mahmoud/boltons/issues/261 mh responds to the issue: You'll notice that when kw-only args are involved the first time (wraps(f)(g)) it works fine. The other way around, however, wraps(g)(f) fails, because by the very nature of funcutils.wraps, you're trying to give f the same signature as g. And f's signature is not like g's. g supports positional b and f() does not. If you want to make a wrapper which converts a keyword-only argument to one that can be positional or keyword only, that'll require a different approach for now. A potential fix would be to pass all function arguments as keywords. But doubt that's the right direction, because, while I have yet to add positional argument only support, that'll definitely throw a wrench into things. """ from boltons.funcutils import wraps def g(a: float, b=10): return a * b def f(a: int, *, b=1): return a * b # all is well here... assert f(3) == 3 assert g(3) == 30 assert wraps(f)(g)( 3) == 3 # yay, g got the f default (not so with functools.wraps!) # but this doesn't work with pytest.raises(TypeError): wraps(g)(f)(3) return
def darkhelm_inject_loop(func): sig = inspect.signature(func) loop_param = sig.parameters['loop'].replace(default=None) sig = sig.replace(parameters=[loop_param]) def add_loop(args, kwargs): bargs = sig.bind(*args, **kwargs) bargs.apply_defaults() if bargs.arguments['loop'] is None: bargs.arguments['loop'] = "don't look at me, I just use gevent" return bargs.arguments def wrapper(*args, **kwargs): return func(**add_loop(args, kwargs)) return wraps(func, injected=['loop'])(wrapper)
def darkhelm_inject_loop(func): sig = inspect.signature(func) loop_param = sig.parameters['loop'].replace(default=None) sig = sig.replace(parameters=[loop_param]) def add_loop(args, kwargs): bargs = sig.bind(*args, **kwargs) bargs.apply_defaults() if bargs.arguments['loop'] is None: bargs.arguments['loop'] = "don't look at me, I just use gevent" return bargs.arguments def wrapper(*args, **kwargs): return func(**add_loop(args, kwargs)) return wraps(func, injected=['loop'])(wrapper)
def __init__(self, **kwds): # Set instance attributes. for key, val in kwds.items(): setattr(self, key, val) # Verify that the identifier is a proper slug if not re.match(r"^[-\w]+$", self.identifier): warnings.warn( "The identifier contains non-alphanumeric characters. It could make life " "difficult for downstream software reusing this class.", UserWarning, ) # Default value for `var_name` is the `identifier`. if self.var_name == "": self.var_name = self.identifier # Extract information from the `compute` function. # The signature self._sig = signature(self.compute) # The input parameter names self._parameters = tuple(self._sig.parameters.keys()) # self._input_params = [p for p in self._sig.parameters.values() if p.default is p.empty] # self._nvar = len(self._input_params) # Copy the docstring and signature self.__call__ = wraps(self.compute)(self.__call__.__func__) if self.doc_template is not None: self.__call__.__doc__ = self.doc_template.format(i=self) # Fill in missing metadata from the doc meta = parse_doc(self.compute.__doc__) for key in ["abstract", "title", "notes", "references"]: setattr(self, key, getattr(self, key) or meta.get(key, ""))