src/pyams_viewlet/viewlet.py
changeset 47 df022d00a9c4
parent 41 9bbb78a1efa9
child 54 6e63f435d731
equal deleted inserted replaced
46:ed2dc23f7f99 47:df022d00a9c4
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
     9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
    10 # FOR A PARTICULAR PURPOSE.
    10 # FOR A PARTICULAR PURPOSE.
    11 #
    11 #
    12 
    12 
    13 __docformat__ = 'restructuredtext'
    13 """PyAMS_viewlet.viewlet module
       
    14 
       
    15 This module provides base content providers and viewlets classes, as well as a decorators
       
    16 which can be used instead of ZCML declarations to register content providers and viewlets.
       
    17 """
    14 
    18 
    15 import logging
    19 import logging
    16 
    20 
    17 import venusian
    21 import venusian
    18 from pyramid.exceptions import ConfigurationError
    22 from pyramid.exceptions import ConfigurationError
    21 from zope.interface import Interface, implementer
    25 from zope.interface import Interface, implementer
    22 
    26 
    23 from pyams_template.template import get_view_template
    27 from pyams_template.template import get_view_template
    24 from pyams_viewlet.interfaces import IViewlet, IViewletManager
    28 from pyams_viewlet.interfaces import IViewlet, IViewletManager
    25 
    29 
    26 
    30 __docformat__ = 'restructuredtext'
    27 logger = logging.getLogger('PyAMS (viewlet)')
    31 
       
    32 
       
    33 LOGGER = logging.getLogger('PyAMS (viewlet)')
    28 
    34 
    29 
    35 
    30 @implementer(IContentProvider)
    36 @implementer(IContentProvider)
    31 class EmptyContentProvider(object):
    37 class EmptyContentProvider:
    32     """Empty content provider base class"""
    38     """Empty content provider base class"""
    33 
    39 
    34     permission = None
    40     permission = None
    35 
    41 
    36     def __init__(self, context, request):
    42     def __init__(self, context, request):
    37         self.context = context
    43         self.context = context
    38         self.request = request
    44         self.request = request
    39 
    45 
    40     def __call__(self):
    46     def __call__(self):
    41         if self.permission and not self.request.has_permission(self.permission, context=self.context):
    47         if self.permission and not self.request.has_permission(self.permission,
       
    48                                                                context=self.context):
    42             return ''
    49             return ''
    43         self.update()
    50         self.update()
    44         return self.render()
    51         return self.render()
    45 
    52 
    46     def update(self):
    53     def update(self):
    47         pass
    54         """See `IContentProvider` interface"""
    48 
    55 
    49     def render(self):
    56     def render(self):  # pylint: disable=no-self-use
       
    57         """See `IContentProvider` interface"""
    50         return ''
    58         return ''
    51 
    59 
    52 
    60 
    53 class BaseContentProvider(EmptyContentProvider):
    61 class BaseContentProvider(EmptyContentProvider):
    54     """Base template based content provider"""
    62     """Base template based content provider"""
    69     def __init__(self, context, request, view):
    77     def __init__(self, context, request, view):
    70         super(ViewContentProvider, self).__init__(context, request)
    78         super(ViewContentProvider, self).__init__(context, request)
    71         self.view = self.__parent__ = view
    79         self.view = self.__parent__ = view
    72 
    80 
    73 
    81 
    74 class contentprovider_config(object):
    82 class contentprovider_config:  # pylint: disable=invalid-name
    75     """Class decorator used to declare a content provider
    83     """Class decorator used to declare a content provider
    76 
    84 
    77     You can provide same arguments as in 'viewlet' ZCML directive:
    85     You can provide same arguments as in 'viewlet' ZCML directive:
    78     @name = name of the viewlet; may be unique for a given viewlet manager
    86     @name = name of the viewlet; may be unique for a given viewlet manager
    79     @view = the view class or interface for which viewlet is displayed
    87     @view = the view class or interface for which viewlet is displayed
    93         self.__dict__.update(settings)
   101         self.__dict__.update(settings)
    94 
   102 
    95     def __call__(self, wrapped):
   103     def __call__(self, wrapped):
    96         settings = self.__dict__.copy()
   104         settings = self.__dict__.copy()
    97 
   105 
    98         def callback(context, name, ob):
   106         def callback(context, name, obj):  # pylint: disable=unused-argument
    99             cdict = {
   107             cdict = {
   100                 '__name__': settings.get('name'),
   108                 '__name__': settings.get('name'),
   101                 '__module__': ob.__module__
   109                 '__module__': obj.__module__
   102             }
   110             }
   103             if 'permission' in settings:
   111             if 'permission' in settings:
   104                 settings['permission'] = settings.get('permission')
   112                 settings['permission'] = settings.get('permission')
   105 
   113 
   106             bases = (ob,)
   114             bases = (obj,)
   107             if not IContentProvider.implementedBy(ob):
   115             if not IContentProvider.implementedBy(obj):  # pylint: disable=no-value-for-parameter
   108                 bases = bases + (ViewContentProvider,)
   116                 bases = bases + (ViewContentProvider,)
   109             new_class = type('<ViewContentProvider %s>' % settings.get('name'), bases, cdict)
   117             new_class = type('<ViewContentProvider %s>' % settings.get('name'), bases, cdict)
   110 
   118 
   111             logger.debug("Registering content provider {0} ({1})".format(settings.get('name'),
   119             LOGGER.debug("Registering content provider {0} ({1})".format(settings.get('name'),
   112                                                                          str(new_class)))
   120                                                                          str(new_class)))
   113             config = context.config.with_package(info.module)
   121             config = context.config.with_package(info.module)  # pylint: disable=no-member
   114             config.registry.registerAdapter(new_class,
   122             config.registry.registerAdapter(new_class,
   115                                             (settings.get('context', Interface),
   123                                             (settings.get('context', Interface),
   116                                              settings.get('layer', IRequest),
   124                                              settings.get('layer', IRequest),
   117                                              settings.get('view', IView)),
   125                                              settings.get('view', IView)),
   118                                             IContentProvider, settings.get('name'))
   126                                             IContentProvider, settings.get('name'))
   119 
   127 
   120         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
   128         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
   121 
   129 
   122         if info.scope == 'class':
   130         if info.scope == 'class':  # pylint: disable=no-member
   123             # if the decorator was attached to a method in a class, or
   131             # if the decorator was attached to a method in a class, or
   124             # otherwise executed at class scope, we need to set an
   132             # otherwise executed at class scope, we need to set an
   125             # 'attr' into the settings if one isn't already in there
   133             # 'attr' into the settings if one isn't already in there
   126             if settings.get('attr') is None:
   134             if settings.get('attr') is None:
   127                 settings['attr'] = wrapped.__name__
   135                 settings['attr'] = wrapped.__name__
   128 
   136 
   129         settings['_info'] = info.codeinfo  # fbo "action_method"
   137         settings['_info'] = info.codeinfo  # pylint: disable=no-member
   130         return wrapped
   138         return wrapped
   131 
   139 
   132 
   140 
   133 @implementer(IViewlet)
   141 @implementer(IViewlet)
   134 class EmptyViewlet(object):
   142 class EmptyViewlet:
   135     """Empty viewlet base class"""
   143     """Empty viewlet base class"""
   136 
   144 
   137     permission = None
   145     permission = None
   138 
   146 
   139     def __init__(self, context, request, view, manager):
   147     def __init__(self, context, request, view, manager):
   141         self.request = request
   149         self.request = request
   142         self.__parent__ = view
   150         self.__parent__ = view
   143         self.manager = manager
   151         self.manager = manager
   144 
   152 
   145     def update(self):
   153     def update(self):
   146         pass
   154         """See `IContentProvider` interface"""
   147 
   155 
   148     def render(self):
   156     def render(self):  # pylint: disable=no-self-use
       
   157         """See `IContentProvider` interface"""
   149         return ''
   158         return ''
   150 
   159 
   151 
   160 
   152 class Viewlet(EmptyViewlet):
   161 class Viewlet(EmptyViewlet):
   153     """Viewlet adapter class used in meta directive as a mixin class."""
   162     """Viewlet adapter class used in meta directive as a mixin class."""
   154 
   163 
   155     render = get_view_template()
   164     render = get_view_template()
   156 
   165 
   157 
   166 
   158 class viewlet_config(object):
   167 class viewlet_config:  # pylint: disable=invalid-name
   159     """Class decorator used to declare a viewlet
   168     """Class decorator used to declare a viewlet
   160 
   169 
   161     You can provide same arguments as in 'viewlet' ZCML directive:
   170     You can provide same arguments as in 'viewlet' ZCML directive:
   162     @name = name of the viewlet; may be unique for a given viewlet manager
   171     @name = name of the viewlet; may be unique for a given viewlet manager
   163     @manager = manager class or interface holding the viewlet
   172     @manager = manager class or interface holding the viewlet
   179         self.__dict__.update(settings)
   188         self.__dict__.update(settings)
   180 
   189 
   181     def __call__(self, wrapped):
   190     def __call__(self, wrapped):
   182         settings = self.__dict__.copy()
   191         settings = self.__dict__.copy()
   183 
   192 
   184         def callback(context, name, ob):
   193         def callback(context, name, obj):  # pylint: disable=unused-argument
   185             cdict = {
   194             cdict = {
   186                 '__name__': settings.get('name'),
   195                 '__name__': settings.get('name'),
   187                 '__module__': ob.__module__
   196                 '__module__': obj.__module__
   188             }
   197             }
   189             if 'permission' in settings:
   198             if 'permission' in settings:
   190                 cdict['permission'] = settings.get('permission')
   199                 cdict['permission'] = settings.get('permission')
   191             if 'weight' in settings:
   200             if 'weight' in settings:
   192                 cdict['weight'] = settings.get('weight')
   201                 cdict['weight'] = settings.get('weight')
   193 
   202 
   194             bases = (ob,)
   203             bases = (obj,)
   195             if not IViewlet.implementedBy(ob):
   204             if not IViewlet.implementedBy(obj):  # pylint: disable=no-value-for-parameter
   196                 bases = bases + (Viewlet,)
   205                 bases = bases + (Viewlet,)
   197             new_class = type('<Viewlet %s>' % settings.get('name'), bases, cdict)
   206             new_class = type('<Viewlet %s>' % settings.get('name'), bases, cdict)
   198 
   207 
   199             logger.debug("Registering viewlet {0} ({1})".format(settings.get('name'),
   208             LOGGER.debug("Registering viewlet {0} ({1})".format(settings.get('name'),
   200                                                                 str(new_class)))
   209                                                                 str(new_class)))
   201             config = context.config.with_package(info.module)
   210             config = context.config.with_package(info.module)  # pylint: disable=no-member
   202             config.registry.registerAdapter(new_class,
   211             config.registry.registerAdapter(new_class,
   203                                             (settings.get('context', Interface),
   212                                             (settings.get('context', Interface),
   204                                              settings.get('layer', IRequest),
   213                                              settings.get('layer', IRequest),
   205                                              settings.get('view', IView),
   214                                              settings.get('view', IView),
   206                                              settings.get('manager', IViewletManager)),
   215                                              settings.get('manager', IViewletManager)),
   207                                             IViewlet, settings.get('name'))
   216                                             IViewlet, settings.get('name'))
   208 
   217 
   209         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
   218         info = self.venusian.attach(wrapped, callback, category='pyams_viewlet')
   210 
   219 
   211         if info.scope == 'class':
   220         if info.scope == 'class':  # pylint: disable=no-member
   212             # if the decorator was attached to a method in a class, or
   221             # if the decorator was attached to a method in a class, or
   213             # otherwise executed at class scope, we need to set an
   222             # otherwise executed at class scope, we need to set an
   214             # 'attr' into the settings if one isn't already in there
   223             # 'attr' into the settings if one isn't already in there
   215             if settings.get('attr') is None:
   224             if settings.get('attr') is None:
   216                 settings['attr'] = wrapped.__name__
   225                 settings['attr'] = wrapped.__name__
   217 
   226 
   218         settings['_info'] = info.codeinfo  # fbo "action_method"
   227         settings['_info'] = info.codeinfo  # pylint: disable=no-member
   219         return wrapped
   228         return wrapped