schema.py
branchtls-sprint
changeset 1451 982e8616d9a2
parent 1398 5fe84a5f7035
child 1477 b056a49c16dc
--- a/schema.py	Thu Apr 23 16:10:17 2009 +0200
+++ b/schema.py	Thu Apr 23 16:11:30 2009 +0200
@@ -27,7 +27,7 @@
 # XXX <3.2 bw compat
 from yams import schema
 schema.use_py_datetime()
-nodes.use_py_datetime() 
+nodes.use_py_datetime()
 
 _ = unicode
 
@@ -43,7 +43,7 @@
 def bw_normalize_etype(etype):
     if etype in ETYPE_NAME_MAP:
         msg = '%s has been renamed to %s, please update your code' % (
-            etype, ETYPE_NAME_MAP[etype])            
+            etype, ETYPE_NAME_MAP[etype])
         warn(msg, DeprecationWarning, stacklevel=4)
         etype = ETYPE_NAME_MAP[etype]
     return etype
@@ -78,12 +78,12 @@
 class RichString(ybo.String):
     """Convenience RichString attribute type
     The follwing declaration::
-      
+
       class Card(EntityType):
           content = RichString(fulltextindexed=True, default_format='text/rest')
-          
+
     is equivalent to::
-      
+
       class Card(EntityType):
           content_format = String(meta=True, internationalizable=True,
                                  default='text/rest', constraints=[format_constraint])
@@ -106,7 +106,7 @@
                                     constraints=rdef.format_constraints)
         yams_add_relation(relations, format_attrdef, name+'_format', insertidx)
     yams_add_relation(relations, rdef, name, insertidx)
-    
+
 def display_name(req, key, form=''):
     """return a internationalized string for the key (schema entity or relation
     name) in a given form
@@ -258,19 +258,19 @@
             eid = getattr(edef, 'eid', None)
         self.eid = eid
         # take care: no _groups attribute when deep-copying
-        if getattr(self, '_groups', None): 
+        if getattr(self, '_groups', None):
             for groups in self._groups.itervalues():
                 for group_or_rqlexpr in groups:
                     if isinstance(group_or_rqlexpr, RRQLExpression):
                         msg = "can't use RRQLExpression on an entity type, use an ERQLExpression (%s)"
                         raise BadSchemaDefinition(msg % self.type)
