cwvreg.py
branchtls-sprint
changeset 1475 5c1ec97f317e
parent 1357 e5a97779c7fc
child 1477 b056a49c16dc
equal deleted inserted replaced
1474:716f0742ee7f 1475:5c1ec97f317e
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     6 """
     6 """
     7 __docformat__ = "restructuredtext en"
     7 __docformat__ = "restructuredtext en"
     8 
     8 
     9 from logilab.common.decorators import cached, clear_cache
     9 from logilab.common.decorators import cached, clear_cache
    10 from logilab.common.interface import extend
       
    11 
    10 
    12 from rql import RQLHelper
    11 from rql import RQLHelper
    13 
    12 
    14 from cubicweb import Binary, UnknownProperty, UnknownEid
    13 from cubicweb import Binary, UnknownProperty, UnknownEid
    15 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
    14 from cubicweb.vregistry import VRegistry, ObjectNotFound, NoSelectableObject
    21     selectors, with a bw compat fallback to accepts_interfaces attribute
    20     selectors, with a bw compat fallback to accepts_interfaces attribute
    22     """
    21     """
    23     from cubicweb.selectors import implements
    22     from cubicweb.selectors import implements
    24     try:
    23     try:
    25         # XXX deprecated
    24         # XXX deprecated
    26         return sorted(obj.accepts_interfaces) 
    25         return sorted(obj.accepts_interfaces)
    27     except AttributeError:
    26     except AttributeError:
    28         try:
    27         try:
    29             impl = obj.__select__.search_selector(implements)
    28             impl = obj.__select__.search_selector(implements)
    30             if impl:
    29             if impl:
    31                 return sorted(impl.expected_ifaces)
    30                 return sorted(impl.expected_ifaces)
    37         return ()
    36         return ()
    38 
    37 
    39 
    38 
    40 class CubicWebRegistry(VRegistry):
    39 class CubicWebRegistry(VRegistry):
    41     """extend the generic VRegistry with some cubicweb specific stuff"""
    40     """extend the generic VRegistry with some cubicweb specific stuff"""
    42     
    41 
    43     def __init__(self, config, debug=None, initlog=True):
    42     def __init__(self, config, debug=None, initlog=True):
    44         if initlog:
    43         if initlog:
    45             # first init log service
    44             # first init log service
    46             config.init_log(debug=debug)
    45             config.init_log(debug=debug)
    47         super(CubicWebRegistry, self).__init__(config)
    46         super(CubicWebRegistry, self).__init__(config)
    48         self.schema = None
    47         self.schema = None
    49         self.reset()
    48         self.reset()
    50         self.initialized = False
    49         self.initialized = False
    51         
    50 
    52     def items(self):
    51     def items(self):
    53         return [item for item in self._registries.items()
    52         return [item for item in self._registries.items()
    54                 if not item[0] in ('propertydefs', 'propertyvalues')]
    53                 if not item[0] in ('propertydefs', 'propertyvalues')]
    55 
    54 
    56     def values(self):
    55     def values(self):
    57         return [value for key, value in self._registries.items()
    56         return [value for key, value in self._registries.items()
    58                 if not key in ('propertydefs', 'propertyvalues')]
    57                 if not key in ('propertydefs', 'propertyvalues')]
    59     
    58 
    60     def reset(self):
    59     def reset(self):
    61         self._registries = {}
    60         self._registries = {}
    62         self._lastmodifs = {}
    61         self._lastmodifs = {}
    63         self._needs_iface = {}
    62         self._needs_iface = {}
    64         # two special registries, propertydefs which care all the property
    63         # two special registries, propertydefs which care all the property
    66         # properties
    65         # properties
    67         self._registries['propertydefs'] = {}
    66         self._registries['propertydefs'] = {}
    68         self._registries['propertyvalues'] = self.eprop_values = {}
    67         self._registries['propertyvalues'] = self.eprop_values = {}
    69         for key, propdef in self.config.eproperty_definitions():
    68         for key, propdef in self.config.eproperty_definitions():
    70             self.register_property(key, **propdef)
    69             self.register_property(key, **propdef)
    71             
    70 
    72     def set_schema(self, schema):
    71     def set_schema(self, schema):
    73         """set application'schema and load application objects"""
    72         """set application'schema and load application objects"""
    74         self.schema = schema
    73         self.schema = schema
    75         clear_cache(self, 'rqlhelper')
    74         clear_cache(self, 'rqlhelper')
    76         # now we can load application's web objects
    75         # now we can load application's web objects
    77         self.register_objects(self.config.vregistry_path())
    76         self.register_objects(self.config.vregistry_path())
    78         
    77 
    79     def update_schema(self, schema):
    78     def update_schema(self, schema):
    80         """update .schema attribute on registered objects, necessary for some
    79         """update .schema attribute on registered objects, necessary for some
    81         tests
    80         tests
    82         """
    81         """
    83         self.schema = schema
    82         self.schema = schema
   108         super(CubicWebRegistry, self).register(obj, **kwargs)
   107         super(CubicWebRegistry, self).register(obj, **kwargs)
   109         # XXX bw compat
   108         # XXX bw compat
   110         ifaces = use_interfaces(obj)
   109         ifaces = use_interfaces(obj)
   111         if ifaces:
   110         if ifaces:
   112             self._needs_iface[obj] = ifaces
   111             self._needs_iface[obj] = ifaces
   113         
   112 
   114     def register_objects(self, path, force_reload=None):
   113     def register_objects(self, path, force_reload=None):
   115         """overriden to remove objects requiring a missing interface"""
   114         """overriden to remove objects requiring a missing interface"""
   116         if super(CubicWebRegistry, self).register_objects(path, force_reload):
   115         if super(CubicWebRegistry, self).register_objects(path, force_reload):
   117             # clear etype cache if you don't want to run into deep weirdness
   116             # clear etype cache if you don't want to run into deep weirdness
   118             clear_cache(self, 'etype_class')
   117             clear_cache(self, 'etype_class')
   145                 self.debug('available in registry %s: %s', registry,
   144                 self.debug('available in registry %s: %s', registry,
   146                            sorted(objects))
   145                            sorted(objects))
   147                 for appobjects in objects.itervalues():
   146                 for appobjects in objects.itervalues():
   148                     for appobject in appobjects:
   147                     for appobject in appobjects:
   149                         appobject.vreg_initialization_completed()
   148                         appobject.vreg_initialization_completed()
   150     
   149 
   151     @cached
   150     @cached
   152     def etype_class(self, etype):
   151     def etype_class(self, etype):
   153         """return an entity class for the given entity type.
   152         """return an entity class for the given entity type.
   154         Try to find out a specific class for this kind of entity or
   153         Try to find out a specific class for this kind of entity or
   155         default to a dump of the class registered for 'Any'
   154         default to a dump of the class registered for 'Any'
   170                 pass
   169                 pass
   171         else:
   170         else:
   172             # no entity class for any of the ancestors, fallback to the default
   171             # no entity class for any of the ancestors, fallback to the default
   173             # one
   172             # one
   174             cls = self.select(self.registry_objects('etypes', 'Any'), etype)
   173             cls = self.select(self.registry_objects('etypes', 'Any'), etype)
   175         # add class itself to the list of implemented interfaces, as well as the
       
   176         # Any entity class so we can select according to class using the
       
   177         # `implements` selector
       
   178         extend(cls, cls)
       
   179         extend(cls, self.etype_class('Any'))
       
   180         return cls
   174         return cls
   181     
   175 
   182     def render(self, registry, oid, req, **context):
   176     def render(self, registry, oid, req, **context):
   183         """select an object in a given registry and render it
   177         """select an object in a given registry and render it
   184 
   178 
   185         - registry: the registry's name
   179         - registry: the registry's name
   186         - oid : the view to call
   180         - oid : the view to call
   187         - req : the HTTP request         
   181         - req : the HTTP request
   188         """
   182         """
   189         objclss = self.registry_objects(registry, oid)
   183         objclss = self.registry_objects(registry, oid)
   190         try:
   184         try:
   191             rset = context.pop('rset')
   185             rset = context.pop('rset')
   192         except KeyError:
   186         except KeyError:
   193             rset = None
   187             rset = None
   194         selected = self.select(objclss, req, rset, **context)
   188         selected = self.select(objclss, req, rset, **context)
   195         return selected.dispatch(**context)
   189         return selected.dispatch(**context)
   196         
   190 
   197     def main_template(self, req, oid='main-template', **context):
   191     def main_template(self, req, oid='main-template', **context):
   198         """display query by calling the given template (default to main),
   192         """display query by calling the given template (default to main),
   199         and returning the output as a string instead of requiring the [w]rite
   193         and returning the output as a string instead of requiring the [w]rite
   200         method as argument
   194         method as argument
   201         """
   195         """
   211         visualizable objects)
   205         visualizable objects)
   212         """
   206         """
   213         return [x for x in sorted(self.possible_objects(registry, *args, **kwargs),
   207         return [x for x in sorted(self.possible_objects(registry, *args, **kwargs),
   214                                   key=lambda x: x.propval('order'))
   208                                   key=lambda x: x.propval('order'))
   215                 if x.propval('visible')]
   209                 if x.propval('visible')]
   216         
   210 
   217     def possible_actions(self, req, rset, **kwargs):
   211     def possible_actions(self, req, rset, **kwargs):
   218         if rset is None:
   212         if rset is None:
   219             actions = self.possible_vobjects('actions', req, rset)
   213             actions = self.possible_vobjects('actions', req, rset)
   220         else:
   214         else:
   221             actions = rset.possible_actions() # cached implementation
   215             actions = rset.possible_actions() # cached implementation
   222         result = {}
   216         result = {}
   223         for action in actions:
   217         for action in actions:
   224             result.setdefault(action.category, []).append(action)
   218             result.setdefault(action.category, []).append(action)
   225         return result
   219         return result
   226         
   220 
   227     def possible_views(self, req, rset, **kwargs):
   221     def possible_views(self, req, rset, **kwargs):
   228         """return an iterator on possible views for this result set
   222         """return an iterator on possible views for this result set
   229 
   223 
   230         views returned are classes, not instances
   224         views returned are classes, not instances
   231         """
   225         """
   239             except NoSelectableObject:
   233             except NoSelectableObject:
   240                 continue
   234                 continue
   241             except Exception:
   235             except Exception:
   242                 self.exception('error while trying to list possible %s views for %s',
   236                 self.exception('error while trying to list possible %s views for %s',
   243                                vid, rset)
   237                                vid, rset)
   244                 
   238 
   245     def select_box(self, oid, *args, **kwargs):
   239     def select_box(self, oid, *args, **kwargs):
   246         """return the most specific view according to the result set"""
   240         """return the most specific view according to the result set"""
   247         try:
   241         try:
   248             return self.select_object('boxes', oid, *args, **kwargs)
   242             return self.select_object('boxes', oid, *args, **kwargs)
   249         except NoSelectableObject:
   243         except NoSelectableObject:
   253         """return the most specific view according to the result set"""
   247         """return the most specific view according to the result set"""
   254         try:
   248         try:
   255             return self.select_object('actions', oid, *args, **kwargs)
   249             return self.select_object('actions', oid, *args, **kwargs)
   256         except NoSelectableObject:
   250         except NoSelectableObject:
   257             return
   251             return
   258     
   252 
   259     def select_component(self, cid, *args, **kwargs):
   253     def select_component(self, cid, *args, **kwargs):
   260         """return the most specific component according to the result set"""
   254         """return the most specific component according to the result set"""
   261         try:
   255         try:
   262             return self.select_object('components', cid, *args, **kwargs)
   256             return self.select_object('components', cid, *args, **kwargs)
   263         except (NoSelectableObject, ObjectNotFound):
   257         except (NoSelectableObject, ObjectNotFound):
   266     def select_view(self, __vid, req, rset, **kwargs):
   260     def select_view(self, __vid, req, rset, **kwargs):
   267         """return the most specific view according to the result set"""
   261         """return the most specific view according to the result set"""
   268         views = self.registry_objects('views', __vid)
   262         views = self.registry_objects('views', __vid)
   269         return self.select(views, req, rset, **kwargs)
   263         return self.select(views, req, rset, **kwargs)
   270 
   264 
   271     
   265 
   272     # properties handling #####################################################
   266     # properties handling #####################################################
   273 
   267 
   274     def user_property_keys(self, withsitewide=False):
   268     def user_property_keys(self, withsitewide=False):
   275         if withsitewide:
   269         if withsitewide:
   276             return sorted(self['propertydefs'])
   270             return sorted(self['propertydefs'])
   280     def register_property(self, key, type, help, default=None, vocabulary=None,
   274     def register_property(self, key, type, help, default=None, vocabulary=None,
   281                           sitewide=False):
   275                           sitewide=False):
   282         """register a given property"""
   276         """register a given property"""
   283         properties = self._registries['propertydefs']
   277         properties = self._registries['propertydefs']
   284         assert type in YAMS_TO_PY
   278         assert type in YAMS_TO_PY
   285         properties[key] = {'type': type, 'vocabulary': vocabulary, 
   279         properties[key] = {'type': type, 'vocabulary': vocabulary,
   286                            'default': default, 'help': help,
   280                            'default': default, 'help': help,
   287                            'sitewide': sitewide}
   281                            'sitewide': sitewide}
   288 
   282 
   289     def property_info(self, key):
   283     def property_info(self, key):
   290         """return dictionary containing description associated to the given
   284         """return dictionary containing description associated to the given
   298                 soft = key.split('.')[-1]
   292                 soft = key.split('.')[-1]
   299                 return {'type': 'String', 'sitewide': True,
   293                 return {'type': 'String', 'sitewide': True,
   300                         'default': None, 'vocabulary': None,
   294                         'default': None, 'vocabulary': None,
   301                         'help': _('%s software version of the database') % soft}
   295                         'help': _('%s software version of the database') % soft}
   302             raise UnknownProperty('unregistered property %r' % key)
   296             raise UnknownProperty('unregistered property %r' % key)
   303             
   297 
   304     def property_value(self, key):
   298     def property_value(self, key):
   305         try:
   299         try:
   306             return self._registries['propertyvalues'][key]
   300             return self._registries['propertyvalues'][key]
   307         except KeyError:
   301         except KeyError:
   308             return self._registries['propertydefs'][key]['default']
   302             return self._registries['propertydefs'][key]['default']
   321             if callable(vocab):
   315             if callable(vocab):
   322                 vocab = vocab(key, None) # XXX need a req object
   316                 vocab = vocab(key, None) # XXX need a req object
   323             if not value in vocab:
   317             if not value in vocab:
   324                 raise ValueError(_('unauthorized value'))
   318                 raise ValueError(_('unauthorized value'))
   325         return value
   319         return value
   326     
   320 
   327     def init_properties(self, propvalues):
   321     def init_properties(self, propvalues):
   328         """init the property values registry using the given set of couple (key, value)
   322         """init the property values registry using the given set of couple (key, value)
   329         """
   323         """
   330         self.initialized = True
   324         self.initialized = True
   331         values = self._registries['propertyvalues']
   325         values = self._registries['propertyvalues']
   381         for vobject in vobjects:
   375         for vobject in vobjects:
   382             vobject.vreg = self
   376             vobject.vreg = self
   383             vobject.schema = self.schema
   377             vobject.schema = self.schema
   384             vobject.config = self.config
   378             vobject.config = self.config
   385         return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs)
   379         return super(MulCnxCubicWebRegistry, self).select(vobjects, *args, **kwargs)
   386     
   380 
   387 from datetime import datetime, date, time, timedelta
   381 from datetime import datetime, date, time, timedelta
   388 
   382 
   389 YAMS_TO_PY = {
   383 YAMS_TO_PY = {
   390     'Boolean':  bool,
   384     'Boolean':  bool,
   391     'String' :  unicode,
   385     'String' :  unicode,