/
aliasing.py
206 lines (163 loc) · 5.57 KB
/
aliasing.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
from odict import odict
from plumber import (
plumber,
Part,
)
from zope.interface import implementer
from zope.interface.common.mapping import (
IEnumerableMapping,
IFullMapping,
)
from node.interfaces import IAliaser
from node.utils import ReverseMapping
from node.parts import (
Adopt,
Nodify,
NodeChildValidate,
)
@implementer(IAliaser, IFullMapping)
class DictAliaser(odict):
"""Uses its own dictionary for aliasing.
``__getitem__`` -> unalias
"""
def __init__(self, data=(), strict=True):
super(DictAliaser, self).__init__(data)
self.strict = strict
def alias(self, key):
try:
return ReverseMapping(self)[key]
except KeyError, e:
if not self.strict:
return key
raise e
def unalias(self, aliased_key):
try:
return self[aliased_key]
except KeyError, e:
if not self.strict:
return aliased_key
raise e
@implementer(IAliaser)
class PrefixAliaser(object):
"""An aliaser that prefix all keys.
As it never raise KeyError it is not whitelisting.
"""
def __init__(self, prefix=None):
self.prefix = prefix
def alias(self, key):
return (self.prefix or '') + key
def unalias(self, prefixed_key):
"""Returns the real key for a prefixed_key.
"""
prefix = self.prefix or ''
if not prefixed_key.startswith(prefix):
raise KeyError(u"key '%s' does not match prefix '%s'" % \
(prefixed_key, prefix))
return prefixed_key[len(prefix):]
@implementer(IAliaser)
class SuffixAliaser(object):
"""An aliaser that suffixes all keys.
As it never raise KeyError it is not whitelisting.
"""
def __init__(self, suffix=None):
self.suffix = suffix
def alias(self, key):
return key + (self.suffix or '')
def unalias(self, suffixed_key):
"""returns the real key for a suffixed_key
"""
suffix = self.suffix or ''
if not suffixed_key.endswith(suffix):
raise KeyError(
u"key '%s' does not match suffix '%s'" % \
(suffixed_key, suffix)
)
return suffixed_key[:-len(suffix)]
@implementer(IAliaser)
class AliaserChain(object):
"""A chain of aliasers.
chain = [aliaser1, aliaser2]
chain.alias(key) == aliaser2.alias(aliaser1.alias(key))
chain.unalias(alias_key) == aliaser2.unalias(aliaser1.unalias(aliased_key))
"""
# XXX: we are IEnumerableMapping if one of our childs is, which is
# important as we become a whitelist, eg. for Node.__iter__
def __init__(self, chain=None):
self.chain = chain
def alias(self, key):
for aliaser in self.chain:
key = aliaser.alias(key)
return key
def unalias(self, key):
for aliaser in reversed(self.chain):
key = aliaser.unalias(key)
return key
class PrefixSuffixAliaser(AliaserChain):
"""Prefixes and suffixes.
"""
def __init__(self, prefix=None, suffix=None):
self.chain = (
PrefixAliaser(prefix),
SuffixAliaser(suffix))
#class NamedAliasers(dict):
# """A dictionary storing aliasers by name.
#
# XXX
# """
class AliasedNodespace(object):
"""Performs aliasing/unaliasing for node children.
Is not the parent of its children, the children don't know about their name
here.
Future additional mode: children are wrapped, wrapper knows name and we are
parent of wrapper.
"""
__metaclass__ = plumber
__plumbing__ = (
NodeChildValidate,
Adopt,
Nodify,
)
def __init__(self, context, aliaser=None):
"""
context
the node whose children to alias
aliaser
the aliaser to be used
"""
# XXX: is just taking over the name ok for all use cases?
# XXX: case where not? -rn
#super(AliasedNodespace, self).__init__(context.name)
self.__name__ = context.name
self.__parent__ = None
self.context = context
self.aliaser = aliaser
def __delitem__(self, key):
unaliased_key = self.aliaser and self.aliaser.unalias(key) or key
try:
del self.context[unaliased_key]
except KeyError:
raise KeyError(key)
def __getitem__(self, key):
unaliased_key = self.aliaser and self.aliaser.unalias(key) or key
try:
return self.context[unaliased_key]
except KeyError:
raise KeyError(key)
def __setitem__(self, key, val):
unaliased_key = self.aliaser and self.aliaser.unalias(key) or key
self.context[unaliased_key] = val
def __iter__(self):
for key in self.context:
try:
yield self.aliaser and self.aliaser.alias(key) or key
except KeyError:
if IEnumerableMapping.providedBy(self.aliaser):
# an enumerable aliaser whitelists, we skip non-listed keys
continue
# no whitelisting and a KeyError on our internal data: that's
# bad! Most probably not triggered on _Node but a subclass
# XXX: test case showing this.
raise RuntimeError( #pragma NO COVERAGE
u"Inconsist internal node state") #pragma NO COVERAGE
def __repr__(self):
return "Aliased " + self.context.__repr__()