From e5520dc3479f2b1fad947dfd3d52426aef546a37 Mon Sep 17 00:00:00 2001 From: Jan Cholasta Date: Wed, 2 Mar 2016 11:00:23 +0100 Subject: [PATCH] ipalib: provide per-call command context Add context which is valid for the duration of command call. The context is accessible using the `context` attribute of Command and Object plugins. Reviewed-By: Martin Basti --- ipalib/frontend.py | 10 ++++++++++ ipalib/request.py | 21 +++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/ipalib/frontend.py b/ipalib/frontend.py index 5b730b1d1..e91660d1d 100644 --- a/ipalib/frontend.py +++ b/ipalib/frontend.py @@ -38,6 +38,7 @@ from ipalib.errors import (ZeroArgumentError, MaxArgumentError, OverlapError, VersionError, OptionError, InvocationError, ValidationError, ConversionError) from ipalib import messages +from ipalib.request import context, context_frame from ipalib.util import json_serialize if six.PY3: @@ -370,6 +371,10 @@ class HasParam(Plugin): check(namespace) setattr(self, name, namespace) + @property + def context(self): + return context.current_frame + class Command(HasParam): """ @@ -424,6 +429,11 @@ class Command(HasParam): XML-RPC and the executed an the nearest IPA server. """ self.ensure_finalized() + with context_frame(): + self.context.principal = getattr(context, 'principal', None) + return self.__do_call(*args, **options) + + def __do_call(self, *args, **options): version_provided = 'version' in options if version_provided: self.verify_client_version(unicode(options['version'])) diff --git a/ipalib/request.py b/ipalib/request.py index 9484be58f..d851ba84d 100644 --- a/ipalib/request.py +++ b/ipalib/request.py @@ -22,6 +22,7 @@ Per-request thread-local data. """ +import contextlib import threading from ipalib.base import ReadOnly, lock @@ -32,6 +33,26 @@ from ipalib.constants import CALLABLE_ERROR context = threading.local() +class _FrameContext(object): + pass + + +@contextlib.contextmanager +def context_frame(): + try: + frame_back = context.current_frame + except AttributeError: + pass + context.current_frame = _FrameContext() + try: + yield + finally: + try: + context.current_frame = frame_back + except UnboundLocalError: + del context.current_frame + + class Connection(ReadOnly): """ Base class for connection objects stored on `request.context`.