def owner(request, resource): body = Body(copy.deepcopy(OWNER)) if request.param == 'state-changing-cause': cause = ChangingCause( 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 = WatchingCause( logger=logging.getLogger('kopf.test.fake.logger'), indices=OperatorIndexers().indices, resource=resource, patch=Patch(), memo=Memo(), body=body, type='irrelevant', event=RawEvent(type='irrelevant', object=OWNER), ) with context([(cause_var, cause)]): yield body else: raise RuntimeError(f"Wrong param for `owner` fixture: {request.param!r}")
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), (subregistry_var, registry)]): kopf.subhandler()(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 subhandling_context() -> AsyncIterator[None]: with invocation.context([ (subregistry_var, registries.ChangingRegistry()), (subexecuted_var, False), ]): # Go for normal handler invocation. yield # If the sub-handlers are not called explicitly, run them implicitly # as if it was done inside of the handler (still under the try-finally clause). if not subexecuted_var.get(): await execute()
def test_invalid_oldnew_for_inappropriate_subhandlers(resource, decorator, registry): @decorator(*resource) def fn(**_): @kopf.subhandler(field='f', old='x') def fn2(**_): pass subregistry = ChangingRegistry() handler = registry._changing.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()
async def invoke_handler( *, handler: Handler, cause: Cause, retry: int, started: datetime.datetime, runtime: datetime.timedelta, settings: configuration.OperatorSettings, lifecycle: Optional[LifeCycleFn], subrefs: Set[ids.HandlerId], extra_context: ExtraContext, ) -> Optional[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. cause = handler.adjust_cause(cause) # The context makes it possible and easy to pass the kwargs _through_ the user-space handlers: # from the framework to the framework's helper functions (e.g. sub-handling, hierarchies, etc). with invocation.context([ (sublifecycle_var, lifecycle), (subsettings_var, settings), (subrefs_var, list(subrefs_var.get([])) + [subrefs]), (handler_var, handler), (cause_var, cause), ]): async with extra_context(): result = await invocation.invoke( handler.fn, settings=settings, kwargsrc=cause, kwargs=dict( param=handler.param, retry=retry, started=started, runtime=runtime, ), ) # Since we know that we invoked the handler, we cast "any" result to a handler result. return Result(result)
def test_subhandler_imperatively(parent_handler, cause_factory): cause = cause_factory(reason=Reason.UPDATE) registry = ChangingRegistry() subregistry_var.set(registry) def fn(**_): pass with context([(handler_var, parent_handler)]): kopf.register(fn) handlers = registry.get_handlers(cause) assert len(handlers) == 1 assert handlers[0].fn is fn