# HG changeset patch # User Thierry Florac # Date 1547816327 -3600 # Node ID 4bbf32d81a21b977e3ac102cdd45a320e03e64fd # Parent 512b92eeaa79234c42567139be544a43c2dc52d8 Updated "provider" expression to be able to pass arguments to "update()" method diff -r 512b92eeaa79 -r 4bbf32d81a21 src/pyams_viewlet/provider.py --- a/src/pyams_viewlet/provider.py Fri Jan 18 10:23:14 2019 +0100 +++ b/src/pyams_viewlet/provider.py Fri Jan 18 13:58:47 2019 +0100 @@ -24,26 +24,79 @@ from pyams_utils.tales import ContextExprMixin +FUNCTION_EXPRESSION = re.compile('(.+)\((.+)\)', re.MULTILINE | re.DOTALL) +ARGUMENTS_EXPRESSION = re.compile('[^(,)]+') + CONTENT_PROVIDER_NAME = re.compile('([A-Za-z0-9_\-\.]+)') def render_content_provider(econtext, name): - match = CONTENT_PROVIDER_NAME.match(name.strip()) - if match: - name = match.groups()[0] - else: - raise ContentProviderLookupError(name) + """TALES provider: content provider + + This TALES expression is used to render a registered "content provider", which + is an adapter providing IContentProvider interface; adapter lookup is based on + current context, request and view. + + The requested provider can be called with our without arguments, like in + ${structure:provider:my_provider} or ${structure:provider:my_provider(arg1, arg2)}. + In the second form, arguments will be passed to the "update" method; arguments can be + static (like strings or integers), or can be variables defined into current template + context; other Python expressions including computations or functions calls are actually + not supported, but dotted syntax is supported to access inner attributes of variables. + """ + + def get_value(econtext, arg): + """Extract argument value from context + Extension expression language is quite simple. Values can be given as + positioned strings, integers or named arguments of the same types. + """ + arg = arg.strip() + if arg.startswith('"') or arg.startswith("'"): + # may be a quoted string... + return arg[1:-1] + if '=' in arg: + key, value = arg.split('=', 1) + value = get_value(econtext, value) + return {key.strip(): value} + try: + arg = int(arg) # check integer value + except ValueError: + args = arg.split('.') + result = econtext.get(args.pop(0)) + for arg in args: + result = getattr(result, arg) + return result + else: + return arg + + name = name.strip() context = econtext.get('context') request = econtext.get('request') if isinstance(request, PyramidPublisherRequest): request = request._request view = econtext.get('view') + args, kwargs = [], {} + func_match = FUNCTION_EXPRESSION.match(name) + if func_match: + name, arguments = func_match.groups() + for arg in map(lambda x: get_value(econtext, x), ARGUMENTS_EXPRESSION.findall(arguments)): + if isinstance(arg, dict): + kwargs.update(arg) + else: + args.append(arg) + else: + match = CONTENT_PROVIDER_NAME.match(name) + if match: + name = match.groups()[0] + else: + raise ContentProviderLookupError(name) + registry = request.registry provider = registry.queryMultiAdapter((context, request, view), IContentProvider, name=name) - # provide a useful error message, if the provider was not found. + # raise an exception if the provider was not found. if provider is None: raise ContentProviderLookupError(name) @@ -56,7 +109,7 @@ # Stage 1: Do the state update registry.notify(BeforeUpdateEvent(provider, request)) - provider.update() + provider.update(*args, **kwargs) # Stage 2: Render the HTML content return provider.render()