-            
+
     def attribute_definitions(self):
         """return an iterator on attribute definitions
-        
+
         attribute relations are a subset of subject relations where the
         object's type is a final entity
-        
+
         an attribute definition is a 2-uple :
         * name of the relation
         * schema of the destination entity type
@@ -280,7 +280,7 @@
             if rschema.type == 'has_text':
                 continue
             yield rschema, attrschema
-            
+
     def add_subject_relation(self, rschema):
         """register the relation schema as possible subject relation"""
         super(CubicWebEntitySchema, self).add_subject_relation(rschema)
@@ -289,7 +289,7 @@
     def del_subject_relation(self, rtype):
         super(CubicWebEntitySchema, self).del_subject_relation(rtype)
         self._update_has_text(False)
-        
+
     def _update_has_text(self, need_has_text=None):
         may_need_has_text, has_has_text = False, False
         for rschema in self.subject_relations():
@@ -317,11 +317,11 @@
             self.schema.add_relation_def(rdef)
         elif not need_has_text and has_has_text:
             self.schema.del_relation_def(self.type, 'has_text', 'String')
-            
+
     def schema_entity(self):
         """return True if this entity type is used to build the schema"""
         return self.type in self.schema.schema_entity_types()
-    
+
     def check_perm(self, session, action, eid=None):
         # NB: session may be a server session or a request object
         user = session.user
@@ -337,17 +337,17 @@
         # else if there is some rql expressions, check them
         if any(rqlexpr.check(session, eid)
                for rqlexpr in self.get_rqlexprs(action)):
-            return        
+            return
         raise Unauthorized(action, str(self))
 
     def rql_expression(self, expression, mainvars=None, eid=None):
         """rql expression factory"""
         return ERQLExpression(expression, mainvars, eid)
-    
+
 class CubicWebRelationSchema(RelationSchema):
     RelationSchema._RPROPERTIES['eid'] = None
     _perms_checked = False
-    
+
     def __init__(self, schema=None, rdef=None, eid=None, **kwargs):
         if rdef is not None:
             # if this relation is inlined
@@ -356,8 +356,8 @@
         if eid is None and rdef is not None:
             eid = getattr(rdef, 'eid', None)
         self.eid = eid
-                    
-        
+
+
     def update(self, subjschema, objschema, rdef):
         super(CubicWebRelationSchema, self).update(subjschema, objschema, rdef)
         if not self._perms_checked and self._groups:
@@ -377,7 +377,7 @@
                             newrqlexprs.append(ERQLExpression(rqlexpr.expression,
                                                               rqlexpr.mainvars,
                                                               rqlexpr.eid))
-                            self.set_rqlexprs(action, newrqlexprs) 
+                            self.set_rqlexprs(action, newrqlexprs)
                         else:
                             msg = "can't use RRQLExpression on a final relation "\
                                   "type (eg attribute relation), use an ERQLExpression (%s)"
@@ -388,16 +388,16 @@
                               "a RRQLExpression (%s)"
                         raise BadSchemaDefinition(msg % self.type)
             self._perms_checked = True
-            
+
     def cardinality(self, subjtype, objtype, target):
         card = self.rproperty(subjtype, objtype, 'cardinality')
         return (target == 'subject' and card[0]) or \
                (target == 'object' and card[1])
-    
+
     def schema_relation(self):
         return self.type in ('relation_type', 'from_entity', 'to_entity',
                              'constrained_by', 'cstrtype')
-    
+
     def physical_mode(self):
         """return an appropriate mode for physical storage of this relation type:
         * 'subjectinline' if every possible subject cardinalities are 1 or ?
@@ -413,7 +413,7 @@
         # in an allowed group, if so that's enough internal sessions should
         # always stop there
         if session.user.matching_groups(self.get_groups(action)):
-            return 
+            return
         # else if there is some rql expressions, check them
         if any(rqlexpr.check(session, *args, **kwargs)
                for rqlexpr in self.get_rqlexprs(action)):
@@ -426,7 +426,7 @@
             return ERQLExpression(expression, mainvars, eid)
         return RRQLExpression(expression, mainvars, eid)
 
-    
+
 class CubicWebSchema(Schema):
     """set of entities and relations schema defining the possible data sets
     used in an application
@@ -434,11 +434,11 @@
 
     :type name: str
     :ivar name: name of the schema, usually the application identifier
-    
+
     :type base: str
     :ivar base: path of the directory where the schema is defined
     """
-    reading_from_database = False    
+    reading_from_database = False
     entity_class = CubicWebEntitySchema
     relation_class = CubicWebRelationSchema
 
@@ -455,7 +455,7 @@
         rschema = self.add_relation_type(ybo.RelationType('identity', meta=True))
         rschema.final = False
         rschema.set_default_groups()
-        
+
     def schema_entity_types(self):
         """return the list of entity types used to build the schema"""
         return frozenset(('CWEType', 'CWRType', 'CWAttribute', 'CWRelation',
@@ -463,7 +463,7 @@
                           # XXX those are not really "schema" entity types
                           #     but we usually don't want them as @* targets
                           'CWProperty', 'CWPermission', 'State', 'Transition'))
-        
+
     def add_entity_type(self, edef):
         edef.name = edef.name.encode()
         edef.name = bw_normalize_etype(edef.name)
@@ -478,13 +478,13 @@
             self.add_relation_def(rdef)
         self._eid_index[eschema.eid] = eschema
         return eschema
-        
+
     def add_relation_type(self, rdef):
         rdef.name = rdef.name.lower().encode()
         rschema = super(CubicWebSchema, self).add_relation_type(rdef)
         self._eid_index[rschema.eid] = rschema
         return rschema
