rtags.py
brancholdstable
changeset 4985 02b52bf9f5f8
parent 4931 92c9d0a5dc11
child 5314 86e5abbebfaf
equal deleted inserted replaced
4563:c25da7573ebd 4985:02b52bf9f5f8
     1 """relation tags store
     1 #:organization: Logilab
     2 
     2 #:copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     3 :organization: Logilab
     3 #:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     4 :copyright: 2001-2010 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.
     4 #:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
     5 
     6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses
     6 """
       
     7 A RelationTag object is an object which allows to link a configuration information to a relation definition. For instance, the standard primary view uses a RelationTag object (uicfg.primaryview_section) to get the section to display relations.
       
     8 
       
     9 .. sourcecode:: python
       
    10 
       
    11    # display ``entry_of`` relations in the ``relations`` section in the ``BlogEntry`` primary view
       
    12    uicfg.primaryview_section.tag_subject_of(('BlogEntry', 'entry_of', '*'),
       
    13                                              'relations')
       
    14 
       
    15    # hide every relation ``entry_of`` in the ``Blog`` primary view
       
    16    uicfg.primaryview_section.tag_object_of(('*', 'entry_of', 'Blog'), 'hidden')
       
    17 
       
    18 Three primitives are defined:
       
    19    * ``tag_subject_of`` tag a relation in the subject's context
       
    20    * ``tag_object_of`` tag a relation in the object's context
       
    21    * ``tag_attribute`` shortcut for tag_subject_of
       
    22 
       
    23 
     7 """
    24 """
     8 __docformat__ = "restructuredtext en"
    25 __docformat__ = "restructuredtext en"
     9 
    26 
    10 import logging
    27 import logging
    11 
    28 
    13 
    30 
    14 RTAGS = []
    31 RTAGS = []
    15 def register_rtag(rtag):
    32 def register_rtag(rtag):
    16     RTAGS.append(rtag)
    33     RTAGS.append(rtag)
    17 
    34 
       
    35 def _ensure_str_key(key):
       
    36     return tuple(str(k) for k in key)
       
    37 
    18 class RelationTags(object):
    38 class RelationTags(object):
    19     """a tag store for full relation definitions :
    39     """a tag store for full relation definitions :
    20 
    40 
    21          (subject type, relation type, object type, tagged)
    41          (subject type, relation type, object type, tagged)
    22 
    42 
    23     allowing to set tags using wildcard (eg '*') as subject type / object type
    43     allowing to set tags using wildcard (eg '*') as subject type / object type
    24 
    44 
    25     This class associates a single tag to each key.
    45     This class associates a single tag to each key.
    26     """
    46     """
    27     _allowed_values = None
    47     _allowed_values = None
       
    48     _initfunc = None
    28     def __init__(self, name=None, initfunc=None, allowed_values=None):
    49     def __init__(self, name=None, initfunc=None, allowed_values=None):
    29         self._name = name or '<unknown>'
    50         self._name = name or '<unknown>'
    30         self._tagdefs = {}
    51         self._tagdefs = {}
    31         if allowed_values is not None:
    52         if allowed_values is not None:
    32             self._allowed_values = allowed_values
    53             self._allowed_values = allowed_values
    33         self._initfunc = initfunc
    54         if initfunc is not None:
       
    55             self._initfunc = initfunc
    34         register_rtag(self)
    56         register_rtag(self)
    35 
    57 
    36     def __repr__(self):
    58     def __repr__(self):
    37         return '%s: %s' % (self._name, repr(self._tagdefs))
    59         return '%s: %s' % (self._name, repr(self._tagdefs))
    38 
    60 
    43 
    65 
    44     def clear(self):
    66     def clear(self):
    45         self._tagdefs.clear()
    67         self._tagdefs.clear()
    46 
    68 
    47     def _get_keys(self, stype, rtype, otype, tagged):
    69     def _get_keys(self, stype, rtype, otype, tagged):
    48         keys = [(rtype, tagged, '*', '*'),
    70         keys = [('*', rtype, '*', tagged),
    49                 (rtype, tagged, '*', otype),
    71                 ('*', rtype, otype, tagged),
    50                 (rtype, tagged, stype, '*'),
    72                 (stype, rtype, '*', tagged),
    51                 (rtype, tagged, stype, otype)]
    73                 (stype, rtype, otype, tagged)]
    52         if stype == '*' or otype == '*':
    74         if stype == '*' or otype == '*':
    53             keys.remove((rtype, tagged, '*', '*'))
    75             keys.remove( ('*', rtype, '*', tagged) )
    54             if stype == '*':
    76             if stype == '*':
    55                 keys.remove((rtype, tagged, '*', otype))
    77                 keys.remove( ('*', rtype, otype, tagged) )
    56             if otype == '*':
    78             if otype == '*':
    57                 keys.remove((rtype, tagged, stype, '*'))
    79                 keys.remove( (stype, rtype, '*', tagged) )
    58         return keys
    80         return keys
    59 
    81 
    60     def init(self, schema, check=True):
    82     def init(self, schema, check=True):
    61         # XXX check existing keys against schema
    83         # XXX check existing keys against schema
    62         if check:
    84         if check:
    63             for (rtype, tagged, stype, otype), value in self._tagdefs.items():
    85             for (stype, rtype, otype, tagged), value in self._tagdefs.items():
    64                 for ertype in (stype, rtype, otype):
    86                 for ertype in (stype, rtype, otype):
    65                     if ertype != '*' and not ertype in schema:
    87                     if ertype != '*' and not ertype in schema:
    66                         self.warning('removing rtag %s: %s, %s undefined in schema',
    88                         self.warning('removing rtag %s: %s, %s undefined in schema',
    67                                      (stype, rtype, otype, tagged), value, ertype)
    89                                      (stype, rtype, otype, tagged), value, ertype)
    68                         self.del_rtag(stype, rtype, otype, tagged)
    90                         self.del_rtag(stype, rtype, otype, tagged)
    69                         break
    91                         break
    70         if self._initfunc is not None:
    92         if self._initfunc is not None:
    71             for eschema in schema.entities():
    93             self.apply(schema, self._initfunc)
    72                 for rschema, tschemas, role in eschema.relation_definitions(True):
    94 
    73                     for tschema in tschemas:
    95     def apply(self, schema, func):
    74                         if role == 'subject':
    96         for eschema in schema.entities():
    75                             sschema, oschema = eschema, tschema
    97             for rschema, tschemas, role in eschema.relation_definitions(True):
    76                         else:
    98                 for tschema in tschemas:
    77                             sschema, oschema = tschema, eschema
    99                     if role == 'subject':
    78                         self._initfunc(self, sschema, rschema, oschema, role)
   100                         sschema, oschema = eschema, tschema
       
   101                     else:
       
   102                         sschema, oschema = tschema, eschema
       
   103                     func(self, sschema, rschema, oschema, role)
    79 
   104 
    80     # rtag declaration api ####################################################
   105     # rtag declaration api ####################################################
    81 
   106 
    82     def tag_attribute(self, key, tag):
   107     def tag_attribute(self, key, *args, **kwargs):
    83         key = list(key)
   108         key = list(key)
    84         key.append('*')
   109         key.append('*')
    85         self.tag_subject_of(key, tag)
   110         key.append('subject')
    86 
   111         self.tag_relation(key, *args, **kwargs)
    87     def tag_subject_of(self, key, tag):
   112 
       
   113     def tag_subject_of(self, key, *args, **kwargs):
    88         key = list(key)
   114         key = list(key)
    89         key.append('subject')
   115         key.append('subject')
    90         self.tag_relation(key, tag)
   116         self.tag_relation(key, *args, **kwargs)
    91 
   117 
    92     def tag_object_of(self, key, tag):
   118     def tag_object_of(self, key, *args, **kwargs):
    93         key = list(key)
   119         key = list(key)
    94         key.append('object')
   120         key.append('object')
    95         self.tag_relation(key, tag)
   121         self.tag_relation(key, *args, **kwargs)
    96 
   122 
    97     def tag_relation(self, key, tag):
   123     def tag_relation(self, key, tag):
    98         #if isinstance(key, basestring):
   124         assert len(key) == 4, 'bad key: %s' % list(key)
    99         #    stype, rtype, otype = key.split()
       
   100         #else:
       
   101         stype, rtype, otype, tagged = [str(k) for k in key]
       
   102         if self._allowed_values is not None:
   125         if self._allowed_values is not None:
   103             assert tag in self._allowed_values, \
   126             assert tag in self._allowed_values, \
   104                    '%r is not an allowed tag (should be in %s)' % (
   127                    '%r is not an allowed tag (should be in %s)' % (
   105                 tag, self._allowed_values)
   128                 tag, self._allowed_values)
   106         self._tagdefs[(rtype, tagged, stype, otype)] = tag
   129         self._tagdefs[_ensure_str_key(key)] = tag
   107         return tag
   130         return tag
   108 
   131 
   109     # rtag runtime api ########################################################
   132     # rtag runtime api ########################################################
   110 
   133 
   111     def del_rtag(self, stype, rtype, otype, tagged):
   134     def del_rtag(self, *key):
   112         del self._tagdefs[(rtype, tagged, stype, otype)]
   135         del self._tagdefs[key]
   113 
   136 
   114     def get(self, stype, rtype, otype, tagged):
   137     def get(self, *key):
   115         for key in reversed(self._get_keys(stype, rtype, otype, tagged)):
   138         for key in reversed(self._get_keys(*key)):
   116             try:
   139             try:
   117                 return self._tagdefs[key]
   140                 return self._tagdefs[key]
   118             except KeyError:
   141             except KeyError:
   119                 continue
   142                 continue
   120         return None
   143         return None
   130     """This class associates a set of tags to each key.
   153     """This class associates a set of tags to each key.
   131     """
   154     """
   132     tag_container_cls = set
   155     tag_container_cls = set
   133 
   156 
   134     def tag_relation(self, key, tag):
   157     def tag_relation(self, key, tag):
   135         stype, rtype, otype, tagged = [str(k) for k in key]
   158         rtags = self._tagdefs.setdefault(_ensure_str_key(key),
   136         rtags = self._tagdefs.setdefault((rtype, tagged, stype, otype),
       
   137                                          self.tag_container_cls())
   159                                          self.tag_container_cls())
   138         rtags.add(tag)
   160         rtags.add(tag)
   139         return rtags
   161         return rtags
   140 
   162 
   141     def get(self, stype, rtype, otype, tagged):
   163     def get(self, stype, rtype, otype, tagged):
   151 class RelationTagsDict(RelationTagsSet):
   173 class RelationTagsDict(RelationTagsSet):
   152     """This class associates a set of tags to each key."""
   174     """This class associates a set of tags to each key."""
   153     tag_container_cls = dict
   175     tag_container_cls = dict
   154 
   176 
   155     def tag_relation(self, key, tag):
   177     def tag_relation(self, key, tag):
   156         stype, rtype, otype, tagged = [str(k) for k in key]
   178         key = _ensure_str_key(key)
   157         try:
   179         try:
   158             rtags = self._tagdefs[(rtype, tagged, stype, otype)]
   180             rtags = self._tagdefs[key]
   159             rtags.update(tag)
   181             rtags.update(tag)
   160             return rtags
   182             return rtags
   161         except KeyError:
   183         except KeyError:
   162             self._tagdefs[(rtype, tagged, stype, otype)] = tag
   184             self._tagdefs[key] = tag
   163             return tag
   185             return tag
   164 
   186 
   165     def setdefault(self, key, tagkey, tagvalue):
   187     def setdefault(self, key, tagkey, tagvalue):
   166         stype, rtype, otype, tagged = [str(k) for k in key]
   188         key = _ensure_str_key(key)
   167         try:
   189         try:
   168             rtags = self._tagdefs[(rtype, tagged, stype, otype)]
   190             rtags = self._tagdefs[key]
   169             rtags.setdefault(tagkey, tagvalue)
   191             rtags.setdefault(tagkey, tagvalue)
   170             return rtags
   192             return rtags
   171         except KeyError:
   193         except KeyError:
   172             self._tagdefs[(rtype, tagged, stype, otype)] = {tagkey: tagvalue}
   194             self._tagdefs[key] = {tagkey: tagvalue}
   173             return self._tagdefs[(rtype, tagged, stype, otype)]
   195             return self._tagdefs[key]
   174 
   196 
   175 
   197 
   176 class RelationTagsBool(RelationTags):
   198 class RelationTagsBool(RelationTags):
   177     _allowed_values = frozenset((True, False))
   199     _allowed_values = frozenset((True, False))
   178 
   200