示例#1
0
def test_existent_path_selects_relevant_ops():
    result = reduce(DIFF, ['key2'])
    assert result == (
        ('change', (), 'old2', 'new2'),
        ('add', ('suba', ), 'olda', 'newa'),
        ('remove', ('subb', ), 'oldb', 'newb'),
    )
示例#2
0
async def invoke_handler(
    handler: handlers_.BaseHandler,
    *args: Any,
    cause: causation.BaseCause,
    settings: configuration.OperatorSettings,
    lifecycle: Optional[lifecycles.LifeCycleFn],
    subrefs: Set[handlers_.HandlerId],
    **kwargs: Any,
) -> Optional[callbacks.Result]:
    """
    Invoke one handler only, according to the calling conventions.

    Specifically, calculate the handler-specific fields (e.g. field diffs).

    Ensure the global context for this asyncio task is set to the handler and
    its cause -- for proper population of the sub-handlers via the decorators
    (see `@kopf.subhandler`).
    """

    # For the field-handlers, the old/new/diff values must match the field, not the whole object.
    if (True and  # for readable indenting
            isinstance(cause, causation.ResourceChangingCause) and
            isinstance(handler, handlers_.ResourceHandler) and handler.field
            is not None):
        old = dicts.resolve(cause.old, handler.field, None)
        new = dicts.resolve(cause.new, handler.field, None)
        diff = diffs.reduce(cause.diff, handler.field)
        cause = causation.enrich_cause(cause=cause,
                                       old=old,
                                       new=new,
                                       diff=diff)

    # Store the context of the current handler, to be used in `@kopf.subhandler`,
    # and maybe other places, and consumed in the recursive `execute()` calls for the children.
    # This replaces the multiple kwargs passing through the whole call stack (easy to forget).
    with invocation.context([
        (sublifecycle_var, lifecycle),
        (subregistry_var, registries.ResourceChangingRegistry()),
        (subsettings_var, settings),
        (subexecuted_var, False),
        (subrefs_var, list(subrefs_var.get([])) + [subrefs]),
        (handler_var, handler),
        (cause_var, cause),
    ]):
        # And call it. If the sub-handlers are not called explicitly, run them implicitly
        # as if it was done inside of the handler (i.e. under try-finally block).
        result = await invocation.invoke(
            handler.fn,
            *args,
            settings=settings,
            cause=cause,
            **kwargs,
        )

        if not subexecuted_var.get() and isinstance(
                cause, causation.ResourceChangingCause):
            await execute()

        # Since we know that we invoked the handler, we cast "any" result to a handler result.
        return callbacks.Result(result)
示例#3
0
def _matches_field(
    handler: handlers.ResourceHandler,
    cause: causation.ResourceCause,
    ignore_fields: bool = False,
) -> bool:
    return (ignore_fields
            or not isinstance(handler, handlers.ResourceChangingHandler)
            or not handler.field
            or (isinstance(cause, causation.ResourceChangingCause)
                and bool(diffs.reduce(cause.diff, handler.field))))
示例#4
0
async def _call_handler(handler: registries.Handler, *args,
                        cause: causation.Cause, lifecycle: Callable, **kwargs):
    """
    Invoke one handler only, according to the calling conventions.

    Specifically, calculate the handler-specific fields (e.g. field diffs).

    Ensure the global context for this asyncio task is set to the handler and
    its cause -- for proper population of the sub-handlers via the decorators
    (see `@kopf.on.this`).
    """

    # For the field-handlers, the old/new/diff values must match the field, not the whole object.
    old = cause.old if handler.field is None else dicts.resolve(
        cause.old, handler.field, None)
    new = cause.new if handler.field is None else dicts.resolve(
        cause.new, handler.field, None)
    diff = cause.diff if handler.field is None else diffs.reduce(
        cause.diff, handler.field)
    cause = cause._replace(old=old, new=new, diff=diff)

    # Store the context of the current resource-object-event-handler, to be used in `@kopf.on.this`,
    # and maybe other places, and consumed in the recursive `execute()` calls for the children.
    # This replaces the multiple kwargs passing through the whole call stack (easy to forget).
    sublifecycle_token = sublifecycle_var.set(lifecycle)
    subregistry_token = subregistry_var.set(
        registries.SimpleRegistry(prefix=handler.id))
    subexecuted_token = subexecuted_var.set(False)
    handler_token = handler_var.set(handler)
    cause_token = cause_var.set(cause)

    # And call it. If the sub-handlers are not called explicitly, run them implicitly
    # as if it was done inside of the handler (i.e. under try-finally block).
    try:
        result = await invocation.invoke(
            handler.fn,
            *args,
            cause=cause,
            **kwargs,
        )

        if not subexecuted_var.get():
            await execute()

        return result

    finally:
        # Reset the context to the parent's context, or to nothing (if already in a root handler).
        sublifecycle_var.reset(sublifecycle_token)
        subregistry_var.reset(subregistry_token)
        subexecuted_var.reset(subexecuted_token)
        handler_var.reset(handler_token)
        cause_var.reset(cause_token)