-    
+
     def add_relation_def(self, rdef):
         """build a part of a relation schema
         (i.e. add a relation between two specific entity's types)
@@ -511,18 +511,18 @@
                                              self.eschema(rdef.object))
             except AttributeError:
                 pass # not a serialized schema
-    
+
     def del_relation_type(self, rtype):
         rschema = self.rschema(rtype)
         self._eid_index.pop(rschema.eid, None)
         super(CubicWebSchema, self).del_relation_type(rtype)
-    
+
     def del_relation_def(self, subjtype, rtype, objtype):
         for k, v in self._eid_index.items():
             if v == (subjtype, rtype, objtype):
                 del self._eid_index[k]
         super(CubicWebSchema, self).del_relation_def(subjtype, rtype, objtype)
-        
+
     def del_entity_type(self, etype):
         eschema = self.eschema(etype)
         self._eid_index.pop(eschema.eid, None)
@@ -531,7 +531,7 @@
         if 'has_text' in eschema.subject_relations():
             self.del_relation_def(etype, 'has_text', 'String')
         super(CubicWebSchema, self).del_entity_type(etype)
-        
+
     def schema_by_eid(self, eid):
         return self._eid_index[eid]
 
@@ -543,22 +543,22 @@
 
     limit the proposed values to a set of entities returned by a rql query,
     but this is not enforced at the repository level
-    
+
      restriction is additional rql restriction that will be added to
      a predefined query, where the S and O variables respectivly represent
      the subject and the object of the relation
     """
-    
+
     def __init__(self, restriction):
         self.restriction = restriction
 
     def serialize(self):
         return self.restriction
-    
+
     def deserialize(cls, value):
         return cls(value)
     deserialize = classmethod(deserialize)
-    
+
     def check(self, entity, rtype, value):
         """return true if the value satisfy the constraint, else false"""
         # implemented as a hook in the repository
@@ -568,7 +568,7 @@
         """raise ValidationError if the relation doesn't satisfy the constraint
         """
         pass # this is a vocabulary constraint, not enforce
-    
+
     def __str__(self):
         return self.restriction
 
@@ -586,7 +586,7 @@
                                       ('s', 'o'), build_descr=False)
     def error(self, eid, rtype, msg):
         raise ValidationError(eid, {rtype: msg})
-        
+
     def repo_check(self, session, eidfrom, rtype, eidto):
         """raise ValidationError if the relation doesn't satisfy the constraint
         """
@@ -608,12 +608,12 @@
             #     eidfrom or eidto (from user interface point of view)
             self.error(eidfrom, rtype, 'unique constraint %s failed' % self)
 
-    
+
 def split_expression(rqlstring):
     for expr in rqlstring.split(','):
         for word in expr.split():
             yield word
-            
+
 def normalize_expression(rqlstring):
     """normalize an rql expression to ease schema synchronization (avoid
     suppressing and reinserting an expression if only a space has been added/removed
@@ -637,19 +637,19 @@
             if len(self.rqlst.defined_vars[mainvar].references()) <= 2:
                 LOGGER.warn('You did not use the %s variable in your RQL expression %s',
                             mainvar, self)
-    
+
     def __str__(self):
         return self.full_rql
     def __repr__(self):
         return '%s(%s)' % (self.__class__.__name__, self.full_rql)
-        
+
     def __deepcopy__(self, memo):
         return self.__class__(self.expression, self.mainvars)
     def __getstate__(self):
         return (self.expression, self.mainvars)
     def __setstate__(self, state):
         self.__init__(*state)
-        
+
     @cached
     def transform_has_permission(self):
         found = None
@@ -693,7 +693,7 @@
             rqlst.recover()
             return rql, found, keyarg
         return rqlst.as_string(), None, None
-        
+
     def _check(self, session, **kwargs):
         """return True if the rql expression is matching the given relation
         between fromeid and toeid
@@ -753,7 +753,7 @@
         if self.eid is not None:
             session.local_perm_cache[key] = False
         return False
-    
+
     @property
     def minimal_rql(self):
         return 'Any %s WHERE %s' % (self.mainvars, self.expression)
@@ -778,16 +778,16 @@
         if 'U' in defined:
             rql += ', U eid %(u)s'
         return rql
