def evaluator(_, transformation: Transformation) -> AnyType: if callable(x): _x = x(**dependency_injection.resolve_dependencies( x, transformation._available_symbols).as_kwargs) dbg("x resolved to '{}'".format(_x)) else: _x = x if callable(y): _y = y(**dependency_injection.resolve_dependencies( y, transformation._available_symbols).as_kwargs) dbg("y resolved to '{}'".format(_y)) else: _y = y return operator(_x, _y)
def test_resolve_dependencies_doesnt_get_hung_up_on_None_though(): def func(foo, bar=None): pass deps = resolve_dependencies(func, {"foo": 1, "bar": True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {"foo": 1, "bar": True}
def test_resolve_dependencies_honors_kwarg_default(): def func(foo, bar=False): pass deps = resolve_dependencies(func, {"foo": 1}) assert deps.as_args == (1, False) assert deps.as_kwargs == {"foo": 1, "bar": False}
def test_resolve_dependencies_honors_kwarg_default_of_None(): def func(foo, bar=None): pass deps = resolve_dependencies(func, {"foo": 1}) assert deps.as_args == (1, None) assert deps.as_kwargs == {"foo": 1, "bar": None}
def test_resolve_dependencies_honors_kwarg_default_of_None(): def func(foo, bar=None): pass deps = resolve_dependencies(func, {'foo': 1}) assert deps.as_args == (1, None) assert deps.as_kwargs == {'foo': 1, 'bar': None}
def test_resolve_dependencies_honors_kwarg_default(): def func(foo, bar=False): pass deps = resolve_dependencies(func, {'foo': 1}) assert deps.as_args == (1, False) assert deps.as_kwargs == {'foo': 1, 'bar': False}
def test_resolve_dependencies_resolves_kwarg(): def func(foo, bar=False): pass deps = resolve_dependencies(func, {'foo': 1, 'bar': True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {'foo': 1, 'bar': True}
def test_resolve_dependencies_doesnt_get_hung_up_on_None_though(): def func(foo, bar=None): pass deps = resolve_dependencies(func, {'foo': 1, 'bar': True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {'foo': 1, 'bar': True}
def loop(in_except): for function, prev_func in functions_iter: if _return_after is not None and prev_func is not _NO_PREVIOUS: if prev_func.__name__ == _return_after: break try: deps = resolve_dependencies(function, state) skip = ( # When function wants exception but we don't have it. not in_except and 'exception' in deps.signature.required or # When function doesn't want exception but we have it. in_except and 'exception' not in deps.signature.parameters) if not skip: new_state = function(**deps.as_kwargs) if new_state is not None: state.update(new_state) if in_except and state['exception'] is None: # exception is cleared, return to normal flow if PYTHON_2: sys.exc_clear() return except: if _raise_immediately: raise state['exception'] = sys.exc_info()[1] loop(True) if in_except: # an exception occurred while we were handling another # exception, but now it's been cleared, so we return to # the normal flow return if in_except: raise # exception hasn't been handled, reraise
def test_resolve_dependencies_resolves_kwarg(): def func(foo, bar=False): pass deps = resolve_dependencies(func, {"foo": 1, "bar": True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {"foo": 1, "bar": True}
def cast(website, request, state): """Implement typecasting (differently from stock Aspen). When matching paths, Aspen looks for ``/%foo/`` and then foo is a variable with the value in the URL path, so ``/bar/`` would end up with ``foo='bar'``. There's a dictionary at ``website.typecasters`` that maps variable names to functions, dependency-injectable as with ``website.algorithm`` (state-chain) functions. If an entry exists in ``typecasters`` for a given path variable, then the value of ``path[part]`` is replaced with the result of calling the function. Before calling your cast function, we add an additional value to the state dict at ``path_part``: the URL path part that matched, as a string. That is user input, so handle it carefully. It's your job to raise ``Response(40x)`` if it's bad input. """ typecasters = website.typecasters path = request.line.uri.path for part in path.keys(): if part not in typecasters: continue state['path_part'] = path[part] path.popall(part) func = typecasters[part] path[part] = func(*resolve_dependencies(func, state).as_args)
def test_resolve_dependencies_resolves_dependencies(): def func(foo): pass deps = resolve_dependencies(func, {'foo': 1}) assert deps.signature.parameters == ('foo', ) assert deps.signature.required == ('foo', ) assert deps.signature.optional == {} assert deps.as_args == (1, ) assert deps.as_kwargs == {'foo': 1}
def test_resolve_dependencies_resolves_dependencies(): def func(foo): pass deps = resolve_dependencies(func, {"foo": 1}) assert deps.signature.parameters == ("foo",) assert deps.signature.required == ("foo",) assert deps.signature.optional == {} assert deps.as_args == (1,) assert deps.as_kwargs == {"foo": 1}
def _apply_handlers(self, *handlers: Union[Callable, Exception]) -> None: dbg("Applying handlers.") for handler in handlers: if _is_flow_control(handler): raise handler kwargs = dependency_injection.resolve_dependencies( handler, self._available_symbols).as_kwargs if isinstance(handler, Transformation): kwargs["input"] = self.states.current_node or self.states.root kwargs["copy"] = False dbg(f"Applying handler {handler}.") self.states.previous_result = handler(**kwargs)
def _apply_handlers(self, *handlers) -> None: dbg('Applying handlers.') for handler in handlers: if _is_flow_control(handler): raise handler kwargs = dependency_injection.resolve_dependencies( handler, self._available_symbols).as_kwargs if isinstance(handler, Transformation): kwargs[ 'transformation_root'] = self.states.current_element or self.states.root kwargs['copy'] = False dbg("Applying handler {}.".format(handler)) self.states.previous_result = handler(**kwargs)
def render_notifications(self, state): r = [] escape = state['escape'] state['escape'] = lambda a: a for name in self.notifications: try: f = getattr(notifications, name) typ, msg = f(*resolve_dependencies(f, state).as_args) r.append(dict(jsonml=msg, name=name, type=typ)) except Exception as e: self._tell_sentry(e, state) state['escape'] = escape return r
def _apply_handlers(handlers, context): echo(click.style(os.path.relpath(context['filepath']), fg='yellow')) for handler in handlers: kwargs = dependency_injection.resolve_dependencies(handler, context).as_kwargs handler_verbosity = getattr(handler, "verbosity", 1) handler_name = getattr(handler, "verbose_name", handler.__name__) echo("\n - {}".format(handler_name), verbosity=handler_verbosity) try: context['input'] = handler(**kwargs) except SkipHandler: continue except AbortHandling as e: echo(str(e), verbosity=e.verbosity) break context.pop('input', None)
def loop(in_except): signatures = self._signatures for function, prev_func in functions_iter: if _return_after is not None and prev_func is not _NO_PREVIOUS: if prev_func.__name__ == _return_after: break try: if function not in signatures: signatures[function] = get_signature(function) deps = resolve_dependencies(signatures[function], state) skip = ( # When function wants exception but we don't have it. not in_except and 'exception' in deps.signature.required or # When function doesn't want exception but we have it. in_except and 'exception' not in deps.signature.parameters ) if not skip: new_state = function(**deps.as_kwargs) if new_state is not None: state.update(new_state) if in_except and state['exception'] is None: # exception is cleared, return to normal flow if PYTHON_2: sys.exc_clear() return except: if _raise_immediately: raise state['exception'] = sys.exc_info()[1] loop(True) if in_except: # an exception occurred while we were handling another # exception, but now it's been cleared, so we return to # the normal flow return if in_except: raise # exception hasn't been handled, reraise
def render_in_english(self): f = self.lazy_body fake_state = {} from liberapay.i18n.base import LOCALE_EN, add_helpers_to_context add_helpers_to_context(fake_state, LOCALE_EN) return f(*resolve_dependencies(f, fake_state).as_args)
def render_body(self, state): f = self.lazy_body self.body = f(*resolve_dependencies(f, state).as_args)
def run(self, _raise_immediately=None, _return_after=None, **state): """Run through the functions in the :py:attr:`functions` list. :param bool _raise_immediately: if not ``None``, will override any default for ``raise_immediately`` that was set in the constructor :param str _return_after: if not ``None``, return after calling the function with this name :param dict state: remaining keyword arguments are used for the initial state dictionary for this run of the algorithm :raises: :py:exc:`FunctionNotFound`, if there is no function named ``_return_after`` :returns: a dictionary representing the final algorithm state The state dictionary is initialized with three items (their default values can be overriden using keyword arguments to :py:func:`run`): - ``algorithm`` - a reference to the parent :py:class:`Algorithm` instance - ``state`` - a circular reference to the state dictionary - ``exception`` - ``None`` For each function in the :py:attr:`functions` list, we look at the function signature and compare it to the current value of ``exception`` in the state dictionary. If ``exception`` is ``None`` then we skip any function that asks for ``exception``, and if ``exception`` is *not* ``None`` then we only call functions that *do* ask for it. The upshot is that any function that raises an exception will cause us to fast-forward to the next exception-handling function in the list. Here are some further notes on exception handling: - If a function provides a default value for ``exception``, then that function will be called whether or not there is an exception being handled. - You should return ``{'exception': None}`` to reset exception handling. Under Python 2 we will call ``sys.exc_clear`` for you (under Python 3 exceptions are cleared automatically at the end of except blocks). - If ``exception`` is not ``None`` after all functions have been run, then we re-raise it. - If ``raise_immediately`` evaluates to ``True`` (looking first at any per-call ``_raise_immediately`` and then at the instance default), then we re-raise any exception immediately instead of fast-forwarding to the next exception handler. """ if _raise_immediately is None: _raise_immediately = self.default_raise_immediately if _return_after is not None: if _return_after not in self.get_names(): raise FunctionNotFound(_return_after) if 'algorithm' not in state: state['algorithm'] = self if 'state' not in state: state['state'] = state if 'exception' not in state: state['exception'] = None for function in self.functions: function_name = function.__name__ try: deps = resolve_dependencies(function, state) have_exception = state['exception'] is not None if 'exception' in deps.signature.required and not have_exception: pass # Function wants exception but we don't have it. elif 'exception' not in deps.signature.parameters and have_exception: pass # Function doesn't want exception but we have it. else: new_state = function(**deps.as_kwargs) if new_state is not None: if PYTHON_2: if 'exception' in new_state: if new_state['exception'] is None: sys.exc_clear() state.update(new_state) except: ExceptionClass, exception = sys.exc_info()[:2] state['exception'] = exception if _raise_immediately: raise if _return_after is not None and function_name == _return_after: break if state['exception'] is not None: if PYTHON_2: # Under Python 2, raising state['exception'] means the # traceback stops at this reraise. We want the traceback to go # back to where the exception was first raised, and a naked # raise will reraise the current exception. raise else: # Under Python 3, exceptions are cleared at the end of the # except block, meaning we have no current exception to reraise # here. Thankfully, the traceback off this reraise will show # back to the original exception. raise state['exception'] return state
def check_callable(cllbl): deps = resolve_dependencies(cllbl, {'foo': 1, 'bar': True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {'foo': 1, 'bar': True}
def callable_evaluator(transformation): kwargs = dependency_injection.resolve_dependencies( constraints, transformation._available_symbols).as_kwargs return MatchesAttributes(constraints(**kwargs))( transformation.states.current_element)
def check_callable(cllbl): deps = resolve_dependencies(cllbl, {"foo": 1, "bar": True}) assert deps.as_args == (1, True) assert deps.as_kwargs == {"foo": 1, "bar": True}