示例#5
0
async def _call_handler(
    handler: registries.ResourceHandler,
    *args: Any,
    cause: causation.BaseCause,
    lifecycle: lifecycles.LifeCycleFn,
    **kwargs: Any,
) -> Any:
    """
    Invoke one handler only, according to the calling conventions.

    Specifically, calculate the handler-specific fields (e.g. field diffs).

    Ensure the global context for this asyncio task is set to the handler and
    its cause -- for proper population of the sub-handlers via the decorators
    (see `@kopf.on.this`).
    """

    # For the field-handlers, the old/new/diff values must match the field, not the whole object.
    if isinstance(
            cause,
            causation.ResourceChangingCause) and handler.field is not None:
        old = dicts.resolve(cause.old, handler.field, None, assume_empty=True)
        new = dicts.resolve(cause.new, handler.field, None, assume_empty=True)
        diff = diffs.reduce(cause.diff, handler.field)
        cause = causation.enrich_cause(cause=cause,
                                       old=old,
                                       new=new,
                                       diff=diff)

    # Store the context of the current resource-object-event-handler, to be used in `@kopf.on.this`,
    # and maybe other places, and consumed in the recursive `execute()` calls for the children.
    # This replaces the multiple kwargs passing through the whole call stack (easy to forget).
    with invocation.context([
        (sublifecycle_var, lifecycle),
        (subregistry_var, registries.ResourceRegistry(prefix=handler.id)),
        (subexecuted_var, False),
        (handler_var, handler),
        (cause_var, cause),
    ]):
        # And call it. If the sub-handlers are not called explicitly, run them implicitly
        # as if it was done inside of the handler (i.e. under try-finally block).
        result = await invocation.invoke(
            handler.fn,
            *args,
            cause=cause,
            **kwargs,
        )

        if not subexecuted_var.get() and isinstance(
                cause, causation.ResourceChangingCause):
            await execute()

        return result
示例#6
0
def test_overly_specific_path_dives_into_dicts_for_change():
    result = reduce(DIFF, ['key4', 'subc'])
    assert result == (('change', (), 'oldc', 'newc'), )
示例#7
0
def test_overly_specific_path_dives_into_dicts_for_removal():
    result = reduce(DIFF, ['key4', 'suba'])
    assert result == (('remove', (), 'olda', None), )
示例#8
0
def test_overly_specific_path_dives_into_dicts_for_addition():
    result = reduce(DIFF, ['key4', 'subb'])
    assert result == (('add', (), None, 'newb'), )
示例#9
0
def test_nonexistent_path_selects_nothing():
    result = reduce(DIFF, ['nonexistent-key'])
    assert result == ()
示例#10
0
def test_empty_path_selects_all_ops():
    result = reduce(DIFF, [])
    assert result == DIFF
示例#11
0
def test_type_ignored_for_inputs_but_is_tuple_for_output(diff, path):
    result = reduce(diff, path)
    assert result == (('op', (), 'old', 'new'), )
示例#12
0
def test_nonexistent_path_selects_nothing(path):
    result = reduce(DIFF, path)
    assert result == ()