-    
+
     def check(self, session, eid=None):
         if 'X' in self.rqlst.defined_vars:
             if eid is None:
                 return False
             return self._check(session, x=eid)
         return self._check(session)
-    
+
 PyFileReader.context['ERQLExpression'] = ERQLExpression
-        
+
 class RRQLExpression(RQLExpression):
     def __init__(self, expression, mainvars=None, eid=None):
         if mainvars is None:
@@ -817,7 +817,7 @@
         if 'U' in defined:
             rql += ', U eid %(u)s'
         return rql
-    
+
     def check(self, session, fromeid=None, toeid=None):
         kwargs = {}
         if 'S' in self.rqlst.defined_vars:
@@ -829,7 +829,7 @@
                 return False
             kwargs['o'] = toeid
         return self._check(session, **kwargs)
-        
+
 PyFileReader.context['RRQLExpression'] = RRQLExpression
 
 # workflow extensions #########################################################
@@ -857,7 +857,7 @@
 class WorkflowableEntityType(ybo.EntityType):
     __metaclass__ = workflowable_definition
     abstract = True
-    
+
 PyFileReader.context['WorkflowableEntityType'] = WorkflowableEntityType
 
 # schema loading ##############################################################
@@ -866,7 +866,7 @@
     """cubicweb specific relation file reader, handling additional RQL
     constraints on a relation definition
     """
-    
+
     def handle_constraint(self, rdef, constraint_text):
         """arbitrary constraint is an rql expression for cubicweb"""
         if not rdef.constraints:
@@ -878,7 +878,7 @@
             rdef.inlined = True
         RelationFileReader.process_properties(self, rdef, relation_def)
 
-        
+
 CONSTRAINTS['RQLConstraint'] = RQLConstraint
 CONSTRAINTS['RQLUniqueConstraint'] = RQLUniqueConstraint
 CONSTRAINTS['RQLVocabularyConstraint'] = RQLVocabularyConstraint
@@ -900,13 +900,13 @@
         self.lib_directory = config.schemas_lib_dir()
         return super(BootstrapSchemaLoader, self).load(
             path, config.appid, register_base_types=False, **kwargs)
-    
+
     def _load_definition_files(self, cubes=None):
         # bootstraping, ignore cubes
         for filepath in self.include_schema_files('bootstrap'):
             self.info('loading %s', filepath)
             self.handle_file(filepath)
-        
+
     def unhandled_file(self, filepath):
         """called when a file without handler associated has been found"""
         self.warning('ignoring file %r', filepath)
@@ -948,14 +948,14 @@
 
 class FormatConstraint(StaticVocabularyConstraint):
     need_perm_formats = [_('text/cubicweb-page-template')]
-        
+
     regular_formats = (_('text/rest'),
                        _('text/html'),
                        _('text/plain'),
                        )
     def __init__(self):
         pass
-    
+
     def serialize(self):
         """called to make persistent valuable data of a constraint"""
         return None
@@ -966,18 +966,18 @@
         a `cls` instance
         """
         return cls()
-    
+
     def vocabulary(self, entity=None, req=None):
         if req is None and entity is not None:
             req = entity.req
         if req is not None and req.user.has_permission(PERM_USE_TEMPLATE_FORMAT):
             return self.regular_formats + tuple(self.need_perm_formats)
         return self.regular_formats
-    
+
     def __str__(self):
         return 'value in (%s)' % u', '.join(repr(unicode(word)) for word in self.vocabulary())
-    
-    
+
+
 format_constraint = FormatConstraint()
 CONSTRAINTS['FormatConstraint'] = FormatConstraint
 PyFileReader.context['format_constraint'] = format_constraint
@@ -992,7 +992,7 @@
 def bw_import_erschema(self, ertype, schemamod=None, instantiate=True):
     return orig_import_erschema(self, bw_normalize_etype(ertype), schemamod, instantiate)
 PyFileReader.import_erschema = bw_import_erschema
-    
+
 # XXX itou for some Statement methods
 from rql import stmts
 orig_get_etype = stmts.ScopeNode.get_etype