def owner(request, resource): if request.param == 'state-changing-cause': cause = ResourceChangingCause( logger=logging.getLogger('kopf.test.fake.logger'), resource=resource, patch=Patch(), memo=ObjectDict(), body=OWNER, initial=False, reason=Reason.NOOP, ) with context([(cause_var, cause)]): yield elif request.param == 'event-watching-cause': cause = ResourceWatchingCause( logger=logging.getLogger('kopf.test.fake.logger'), resource=resource, patch=Patch(), memo=ObjectDict(), body=OWNER, type='irrelevant', raw=Event(type='irrelevant', object=OWNER), ) with context([(cause_var, cause)]): yield else: raise RuntimeError( f"Wrong param for `owner` fixture: {request.param!r}")
def owner(request, resource): body = Body(copy.deepcopy(OWNER)) if request.param == 'state-changing-cause': cause = ResourceChangingCause( logger=logging.getLogger('kopf.test.fake.logger'), indices=OperatorIndexers().indices, resource=resource, patch=Patch(), memo=Memo(), body=body, initial=False, reason=Reason.NOOP, ) with context([(cause_var, cause)]): yield body elif request.param == 'event-watching-cause': cause = ResourceWatchingCause( logger=logging.getLogger('kopf.test.fake.logger'), indices=OperatorIndexers().indices, resource=resource, patch=Patch(), memo=Memo(), body=body, type='irrelevant', raw=RawEvent(type='irrelevant', object=OWNER), ) with context([(cause_var, cause)]): yield body else: raise RuntimeError( f"Wrong param for `owner` fixture: {request.param!r}")
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)
def test_with_parent(mocker, parent_handler, resource_registry_cls): registry = resource_registry_cls() with context([(handler_var, parent_handler)]): kopf.on.this(registry=registry)(child_fn) handlers = registry.get_handlers(mocker.MagicMock()) assert len(handlers) == 1 assert handlers[0].fn is child_fn assert handlers[0].id == 'parent_fn/child_fn'
def test_with_parent(parent_handler, resource_registry_cls, cause_factory): cause = cause_factory(resource_registry_cls) registry = resource_registry_cls() with context([(handler_var, parent_handler)]): kopf.on.this(registry=registry)(child_fn) handlers = registry.get_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is child_fn assert handlers[0].id == 'parent_fn/child_fn'
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
def test_invalid_oldnew_for_inappropriate_subhandlers(resource, decorator, registry): @decorator(resource.group, resource.version, resource.plural) def fn(**_): @kopf.on.this(field='f', old='x') def fn2(**_): pass subregistry = ResourceChangingRegistry() handler = registry.resource_changing_handlers.get_all_handlers()[0] with context([(handler_var, handler), (subregistry_var, subregistry)]): with pytest.raises(TypeError, match="can only be used in update handlers"): handler.fn()
def test_subhandler_declaratively(parent_handler, cause_factory): cause = cause_factory(reason=Reason.UPDATE) registry = ResourceChangingRegistry() subregistry_var.set(registry) with context([(handler_var, parent_handler)]): @kopf.subhandler() def fn(**_): pass handlers = registry.get_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn
def test_subhandler_declaratively(mocker, parent_handler): cause = mocker.MagicMock(reason=Reason.UPDATE, diff=None) registry = ResourceChangingRegistry() subregistry_var.set(registry) with context([(handler_var, parent_handler)]): @kopf.on.this() def fn(**_): pass handlers = registry.get_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn
def test_subhandler_declaratively(parent_handler, cause_factory): cause = cause_factory(reason=Reason.UPDATE) registry = SimpleRegistry() subregistry_var.set(registry) with context([(handler_var, parent_handler)]): @kopf.on.this() def fn(**_): pass with pytest.deprecated_call(match=r"cease using the internal registries"): handlers = registry.get_cause_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn
def test_subhandler_imperatively(parent_handler, cause_factory): cause = cause_factory(reason=Reason.UPDATE) registry = SimpleRegistry() subregistry_var.set(registry) def fn(**_): pass with context([(handler_var, parent_handler)]): kopf.register(fn) with pytest.deprecated_call( match=r"use ResourceChangingRegistry.get_handlers\(\)"): handlers = registry.get_cause_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn
def test_subhandler_declaratively(mocker, parent_handler): cause = mocker.MagicMock(reason=Reason.UPDATE, diff=None) registry = SimpleRegistry() subregistry_var.set(registry) with context([(handler_var, parent_handler)]): @kopf.on.this() def fn(**_): pass with pytest.deprecated_call( match=r"use ResourceChangingRegistry.get_handlers\(\)"): handlers = registry.get_cause_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn