rtags.py 4.77 KB
Newer Older
sylvain.thenault@logilab.fr's avatar
cleanup    
sylvain.thenault@logilab.fr committed
1
2
3
4
5
6
7
"""relation tags store

:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
8

9
10
11
12
import logging

from logilab.common.logging_ext import set_log_methods

13
14
15
RTAGS = []
def register_rtag(rtag):
    RTAGS.append(rtag)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
16

17
class RelationTags(object):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
18
    """a tag store for full relation definitions :
sylvain.thenault@logilab.fr's avatar
cleanup    
sylvain.thenault@logilab.fr committed
19

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
20
         (subject type, relation type, object type, tagged)
sylvain.thenault@logilab.fr's avatar
cleanup    
sylvain.thenault@logilab.fr committed
21
22

    allowing to set tags using wildcard (eg '*') as subject type / object type
23

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
24
    This class associates a single tag to each key.
sylvain.thenault@logilab.fr's avatar
cleanup    
sylvain.thenault@logilab.fr committed
25
    """
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
26
27
    _allowed_values = None
    def __init__(self, initfunc=None, allowed_values=None):
28
        self._tagdefs = {}
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
29
30
31
        if allowed_values is not None:
            self._allowed_values = allowed_values
        self._initfunc = initfunc
32
        register_rtag(self)
33

Aurelien Campeas's avatar
Aurelien Campeas committed
34
35
36
    def __repr__(self):
        return repr(self._tagdefs)

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
37
38
39
40
41
    # dict compat
    def __getitem__(self, key):
        return self.get(*key)
    __contains__ = __getitem__

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
42
    def _get_keys(self, stype, rtype, otype, tagged):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
43
44
45
46
47
48
49
50
51
52
53
54
        keys = [(rtype, tagged, '*', '*'),
                (rtype, tagged, '*', otype),
                (rtype, tagged, stype, '*'),
                (rtype, tagged, stype, otype)]
        if stype == '*' or otype == '*':
            keys.remove((rtype, tagged, '*', '*'))
            if stype == '*':
                keys.remove((rtype, tagged, '*', otype))
            if otype == '*':
                keys.remove((rtype, tagged, stype, '*'))
        return keys

55
    def init(self, schema, check=True):
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
56
        # XXX check existing keys against schema
57
58
59
60
61
62
63
64
        if check:
            for (rtype, tagged, stype, otype), value in self._tagdefs.items():
                for ertype in (stype, rtype, otype):
                    if ertype != '*' and not ertype in schema:
                        self.warning('removing rtag %s: %s, %s undefined in schema',
                                     (stype, rtype, otype, tagged), value, ertype)
                        self.del_rtag(stype, rtype, otype, tagged)
                        break
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
65
66
67
68
69
        if self._initfunc is not None:
            for eschema in schema.entities():
                for rschema, tschemas, role in eschema.relation_definitions(True):
                    for tschema in tschemas:
                        if role == 'subject':
70
                            sschema, oschema = eschema, tschema
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
71
                        else:
72
73
                            sschema, oschema = tschema, eschema
                        self._initfunc(self, sschema, rschema, oschema, role)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97

    # rtag declaration api ####################################################

    def tag_attribute(self, key, tag):
        key = list(key)
        key.append('*')
        self.tag_subject_of(key, tag)

    def tag_subject_of(self, key, tag):
        key = list(key)
        key.append('subject')
        self.tag_relation(key, tag)

    def tag_object_of(self, key, tag):
        key = list(key)
        key.append('object')
        self.tag_relation(key, tag)

    def tag_relation(self, key, tag):
        #if isinstance(key, basestring):
        #    stype, rtype, otype = key.split()
        #else:
        stype, rtype, otype, tagged = [str(k) for k in key]
        if self._allowed_values is not None:
Aurelien Campeas's avatar
Aurelien Campeas committed
98
            assert tag in self._allowed_values, '%r is not an allowed tag' % tag
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
99
100
101
102
103
104
105
106
        self._tagdefs[(rtype, tagged, stype, otype)] = tag

    # rtag runtime api ########################################################

    def del_rtag(self, stype, rtype, otype, tagged):
        del self._tagdefs[(rtype, tagged, stype, otype)]

    def get(self, stype, rtype, otype, tagged):
107
        for key in reversed(self._get_keys(stype, rtype, otype, tagged)):
108
109
110
111
112
            try:
                return self._tagdefs[key]
            except KeyError:
                continue
        return None
113

114
115
116
117
    def etype_get(self, etype, rtype, role, ttype='*'):
        if role == 'subject':
            return self.get(etype, rtype, ttype, role)
        return self.get(ttype, rtype, etype, role)
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
118
119


120

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
121
122
123
class RelationTagsSet(RelationTags):
    """This class associates a set of tags to each key."""

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
124
125
    def tag_relation(self, key, tag):
        stype, rtype, otype, tagged = [str(k) for k in key]
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
126
        rtags = self._tagdefs.setdefault((rtype, tagged, stype, otype), set())
127
        rtags.add(tag)
128

sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
129
    def get(self, stype, rtype, otype, tagged):
130
        rtags = set()
131
        for key in self._get_keys(stype, rtype, otype, tagged):
132
133
134
135
136
            try:
                rtags.update(self._tagdefs[key])
            except KeyError:
                continue
        return rtags
sylvain.thenault@logilab.fr's avatar
sylvain.thenault@logilab.fr committed
137
138
139
140
141
142


class RelationTagsBool(RelationTags):
    _allowed_values = frozenset((True, False))


143
set_log_methods(RelationTags, logging.getLogger('cubicweb.rtags'))