# HG changeset patch # User Thierry Florac # Date 1499860601 -7200 # Node ID 119b9c2f30227a6961ce841b60881165c789a8ae # Parent 2d076b2b485a71135fd06579423410b8205af66b Refactored request and session properties management diff -r 2d076b2b485a -r 119b9c2f3022 src/pyams_utils/property.py --- a/src/pyams_utils/property.py Wed Jul 12 13:55:47 2017 +0200 +++ b/src/pyams_utils/property.py Wed Jul 12 13:56:41 2017 +0200 @@ -18,8 +18,6 @@ # import interfaces # import packages -from pyams_utils.request import check_request, get_request_data, set_request_data -from pyams_utils.session import get_session_data, set_session_data from zope.schema.fieldproperty import FieldProperty @@ -75,73 +73,6 @@ return result -_marker = object() - - -def request_property(key, prefix=None): - """Define a method decorator used to store result into current request's annotations - - If not request is currently running, a new one is created. - `key` is a required argument; if None, the key will be the method's object - - :param str key: session's value key; if *None*, the key will be the method's object; if *key* is a callable - object, it will be called to get the actual session key - :param prefix: str; prefix to use for session key; if *None*, the prefix will be the property name - """ - - def request_decorator(func): - - def wrapper(obj, key, *args, **kwargs): - request = check_request() - if callable(key): - key = key(obj) - if not key: - key = '{1}::{0!r}'.format(obj, prefix or func.__name__) - data = get_request_data(request, key, _marker) - if data is _marker: - data = func - if callable(data): - data = data(obj, *args, **kwargs) - set_request_data(request, key, data) - return data - - return lambda x: wrapper(x, key=key) - - return request_decorator - - -def session_property(app, key=None, prefix=None): - """Define a method decorator used to store result into request's session - - If no request is currently running, a new one is created. - - :param str app: application identifier used to prefix session keys - :param str key: session's value key; if *None*, the key will be the method's object; if *key* is a callable - object, il will be called to get the actual session key - :param prefix: str; prefix to use for session key; if *None*, the prefix will be the property name - """ - - def session_decorator(func): - - def wrapper(obj, app, key, *args, **kwargs): - request = check_request() - if callable(key): - key = key(obj) - if not key: - key = '{1}::{0!r}'.format(obj, prefix or func.__name__) - data = get_session_data(request, app, key, _marker) - if data is _marker: - data = func - if callable(data): - data = data(obj, *args, **kwargs) - set_session_data(request, app, key, data) - return data - - return lambda x: wrapper(x, app=app, key=key) - - return session_decorator - - class classproperty: """Same decorator as property(), but passes obj.__class__ instead of obj to fget/fset/fdel. diff -r 2d076b2b485a -r 119b9c2f3022 src/pyams_utils/request.py --- a/src/pyams_utils/request.py Wed Jul 12 13:55:47 2017 +0200 +++ b/src/pyams_utils/request.py Wed Jul 12 13:56:41 2017 +0200 @@ -16,15 +16,82 @@ # import standard library # import interfaces -from pyams_utils.interfaces import MissingRequestError +from pyams_utils.interfaces import MissingRequestError, ICacheKeyValue +from pyramid.interfaces import IAuthenticationPolicy, IAuthorizationPolicy from zope.annotation.interfaces import IAttributeAnnotatable, IAnnotations # import packages from pyramid.request import Request +from pyramid.security import Allowed from pyramid.threadlocal import get_current_request, get_current_registry from zope.interface import alsoProvides +_marker = object() + + +def request_property(key=None, prefix=None): + """Define a method decorator used to store result into current request's annotations + + If no request is currently running, a new one is created. + `key` is a required argument; if None, the key will be the method's object + + :param str key: annotations value key; if *None*, the key will be the method's object; if *key* is a callable + object, it will be called to get the actual session key + :param prefix: str; prefix to use for session key; if *None*, the prefix will be the property name + """ + + def request_decorator(func): + + def wrapper(obj, key, *args, **kwargs): + request = check_request() + if callable(key): + key = key(obj, *args, **kwargs) + if not key: + key = '{0}::{1}'.format(prefix or func.__name__, ICacheKeyValue(obj)) + if args: + key += '::' + '::'.join((ICacheKeyValue(arg) for arg in args)) + if kwargs: + key += '::' + '::'.join((ICacheKeyValue(arg) for arg in kwargs.items())) + data = get_request_data(request, key, _marker) + if data is _marker: + data = func + if callable(data): + data = data(obj, *args, **kwargs) + set_request_data(request, key, data) + return data + + return lambda x, *args, **kwargs: wrapper(x, key, *args, **kwargs) + + return request_decorator + + +class PyAMSRequest(Request): + """Custom request factory + + Used to add 'context' argument to 'effective_principals' method call + to be able to get 'roles' principals + """ + + @request_property(key=None) + def has_permission(self, permission, context=None): + if context is None: + context = self.context + try: + reg = self.registry + except AttributeError: + reg = get_current_registry() + authn_policy = reg.queryUtility(IAuthenticationPolicy) + if authn_policy is None: + return Allowed('No authentication policy in use.') + authz_policy = reg.queryUtility(IAuthorizationPolicy) + if authz_policy is None: + raise ValueError('Authentication policy registered without ' + 'authorization policy') # should never happen + principals = authn_policy.effective_principals(self, context) + return authz_policy.permits(context, principals, permission) + + def get_request(raise_exception=True): """Get current request diff -r 2d076b2b485a -r 119b9c2f3022 src/pyams_utils/session.py --- a/src/pyams_utils/session.py Wed Jul 12 13:55:47 2017 +0200 +++ b/src/pyams_utils/session.py Wed Jul 12 13:56:41 2017 +0200 @@ -18,6 +18,42 @@ # import interfaces # import packages +from pyams_utils.request import check_request + + +_marker = object() + + +def session_property(app, key=None, prefix=None): + """Define a method decorator used to store result into request's session + + If no request is currently running, a new one is created. + + :param str app: application identifier used to prefix session keys + :param str key: session's value key; if *None*, the key will be the method's object; if *key* is a callable + object, il will be called to get the actual session key + :param prefix: str; prefix to use for session key; if *None*, the prefix will be the property name + """ + + def session_decorator(func): + + def wrapper(obj, app, key, *args, **kwargs): + request = check_request() + if callable(key): + key = key(obj, *args, **kwargs) + if not key: + key = '{1}::{0!r}'.format(obj, prefix or func.__name__) + data = get_session_data(request, app, key, _marker) + if data is _marker: + data = func + if callable(data): + data = data(obj, *args, **kwargs) + set_session_data(request, app, key, data) + return data + + return lambda x, *args, **kwargs: wrapper(x, app, key, *args, **kwargs) + + return session_decorator def get_session_data(request, app, key, default=None): diff -r 2d076b2b485a -r 119b9c2f3022 src/pyams_utils/zodb.py --- a/src/pyams_utils/zodb.py Wed Jul 12 13:55:47 2017 +0200 +++ b/src/pyams_utils/zodb.py Wed Jul 12 13:56:41 2017 +0200 @@ -17,6 +17,7 @@ # import interfaces from persistent.interfaces import IPersistent +from pyams_utils.interfaces import ICacheKeyValue from pyams_utils.interfaces.site import IOptionalUtility from pyams_utils.interfaces.zeo import IZEOConnection from transaction.interfaces import ITransactionManager @@ -71,6 +72,11 @@ # recent spelling. +@adapter_config(context=object, provides=ICacheKeyValue) +def persistent_key_adapter(obj): + return '{0!r}'.format(obj) + + @implementer(IZEOConnection) class ZEOConnection(object): """ZEO connection object