Statistics
| Branch: | Tag: | Revision:

root / commissioning / api / specificator.py @ c8ef1fdb

History | View | Annotate | Download (17.3 kB)

1 9f1a1bd0 Georgios D. Tsoukalas
2 ac4d0f37 Georgios D. Tsoukalas
from random import random, choice, randint
3 ac4d0f37 Georgios D. Tsoukalas
from math import log
4 9f1a1bd0 Georgios D. Tsoukalas
5 9f1a1bd0 Georgios D. Tsoukalas
def shorts(s):
6 9f1a1bd0 Georgios D. Tsoukalas
    if not isinstance(s, unicode):
7 9f1a1bd0 Georgios D. Tsoukalas
        s = str(s)
8 9f1a1bd0 Georgios D. Tsoukalas
9 9f1a1bd0 Georgios D. Tsoukalas
    if len(s) <= 64:
10 9f1a1bd0 Georgios D. Tsoukalas
        return s
11 9f1a1bd0 Georgios D. Tsoukalas
12 9f1a1bd0 Georgios D. Tsoukalas
    return s[:61] + '...'
13 9f1a1bd0 Georgios D. Tsoukalas
        
14 9f1a1bd0 Georgios D. Tsoukalas
15 9f1a1bd0 Georgios D. Tsoukalas
class CanonifyException(Exception):
16 9f1a1bd0 Georgios D. Tsoukalas
    pass
17 9f1a1bd0 Georgios D. Tsoukalas
18 9f1a1bd0 Georgios D. Tsoukalas
class SpecifyException(Exception):
19 9f1a1bd0 Georgios D. Tsoukalas
    pass
20 9f1a1bd0 Georgios D. Tsoukalas
21 9f1a1bd0 Georgios D. Tsoukalas
22 9f1a1bd0 Georgios D. Tsoukalas
class Canonical(object):
23 ac4d0f37 Georgios D. Tsoukalas
24 ac4d0f37 Georgios D. Tsoukalas
    random_choice = None
25 ac4d0f37 Georgios D. Tsoukalas
26 9f1a1bd0 Georgios D. Tsoukalas
    def __init__(self, *args, **kw):
27 9f1a1bd0 Georgios D. Tsoukalas
        self.args = args
28 9f1a1bd0 Georgios D. Tsoukalas
        self.kw = kw
29 9f1a1bd0 Georgios D. Tsoukalas
        self.name = kw.pop('classname', self.__class__.__name__)
30 ac4d0f37 Georgios D. Tsoukalas
        random_choice = kw.pop('random', None)
31 ac4d0f37 Georgios D. Tsoukalas
        if random_choice is not None:
32 ac4d0f37 Georgios D. Tsoukalas
            self.random_choice = random_choice
33 9f1a1bd0 Georgios D. Tsoukalas
        opts = {}
34 9f1a1bd0 Georgios D. Tsoukalas
        for k, v in kw.items():
35 9f1a1bd0 Georgios D. Tsoukalas
            if not isinstance(v, Canonical):
36 9f1a1bd0 Georgios D. Tsoukalas
                opts[k] = v
37 9f1a1bd0 Georgios D. Tsoukalas
                del kw[k]
38 9f1a1bd0 Georgios D. Tsoukalas
39 9f1a1bd0 Georgios D. Tsoukalas
        self.opts = opts
40 9f1a1bd0 Georgios D. Tsoukalas
        self.init()
41 9f1a1bd0 Georgios D. Tsoukalas
42 9f1a1bd0 Georgios D. Tsoukalas
        if 'default' in opts:
43 9f1a1bd0 Georgios D. Tsoukalas
            item = opts['default']
44 9f1a1bd0 Georgios D. Tsoukalas
            if item is None:
45 9f1a1bd0 Georgios D. Tsoukalas
                opts['null'] = 1
46 9f1a1bd0 Georgios D. Tsoukalas
            else:
47 82b4be2b Georgios D. Tsoukalas
                opts['default'] = self.check(item)
48 9f1a1bd0 Georgios D. Tsoukalas
49 9f1a1bd0 Georgios D. Tsoukalas
    def init(self):
50 9f1a1bd0 Georgios D. Tsoukalas
        return
51 9f1a1bd0 Georgios D. Tsoukalas
52 9f1a1bd0 Georgios D. Tsoukalas
    def __call__(self, item):
53 9f1a1bd0 Georgios D. Tsoukalas
        opts = self.opts
54 9f1a1bd0 Georgios D. Tsoukalas
        if item is None and 'default' in opts:
55 9f1a1bd0 Georgios D. Tsoukalas
            item = opts['default']
56 9f1a1bd0 Georgios D. Tsoukalas
57 9f1a1bd0 Georgios D. Tsoukalas
        can_be_null = opts.get('null', False)
58 9f1a1bd0 Georgios D. Tsoukalas
        if item is None and can_be_null:
59 9f1a1bd0 Georgios D. Tsoukalas
            return None
60 9f1a1bd0 Georgios D. Tsoukalas
61 82b4be2b Georgios D. Tsoukalas
        return self.check(item)
62 9f1a1bd0 Georgios D. Tsoukalas
63 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
64 9f1a1bd0 Georgios D. Tsoukalas
        return item
65 9f1a1bd0 Georgios D. Tsoukalas
66 82b4be2b Georgios D. Tsoukalas
    def create(self):
67 82b4be2b Georgios D. Tsoukalas
        return None
68 82b4be2b Georgios D. Tsoukalas
69 ac4d0f37 Georgios D. Tsoukalas
    def random(self, **kw):
70 ac4d0f37 Georgios D. Tsoukalas
        random_choice = self.random_choice
71 ac4d0f37 Georgios D. Tsoukalas
        if random_choice is None:
72 ac4d0f37 Georgios D. Tsoukalas
            return None
73 ac4d0f37 Georgios D. Tsoukalas
74 ac4d0f37 Georgios D. Tsoukalas
        if callable(random_choice):
75 ac4d0f37 Georgios D. Tsoukalas
            return random_choice(kw)
76 ac4d0f37 Georgios D. Tsoukalas
77 ac4d0f37 Georgios D. Tsoukalas
        if isinstance(random_choice, str):
78 ac4d0f37 Georgios D. Tsoukalas
            return getattr(self, random_choice)(kw)
79 ac4d0f37 Georgios D. Tsoukalas
80 ac4d0f37 Georgios D. Tsoukalas
        return choice(random_choice)
81 82b4be2b Georgios D. Tsoukalas
82 9f1a1bd0 Georgios D. Tsoukalas
    def tostring(self, depth=0, showopts=0, multiline=0):
83 9f1a1bd0 Georgios D. Tsoukalas
        depth += 1
84 9f1a1bd0 Georgios D. Tsoukalas
        if not multiline:
85 9f1a1bd0 Georgios D. Tsoukalas
            argdepth = ''
86 9f1a1bd0 Georgios D. Tsoukalas
            owndepth = ''
87 9f1a1bd0 Georgios D. Tsoukalas
            joinchar = ','
88 9f1a1bd0 Georgios D. Tsoukalas
            padchar = ''
89 9f1a1bd0 Georgios D. Tsoukalas
        else:
90 9f1a1bd0 Georgios D. Tsoukalas
            argdepth = '    ' * depth
91 9f1a1bd0 Georgios D. Tsoukalas
            owndepth = '    ' * (depth - 1)
92 9f1a1bd0 Georgios D. Tsoukalas
            joinchar = ',\n'
93 9f1a1bd0 Georgios D. Tsoukalas
            padchar = '\n'
94 9f1a1bd0 Georgios D. Tsoukalas
95 9f1a1bd0 Georgios D. Tsoukalas
        args = [a.tostring( depth=depth,
96 9f1a1bd0 Georgios D. Tsoukalas
                            showopts=showopts,
97 9f1a1bd0 Georgios D. Tsoukalas
                            multiline=multiline) for a in self.args]
98 9f1a1bd0 Georgios D. Tsoukalas
        args += [("%s=%s" %
99 9f1a1bd0 Georgios D. Tsoukalas
                    (k, v.tostring( depth=depth,
100 9f1a1bd0 Georgios D. Tsoukalas
                                    showopts=showopts,
101 9f1a1bd0 Georgios D. Tsoukalas
                                    multiline=multiline)))
102 9f1a1bd0 Georgios D. Tsoukalas
                                    
103 9f1a1bd0 Georgios D. Tsoukalas
                                    for k, v in self.kw.items()]
104 9f1a1bd0 Georgios D. Tsoukalas
        if showopts:
105 9f1a1bd0 Georgios D. Tsoukalas
            args += [("%s=%s" % (k, str(v))) for k, v in self.opts.items()]
106 9f1a1bd0 Georgios D. Tsoukalas
107 9f1a1bd0 Georgios D. Tsoukalas
        if len(args) == 0:
108 9f1a1bd0 Georgios D. Tsoukalas
            string = "%s(%s)" % (self.name, ','.join(args))
109 9f1a1bd0 Georgios D. Tsoukalas
        else:
110 9f1a1bd0 Georgios D. Tsoukalas
            string = "%s(%s" % (self.name, padchar)
111 9f1a1bd0 Georgios D. Tsoukalas
            for arg in args:
112 9f1a1bd0 Georgios D. Tsoukalas
                string += argdepth + arg + joinchar
113 9f1a1bd0 Georgios D. Tsoukalas
            string = string[:-1] + padchar
114 9f1a1bd0 Georgios D. Tsoukalas
            string += owndepth + ")"
115 9f1a1bd0 Georgios D. Tsoukalas
116 9f1a1bd0 Georgios D. Tsoukalas
        return string
117 9f1a1bd0 Georgios D. Tsoukalas
118 9f1a1bd0 Georgios D. Tsoukalas
    __str__ = tostring
119 9f1a1bd0 Georgios D. Tsoukalas
120 9f1a1bd0 Georgios D. Tsoukalas
    def __repr__(self):
121 9f1a1bd0 Georgios D. Tsoukalas
        return self.tostring(multiline=0, showopts=1)
122 9f1a1bd0 Georgios D. Tsoukalas
123 82b4be2b Georgios D. Tsoukalas
    def check(item):
124 9f1a1bd0 Georgios D. Tsoukalas
        canonified = item
125 9f1a1bd0 Georgios D. Tsoukalas
        return canonified
126 9f1a1bd0 Georgios D. Tsoukalas
127 9f1a1bd0 Georgios D. Tsoukalas
128 9f1a1bd0 Georgios D. Tsoukalas
class Null(Canonical):
129 9f1a1bd0 Georgios D. Tsoukalas
130 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
131 9f1a1bd0 Georgios D. Tsoukalas
        return None
132 9f1a1bd0 Georgios D. Tsoukalas
133 9f1a1bd0 Georgios D. Tsoukalas
Nothing = Null()
134 9f1a1bd0 Georgios D. Tsoukalas
135 ac4d0f37 Georgios D. Tsoukalas
136 9f1a1bd0 Georgios D. Tsoukalas
class Integer(Canonical):
137 9f1a1bd0 Georgios D. Tsoukalas
138 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
139 9f1a1bd0 Georgios D. Tsoukalas
        try:
140 82b4be2b Georgios D. Tsoukalas
            num = long(item)
141 9f1a1bd0 Georgios D. Tsoukalas
        except ValueError, e:
142 9f1a1bd0 Georgios D. Tsoukalas
            try:
143 82b4be2b Georgios D. Tsoukalas
                num = long(item, 16)
144 9f1a1bd0 Georgios D. Tsoukalas
            except Exception:
145 82b4be2b Georgios D. Tsoukalas
                m = "%s: cannot convert '%s' to long" % (self, shorts(item))
146 9f1a1bd0 Georgios D. Tsoukalas
                raise CanonifyException(m)
147 9f1a1bd0 Georgios D. Tsoukalas
148 9f1a1bd0 Georgios D. Tsoukalas
        optget = self.opts.get
149 9f1a1bd0 Georgios D. Tsoukalas
        minimum = optget('minimum', None)
150 9f1a1bd0 Georgios D. Tsoukalas
        maximum = optget('maximum', None)
151 9f1a1bd0 Georgios D. Tsoukalas
152 9f1a1bd0 Georgios D. Tsoukalas
        if minimum is not None and num < minimum:
153 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: %d < minimum=%d" % (self, num, minimum)
154 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
155 9f1a1bd0 Georgios D. Tsoukalas
156 9f1a1bd0 Georgios D. Tsoukalas
        if maximum is not None and num > maximum:
157 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: %d > maximum=%d" % (self, num, maximum)
158 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
159 9f1a1bd0 Georgios D. Tsoukalas
160 9f1a1bd0 Georgios D. Tsoukalas
        return num
161 9f1a1bd0 Georgios D. Tsoukalas
162 ac4d0f37 Georgios D. Tsoukalas
    def random_integer(self, kw):
163 ac4d0f37 Georgios D. Tsoukalas
        optget = self.opts.get
164 ac4d0f37 Georgios D. Tsoukalas
        kwget = kw.get
165 ac4d0f37 Georgios D. Tsoukalas
        minimum = kwget('minimum', optget('minimum', -4294967296L))
166 ac4d0f37 Georgios D. Tsoukalas
        maximum = kwget('maximum', optget('maximum', 4294967295L))
167 ac4d0f37 Georgios D. Tsoukalas
        r = random()
168 ac4d0f37 Georgios D. Tsoukalas
        if r < 0.1:
169 ac4d0f37 Georgios D. Tsoukalas
            return minimum
170 ac4d0f37 Georgios D. Tsoukalas
        if r < 0.2:
171 ac4d0f37 Georgios D. Tsoukalas
            return maximum
172 ac4d0f37 Georgios D. Tsoukalas
        if minimum <= 0 and maximum >= 0 and r < 0.3:
173 ac4d0f37 Georgios D. Tsoukalas
            return 0L
174 ac4d0f37 Georgios D. Tsoukalas
        return long(minimum + r * (maximum - minimum))
175 ac4d0f37 Georgios D. Tsoukalas
176 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_integer
177 ac4d0f37 Georgios D. Tsoukalas
178 ac4d0f37 Georgios D. Tsoukalas
179 9f1a1bd0 Georgios D. Tsoukalas
180 82b4be2b Georgios D. Tsoukalas
Serial = Integer(
181 82b4be2b Georgios D. Tsoukalas
            classname   =   'Serial',
182 82b4be2b Georgios D. Tsoukalas
            null        =   True,
183 82b4be2b Georgios D. Tsoukalas
)
184 82b4be2b Georgios D. Tsoukalas
185 82b4be2b Georgios D. Tsoukalas
186 d4eaff2f Georgios D. Tsoukalas
class Text(Canonical):
187 9f1a1bd0 Georgios D. Tsoukalas
188 9f1a1bd0 Georgios D. Tsoukalas
    re = None
189 9f1a1bd0 Georgios D. Tsoukalas
    matcher = None
190 9f1a1bd0 Georgios D. Tsoukalas
    choices = None
191 9f1a1bd0 Georgios D. Tsoukalas
192 9f1a1bd0 Georgios D. Tsoukalas
    def init(self):
193 9f1a1bd0 Georgios D. Tsoukalas
        opts = self.opts
194 9f1a1bd0 Georgios D. Tsoukalas
        if 'regex' in opts:
195 9f1a1bd0 Georgios D. Tsoukalas
            pat = opts['regex']
196 9f1a1bd0 Georgios D. Tsoukalas
            re = self.re
197 9f1a1bd0 Georgios D. Tsoukalas
            if re is None:
198 9f1a1bd0 Georgios D. Tsoukalas
                import re
199 9f1a1bd0 Georgios D. Tsoukalas
                self.re = re
200 9f1a1bd0 Georgios D. Tsoukalas
201 9f1a1bd0 Georgios D. Tsoukalas
            self.matcher = re.compile(pat, re.UNICODE)
202 9f1a1bd0 Georgios D. Tsoukalas
            self.pat = pat
203 9f1a1bd0 Georgios D. Tsoukalas
204 9f1a1bd0 Georgios D. Tsoukalas
        if 'choices' in opts:
205 9f1a1bd0 Georgios D. Tsoukalas
            opts['choices'] = dict((unicode(x), unicode(x))
206 9f1a1bd0 Georgios D. Tsoukalas
                                    for x in opts['choices'])
207 9f1a1bd0 Georgios D. Tsoukalas
208 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
209 9f1a1bd0 Georgios D. Tsoukalas
        if not isinstance(item, unicode):
210 9f1a1bd0 Georgios D. Tsoukalas
            # require non-unicode items to be utf8
211 9f1a1bd0 Georgios D. Tsoukalas
            item = str(item)
212 9f1a1bd0 Georgios D. Tsoukalas
            try:
213 9f1a1bd0 Georgios D. Tsoukalas
                item = item.decode('utf8')
214 9f1a1bd0 Georgios D. Tsoukalas
            except UnicodeDecodeError, e:
215 9f1a1bd0 Georgios D. Tsoukalas
                item = item.decode('latin1')
216 9f1a1bd0 Georgios D. Tsoukalas
                m = "%s: non-unicode '%s' is not utf8" % (self, shorts(item))
217 9f1a1bd0 Georgios D. Tsoukalas
                raise CanonifyException(m)
218 9f1a1bd0 Georgios D. Tsoukalas
219 9f1a1bd0 Georgios D. Tsoukalas
        opts = self.opts
220 9f1a1bd0 Georgios D. Tsoukalas
        if 'choices' in opts:
221 9f1a1bd0 Georgios D. Tsoukalas
            choices = opts['choices']
222 9f1a1bd0 Georgios D. Tsoukalas
            try:
223 9f1a1bd0 Georgios D. Tsoukalas
                unknown = item not in choices
224 9f1a1bd0 Georgios D. Tsoukalas
            except TypeError, e:
225 9f1a1bd0 Georgios D. Tsoukalas
                m = "%s: unhashable type '%s'" % (self.name, shorts(item))
226 9f1a1bd0 Georgios D. Tsoukalas
                raise CanonifyException(m, e)
227 9f1a1bd0 Georgios D. Tsoukalas
228 9f1a1bd0 Georgios D. Tsoukalas
            if unknown:
229 9f1a1bd0 Georgios D. Tsoukalas
                m = "%s: '%s' not in choices" % (self.name, shorts(item))
230 9f1a1bd0 Georgios D. Tsoukalas
                raise CanonifyException(m)
231 9f1a1bd0 Georgios D. Tsoukalas
232 9f1a1bd0 Georgios D. Tsoukalas
            return choices[item]
233 9f1a1bd0 Georgios D. Tsoukalas
234 9f1a1bd0 Georgios D. Tsoukalas
        optget = opts.get
235 9f1a1bd0 Georgios D. Tsoukalas
        itemlen = len(item)
236 9f1a1bd0 Georgios D. Tsoukalas
        maxlen = optget('maxlen', None)
237 9f1a1bd0 Georgios D. Tsoukalas
        if maxlen is not None and itemlen > maxlen:
238 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: len('%s') > maxlen=%d" % (self, shorts(item), maxlen)
239 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
240 9f1a1bd0 Georgios D. Tsoukalas
241 9f1a1bd0 Georgios D. Tsoukalas
        minlen = optget('minlen', None)
242 9f1a1bd0 Georgios D. Tsoukalas
        if minlen is not None and itemlen < minlen:
243 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: len('%s') < minlen=%d" % (self, shorts(item), minlen)
244 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
245 9f1a1bd0 Georgios D. Tsoukalas
            
246 9f1a1bd0 Georgios D. Tsoukalas
        matcher = self.matcher
247 9f1a1bd0 Georgios D. Tsoukalas
        if matcher is not None:
248 9f1a1bd0 Georgios D. Tsoukalas
            match = matcher.match(item)
249 9f1a1bd0 Georgios D. Tsoukalas
            if  (       match is None
250 9f1a1bd0 Georgios D. Tsoukalas
                    or  (match.start(), match.end()) != (0, itemlen)    ):
251 9f1a1bd0 Georgios D. Tsoukalas
252 9f1a1bd0 Georgios D. Tsoukalas
                    m = ("%s: '%s' does not match '%s'"
253 9f1a1bd0 Georgios D. Tsoukalas
                            % (self, shorts(item), self.pat))
254 9f1a1bd0 Georgios D. Tsoukalas
                    raise CanonifyException(m)
255 9f1a1bd0 Georgios D. Tsoukalas
256 9f1a1bd0 Georgios D. Tsoukalas
        return item
257 9f1a1bd0 Georgios D. Tsoukalas
258 ac4d0f37 Georgios D. Tsoukalas
    default_alphabet = '0123456789αβγδεζ'.decode('utf8')
259 ac4d0f37 Georgios D. Tsoukalas
260 ac4d0f37 Georgios D. Tsoukalas
    def random_string(self, kw):
261 ac4d0f37 Georgios D. Tsoukalas
        opts = self.opts
262 ac4d0f37 Georgios D. Tsoukalas
        if 'regex' in opts:
263 ac4d0f37 Georgios D. Tsoukalas
            m = 'Unfortunately, random for regex strings not supported'
264 ac4d0f37 Georgios D. Tsoukalas
            raise ValueError(m)
265 ac4d0f37 Georgios D. Tsoukalas
266 ac4d0f37 Georgios D. Tsoukalas
        optget = opts.get
267 ac4d0f37 Georgios D. Tsoukalas
        kwget = kw.get
268 ac4d0f37 Georgios D. Tsoukalas
        minlen = kwget('minlen', optget('minlen', 0))
269 ac4d0f37 Georgios D. Tsoukalas
        maxlen = kwget('maxlen', optget('maxlen', 32))
270 ac4d0f37 Georgios D. Tsoukalas
        alphabet = kwget('alphabet', self.default_alphabet)
271 ac4d0f37 Georgios D. Tsoukalas
        z = maxlen - minlen
272 ac4d0f37 Georgios D. Tsoukalas
        if z < 1:
273 ac4d0f37 Georgios D. Tsoukalas
            z = 1
274 ac4d0f37 Georgios D. Tsoukalas
275 ac4d0f37 Georgios D. Tsoukalas
        g = log(z, 2)
276 ac4d0f37 Georgios D. Tsoukalas
        r = random() * g
277 ac4d0f37 Georgios D. Tsoukalas
        z = minlen + int(2**r)
278 ac4d0f37 Georgios D. Tsoukalas
279 ac4d0f37 Georgios D. Tsoukalas
        s = u''
280 ac4d0f37 Georgios D. Tsoukalas
        for _ in xrange(z):
281 ac4d0f37 Georgios D. Tsoukalas
            s += choice(alphabet)
282 ac4d0f37 Georgios D. Tsoukalas
283 ac4d0f37 Georgios D. Tsoukalas
        return s
284 ac4d0f37 Georgios D. Tsoukalas
285 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_string
286 ac4d0f37 Georgios D. Tsoukalas
287 9f1a1bd0 Georgios D. Tsoukalas
288 d4eaff2f Georgios D. Tsoukalas
class Bytes(Canonical):
289 d4eaff2f Georgios D. Tsoukalas
290 d4eaff2f Georgios D. Tsoukalas
    re = None
291 d4eaff2f Georgios D. Tsoukalas
    matcher = None
292 d4eaff2f Georgios D. Tsoukalas
    choices = None
293 d4eaff2f Georgios D. Tsoukalas
294 d4eaff2f Georgios D. Tsoukalas
    def init(self):
295 d4eaff2f Georgios D. Tsoukalas
        opts = self.opts
296 d4eaff2f Georgios D. Tsoukalas
        if 'regex' in opts:
297 d4eaff2f Georgios D. Tsoukalas
            pat = opts['regex']
298 d4eaff2f Georgios D. Tsoukalas
            re = self.re
299 d4eaff2f Georgios D. Tsoukalas
            if re is None:
300 d4eaff2f Georgios D. Tsoukalas
                import re
301 d4eaff2f Georgios D. Tsoukalas
                self.re = re
302 d4eaff2f Georgios D. Tsoukalas
303 d4eaff2f Georgios D. Tsoukalas
            self.matcher = re.compile(pat)
304 d4eaff2f Georgios D. Tsoukalas
            self.pat = pat
305 d4eaff2f Georgios D. Tsoukalas
306 d4eaff2f Georgios D. Tsoukalas
        if 'choices' in opts:
307 d4eaff2f Georgios D. Tsoukalas
            opts['choices'] = dict((str(x), str(x))
308 d4eaff2f Georgios D. Tsoukalas
                                    for x in opts['choices'])
309 d4eaff2f Georgios D. Tsoukalas
310 d4eaff2f Georgios D. Tsoukalas
    def check(self, item):
311 d4eaff2f Georgios D. Tsoukalas
        if isinstance(item, unicode):
312 d4eaff2f Georgios D. Tsoukalas
            # convert unicode to utf8
313 d4eaff2f Georgios D. Tsoukalas
            item = item.encode('utf8')
314 d4eaff2f Georgios D. Tsoukalas
315 d4eaff2f Georgios D. Tsoukalas
        opts = self.opts
316 d4eaff2f Georgios D. Tsoukalas
        if 'choices' in opts:
317 d4eaff2f Georgios D. Tsoukalas
            choices = opts['choices']
318 d4eaff2f Georgios D. Tsoukalas
            try:
319 d4eaff2f Georgios D. Tsoukalas
                unknown = item not in choices
320 d4eaff2f Georgios D. Tsoukalas
            except TypeError, e:
321 d4eaff2f Georgios D. Tsoukalas
                m = "%s: unhashable type '%s'" % (self.name, shorts(item))
322 d4eaff2f Georgios D. Tsoukalas
                raise CanonifyException(m, e)
323 d4eaff2f Georgios D. Tsoukalas
324 d4eaff2f Georgios D. Tsoukalas
            if unknown:
325 d4eaff2f Georgios D. Tsoukalas
                m = "%s: '%s' not in choices" % (self.name, shorts(item))
326 d4eaff2f Georgios D. Tsoukalas
                raise CanonifyException(m)
327 d4eaff2f Georgios D. Tsoukalas
328 d4eaff2f Georgios D. Tsoukalas
            return choices[item]
329 d4eaff2f Georgios D. Tsoukalas
330 d4eaff2f Georgios D. Tsoukalas
        optget = opts.get
331 d4eaff2f Georgios D. Tsoukalas
        itemlen = len(item)
332 d4eaff2f Georgios D. Tsoukalas
        maxlen = optget('maxlen', None)
333 d4eaff2f Georgios D. Tsoukalas
        if maxlen is not None and itemlen > maxlen:
334 d4eaff2f Georgios D. Tsoukalas
            m = "%s: len('%s') > maxlen=%d" % (self, shorts(item), maxlen)
335 d4eaff2f Georgios D. Tsoukalas
            raise CanonifyException(m)
336 d4eaff2f Georgios D. Tsoukalas
337 d4eaff2f Georgios D. Tsoukalas
        minlen = optget('minlen', None)
338 d4eaff2f Georgios D. Tsoukalas
        if minlen is not None and itemlen < minlen:
339 d4eaff2f Georgios D. Tsoukalas
            m = "%s: len('%s') < minlen=%d" % (self, shorts(item), minlen)
340 d4eaff2f Georgios D. Tsoukalas
            raise CanonifyException(m)
341 d4eaff2f Georgios D. Tsoukalas
            
342 d4eaff2f Georgios D. Tsoukalas
        matcher = self.matcher
343 d4eaff2f Georgios D. Tsoukalas
        if matcher is not None:
344 d4eaff2f Georgios D. Tsoukalas
            match = matcher.match(item)
345 d4eaff2f Georgios D. Tsoukalas
            if  (       match is None
346 d4eaff2f Georgios D. Tsoukalas
                    or  (match.start(), match.end()) != (0, itemlen)    ):
347 d4eaff2f Georgios D. Tsoukalas
348 d4eaff2f Georgios D. Tsoukalas
                    m = ("%s: '%s' does not match '%s'"
349 d4eaff2f Georgios D. Tsoukalas
                            % (self, shorts(item), self.pat))
350 d4eaff2f Georgios D. Tsoukalas
                    raise CanonifyException(m)
351 d4eaff2f Georgios D. Tsoukalas
352 d4eaff2f Georgios D. Tsoukalas
        return item
353 d4eaff2f Georgios D. Tsoukalas
354 d4eaff2f Georgios D. Tsoukalas
    default_alphabet = '0123456789abcdef'
355 d4eaff2f Georgios D. Tsoukalas
356 d4eaff2f Georgios D. Tsoukalas
    def random_bytes(self, kw):
357 d4eaff2f Georgios D. Tsoukalas
        opts = self.opts
358 d4eaff2f Georgios D. Tsoukalas
        if 'regex' in opts:
359 d4eaff2f Georgios D. Tsoukalas
            m = 'Unfortunately, random for regex strings not supported'
360 d4eaff2f Georgios D. Tsoukalas
            raise ValueError(m)
361 d4eaff2f Georgios D. Tsoukalas
362 d4eaff2f Georgios D. Tsoukalas
        optget = opts.get
363 d4eaff2f Georgios D. Tsoukalas
        kwget = kw.get
364 d4eaff2f Georgios D. Tsoukalas
        minlen = kwget('minlen', optget('minlen', 0))
365 d4eaff2f Georgios D. Tsoukalas
        maxlen = kwget('maxlen', optget('maxlen', 32))
366 d4eaff2f Georgios D. Tsoukalas
        alphabet = kwget('alphabet', self.default_alphabet)
367 d4eaff2f Georgios D. Tsoukalas
        z = maxlen - minlen
368 d4eaff2f Georgios D. Tsoukalas
        if z < 1:
369 d4eaff2f Georgios D. Tsoukalas
            z = 1
370 d4eaff2f Georgios D. Tsoukalas
371 d4eaff2f Georgios D. Tsoukalas
        g = log(z, 2)
372 d4eaff2f Georgios D. Tsoukalas
        r = random() * g
373 d4eaff2f Georgios D. Tsoukalas
        z = minlen + int(2**r)
374 d4eaff2f Georgios D. Tsoukalas
375 d4eaff2f Georgios D. Tsoukalas
        s = u''
376 d4eaff2f Georgios D. Tsoukalas
        for _ in xrange(z):
377 d4eaff2f Georgios D. Tsoukalas
            s += choice(alphabet)
378 d4eaff2f Georgios D. Tsoukalas
379 d4eaff2f Georgios D. Tsoukalas
        return s
380 d4eaff2f Georgios D. Tsoukalas
381 d4eaff2f Georgios D. Tsoukalas
    random_choice = random_bytes
382 d4eaff2f Georgios D. Tsoukalas
383 d4eaff2f Georgios D. Tsoukalas
384 9f1a1bd0 Georgios D. Tsoukalas
class ListOf(Canonical):
385 9f1a1bd0 Georgios D. Tsoukalas
386 9f1a1bd0 Georgios D. Tsoukalas
    def init(self):
387 9f1a1bd0 Georgios D. Tsoukalas
        args = self.args
388 9f1a1bd0 Georgios D. Tsoukalas
        kw = self.kw
389 9f1a1bd0 Georgios D. Tsoukalas
390 9f1a1bd0 Georgios D. Tsoukalas
        if not (args or kw):
391 9f1a1bd0 Georgios D. Tsoukalas
            raise SpecifyException("ListOf requires one or more arguments")
392 9f1a1bd0 Georgios D. Tsoukalas
393 9f1a1bd0 Georgios D. Tsoukalas
        if args and kw:
394 9f1a1bd0 Georgios D. Tsoukalas
            m = ("ListOf requires either positional "
395 9f1a1bd0 Georgios D. Tsoukalas
                 "or keyword arguments, but not both")
396 9f1a1bd0 Georgios D. Tsoukalas
            raise SpecifyException(m)
397 9f1a1bd0 Georgios D. Tsoukalas
398 9f1a1bd0 Georgios D. Tsoukalas
        if args:
399 9f1a1bd0 Georgios D. Tsoukalas
            if len(args) > 1:
400 9f1a1bd0 Georgios D. Tsoukalas
                self.canonical = Tuple(*args)
401 9f1a1bd0 Georgios D. Tsoukalas
            else:
402 9f1a1bd0 Georgios D. Tsoukalas
                self.canonical = args[0]
403 9f1a1bd0 Georgios D. Tsoukalas
        else:
404 9f1a1bd0 Georgios D. Tsoukalas
            self.canonical = Args(**kw)
405 9f1a1bd0 Georgios D. Tsoukalas
406 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
407 9f1a1bd0 Georgios D. Tsoukalas
        if item is None:
408 9f1a1bd0 Georgios D. Tsoukalas
            item = ()
409 9f1a1bd0 Georgios D. Tsoukalas
410 9f1a1bd0 Georgios D. Tsoukalas
        try:
411 9f1a1bd0 Georgios D. Tsoukalas
            items = iter(item)
412 9f1a1bd0 Georgios D. Tsoukalas
        except TypeError, e:
413 82b4be2b Georgios D. Tsoukalas
            m = "%s: %s is not iterable" % (self, shorts(item))
414 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
415 9f1a1bd0 Georgios D. Tsoukalas
416 9f1a1bd0 Georgios D. Tsoukalas
        canonical = self.canonical
417 9f1a1bd0 Georgios D. Tsoukalas
        canonified = []
418 9f1a1bd0 Georgios D. Tsoukalas
        append = canonified.append
419 9f1a1bd0 Georgios D. Tsoukalas
420 9f1a1bd0 Georgios D. Tsoukalas
        for item in items:
421 9f1a1bd0 Georgios D. Tsoukalas
            item = canonical(item)
422 9f1a1bd0 Georgios D. Tsoukalas
            append(item)
423 9f1a1bd0 Georgios D. Tsoukalas
424 9f1a1bd0 Georgios D. Tsoukalas
        if not canonified and self.opts.get('nonempty', False):
425 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: must be nonempty" % (self,)
426 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
427 9f1a1bd0 Georgios D. Tsoukalas
428 9f1a1bd0 Georgios D. Tsoukalas
        return canonified
429 9f1a1bd0 Georgios D. Tsoukalas
430 ac4d0f37 Georgios D. Tsoukalas
    def random_listof(self, kw):
431 ac4d0f37 Georgios D. Tsoukalas
        z = randint(1, 4)
432 ac4d0f37 Georgios D. Tsoukalas
        get_random = self.canonical.random
433 ac4d0f37 Georgios D. Tsoukalas
434 ac4d0f37 Georgios D. Tsoukalas
        return [get_random() for _ in xrange(z)]
435 ac4d0f37 Georgios D. Tsoukalas
436 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_listof
437 ac4d0f37 Georgios D. Tsoukalas
438 9f1a1bd0 Georgios D. Tsoukalas
439 9f1a1bd0 Georgios D. Tsoukalas
class Args(Canonical):
440 9f1a1bd0 Georgios D. Tsoukalas
441 9f1a1bd0 Georgios D. Tsoukalas
    def init(self):
442 9f1a1bd0 Georgios D. Tsoukalas
        if self.args:
443 9f1a1bd0 Georgios D. Tsoukalas
            raise ValueError("Args accepts only keyword arguments")
444 9f1a1bd0 Georgios D. Tsoukalas
445 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
446 9f1a1bd0 Georgios D. Tsoukalas
        try:
447 9f1a1bd0 Georgios D. Tsoukalas
            item = dict(item)
448 9f1a1bd0 Georgios D. Tsoukalas
        except TypeError, e:
449 82b4be2b Georgios D. Tsoukalas
            m = "%s: %s is not dict-able" % (self, shorts(item))
450 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
451 9f1a1bd0 Georgios D. Tsoukalas
452 9f1a1bd0 Georgios D. Tsoukalas
        canonified = {}
453 9f1a1bd0 Georgios D. Tsoukalas
454 9f1a1bd0 Georgios D. Tsoukalas
        try:
455 9f1a1bd0 Georgios D. Tsoukalas
            for n, c in self.kw.items():
456 05fd3edd Georgios D. Tsoukalas
                t = item[n] if n in item else None
457 05fd3edd Georgios D. Tsoukalas
                canonified[n] = c(t)
458 9f1a1bd0 Georgios D. Tsoukalas
        except KeyError:
459 82b4be2b Georgios D. Tsoukalas
            m = ("%s: Argument '%s' not found in '%s'" 
460 82b4be2b Georgios D. Tsoukalas
                        % (self, shorts(n), shorts(item)))
461 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
462 9f1a1bd0 Georgios D. Tsoukalas
463 9f1a1bd0 Georgios D. Tsoukalas
        return canonified
464 9f1a1bd0 Georgios D. Tsoukalas
465 ac4d0f37 Georgios D. Tsoukalas
    def random_args(self, kw):
466 ac4d0f37 Georgios D. Tsoukalas
        args = {}
467 ac4d0f37 Georgios D. Tsoukalas
        for n, c in self.kw.items():
468 ac4d0f37 Georgios D. Tsoukalas
            args[n] = c.random()
469 ac4d0f37 Georgios D. Tsoukalas
        return args
470 ac4d0f37 Georgios D. Tsoukalas
471 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_args
472 ac4d0f37 Georgios D. Tsoukalas
473 9f1a1bd0 Georgios D. Tsoukalas
474 9f1a1bd0 Georgios D. Tsoukalas
class Tuple(Canonical):
475 9f1a1bd0 Georgios D. Tsoukalas
476 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
477 9f1a1bd0 Georgios D. Tsoukalas
        try:
478 9f1a1bd0 Georgios D. Tsoukalas
            items = list(item)
479 9f1a1bd0 Georgios D. Tsoukalas
        except TypeError, e:
480 82b4be2b Georgios D. Tsoukalas
            m = "%s: %s is not iterable" % (self, shorts(item))
481 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
482 9f1a1bd0 Georgios D. Tsoukalas
483 9f1a1bd0 Georgios D. Tsoukalas
        canonicals = self.args
484 9f1a1bd0 Georgios D. Tsoukalas
        zi = len(items)
485 9f1a1bd0 Georgios D. Tsoukalas
        zc = len(canonicals)
486 9f1a1bd0 Georgios D. Tsoukalas
487 9f1a1bd0 Georgios D. Tsoukalas
        if zi != zc:
488 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: expecting %d elements, not %d (%s)" % (self, zc, zi, str(items))
489 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
490 9f1a1bd0 Georgios D. Tsoukalas
491 9f1a1bd0 Georgios D. Tsoukalas
        g = (canonical(element) for canonical, element in zip(self.args, item))
492 9f1a1bd0 Georgios D. Tsoukalas
493 9f1a1bd0 Georgios D. Tsoukalas
        return tuple(g)
494 9f1a1bd0 Georgios D. Tsoukalas
495 9f1a1bd0 Georgios D. Tsoukalas
    def __add__(self, other):
496 9f1a1bd0 Georgios D. Tsoukalas
        oargs = other.args if isinstance(other, Tuple) else (other,)
497 9f1a1bd0 Georgios D. Tsoukalas
        args = self.args + oargs
498 9f1a1bd0 Georgios D. Tsoukalas
        return self.__class__(*args)
499 9f1a1bd0 Georgios D. Tsoukalas
500 ac4d0f37 Georgios D. Tsoukalas
    def random_tuple(self, kw):
501 ac4d0f37 Georgios D. Tsoukalas
        return tuple(c.random() for c in self.args)
502 ac4d0f37 Georgios D. Tsoukalas
503 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_tuple
504 ac4d0f37 Georgios D. Tsoukalas
505 9f1a1bd0 Georgios D. Tsoukalas
506 9f1a1bd0 Georgios D. Tsoukalas
class Dict(Canonical):
507 9f1a1bd0 Georgios D. Tsoukalas
508 82b4be2b Georgios D. Tsoukalas
    def check(self, item):
509 9f1a1bd0 Georgios D. Tsoukalas
510 9f1a1bd0 Georgios D. Tsoukalas
        try:
511 9f1a1bd0 Georgios D. Tsoukalas
            item = dict(item)
512 9f1a1bd0 Georgios D. Tsoukalas
        except TypeError:
513 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: '%s' is not dict-able" % (self, shorts(item))
514 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
515 9f1a1bd0 Georgios D. Tsoukalas
516 9f1a1bd0 Georgios D. Tsoukalas
        canonified = {}
517 05fd3edd Georgios D. Tsoukalas
        canonical = self.kw
518 05fd3edd Georgios D. Tsoukalas
519 05fd3edd Georgios D. Tsoukalas
        for n, c in canonical.items():
520 05fd3edd Georgios D. Tsoukalas
            if n not in item:
521 05fd3edd Georgios D. Tsoukalas
                m = "%s: key '%s' not found" % (self, shorts(n))
522 05fd3edd Georgios D. Tsoukalas
                raise CanonifyException(m)
523 9f1a1bd0 Georgios D. Tsoukalas
            canonified[n] = c(item[n])   
524 9f1a1bd0 Georgios D. Tsoukalas
525 05fd3edd Georgios D. Tsoukalas
        strict = self.opts.get('strict', False)
526 05fd3edd Georgios D. Tsoukalas
        if strict and len(item) != len(canonical):
527 05fd3edd Georgios D. Tsoukalas
            for k in sorted(item.keys()):
528 05fd3edd Georgios D. Tsoukalas
                if k not in canonical:
529 05fd3edd Georgios D. Tsoukalas
                    break
530 05fd3edd Georgios D. Tsoukalas
531 05fd3edd Georgios D. Tsoukalas
            m = "%s: unexpected key '%s' (strict mode)" % (self, shorts(k))
532 05fd3edd Georgios D. Tsoukalas
            raise CanonifyException(m)
533 05fd3edd Georgios D. Tsoukalas
534 9f1a1bd0 Georgios D. Tsoukalas
        return canonified
535 9f1a1bd0 Georgios D. Tsoukalas
536 ac4d0f37 Georgios D. Tsoukalas
    def random_dict(self, kw):
537 ac4d0f37 Georgios D. Tsoukalas
        item = {}
538 05fd3edd Georgios D. Tsoukalas
        for n, c in self.canonical.items():
539 ac4d0f37 Georgios D. Tsoukalas
            item[n] = c.random()
540 ac4d0f37 Georgios D. Tsoukalas
541 ac4d0f37 Georgios D. Tsoukalas
        return item
542 ac4d0f37 Georgios D. Tsoukalas
543 ac4d0f37 Georgios D. Tsoukalas
    random_choice = random_dict
544 ac4d0f37 Georgios D. Tsoukalas
545 9f1a1bd0 Georgios D. Tsoukalas
546 9f1a1bd0 Georgios D. Tsoukalas
class Canonifier(object):
547 9f1a1bd0 Georgios D. Tsoukalas
    def __init__(self, name, input_canonicals, output_canonicals):
548 9f1a1bd0 Georgios D. Tsoukalas
        self.name = name
549 9f1a1bd0 Georgios D. Tsoukalas
        self.input_canonicals = dict(input_canonicals)
550 9f1a1bd0 Georgios D. Tsoukalas
        self.output_canonicals = dict(output_canonicals)
551 9f1a1bd0 Georgios D. Tsoukalas
552 9f1a1bd0 Georgios D. Tsoukalas
    def call_names(self):
553 9f1a1bd0 Georgios D. Tsoukalas
        return self.input_canonicals.keys()
554 9f1a1bd0 Georgios D. Tsoukalas
555 82b4be2b Georgios D. Tsoukalas
    def call_attrs(self):
556 82b4be2b Georgios D. Tsoukalas
        for call_name, canonical in self.input_canonicals.iteritems():
557 82b4be2b Georgios D. Tsoukalas
            yield call_name, canonical.tostring(showopts=1, multiline=1)
558 82b4be2b Georgios D. Tsoukalas
559 9f1a1bd0 Georgios D. Tsoukalas
    def input_canonical(self, name):
560 9f1a1bd0 Georgios D. Tsoukalas
        input_canonicals = self.input_canonicals
561 9f1a1bd0 Georgios D. Tsoukalas
        if name not in input_canonicals:
562 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: Invalid input call '%s'" % (self.name, name)
563 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
564 9f1a1bd0 Georgios D. Tsoukalas
565 9f1a1bd0 Georgios D. Tsoukalas
        return input_canonicals[name]
566 9f1a1bd0 Georgios D. Tsoukalas
567 9f1a1bd0 Georgios D. Tsoukalas
    def canonify_input(self, name, the_input):
568 9f1a1bd0 Georgios D. Tsoukalas
        return self.input_canonical(name)(the_input)
569 9f1a1bd0 Georgios D. Tsoukalas
570 9f1a1bd0 Georgios D. Tsoukalas
    def output_canonical(self, name):
571 9f1a1bd0 Georgios D. Tsoukalas
        output_canonicals = self.output_canonicals
572 9f1a1bd0 Georgios D. Tsoukalas
        if name not in output_canonicals:
573 9f1a1bd0 Georgios D. Tsoukalas
            m = "%s: Output canonical '%s' does not exist" % (self.name, name)
574 9f1a1bd0 Georgios D. Tsoukalas
            raise CanonifyException(m)
575 9f1a1bd0 Georgios D. Tsoukalas
576 9f1a1bd0 Georgios D. Tsoukalas
        return output_canonicals[name]
577 9f1a1bd0 Georgios D. Tsoukalas
578 9f1a1bd0 Georgios D. Tsoukalas
    def canonify_output(self, name, the_output):
579 9f1a1bd0 Georgios D. Tsoukalas
        return self.output_canonical(name)(the_output)
580 9f1a1bd0 Georgios D. Tsoukalas
581 9f1a1bd0 Georgios D. Tsoukalas
582 9f1a1bd0 Georgios D. Tsoukalas
class Specificator(object):
583 9f1a1bd0 Georgios D. Tsoukalas
584 9f1a1bd0 Georgios D. Tsoukalas
    def __new__(cls):
585 9f1a1bd0 Georgios D. Tsoukalas
        if cls is Specificator:
586 9f1a1bd0 Georgios D. Tsoukalas
            m = "Specificator classes must be subclassed"
587 9f1a1bd0 Georgios D. Tsoukalas
            raise SpecifyException(m)
588 9f1a1bd0 Georgios D. Tsoukalas
589 9f1a1bd0 Georgios D. Tsoukalas
        import inspect
590 9f1a1bd0 Georgios D. Tsoukalas
591 9f1a1bd0 Georgios D. Tsoukalas
        canonical_inputs = {}
592 9f1a1bd0 Georgios D. Tsoukalas
        canonical_outputs = {}
593 9f1a1bd0 Georgios D. Tsoukalas
594 9f1a1bd0 Georgios D. Tsoukalas
        for name in dir(cls):
595 9f1a1bd0 Georgios D. Tsoukalas
            f = getattr(cls, name)
596 9f1a1bd0 Georgios D. Tsoukalas
            if not inspect.ismethod(f) or f.__name__.startswith('_'):
597 9f1a1bd0 Georgios D. Tsoukalas
                continue
598 9f1a1bd0 Georgios D. Tsoukalas
599 9f1a1bd0 Georgios D. Tsoukalas
            argspec = inspect.getargspec(f)
600 9f1a1bd0 Georgios D. Tsoukalas
            defaults = argspec.defaults
601 9f1a1bd0 Georgios D. Tsoukalas
            args = argspec.args
602 9f1a1bd0 Georgios D. Tsoukalas
            if args and args[0] == 'self':
603 9f1a1bd0 Georgios D. Tsoukalas
                args = args[1:]
604 9f1a1bd0 Georgios D. Tsoukalas
605 9f1a1bd0 Georgios D. Tsoukalas
            if not defaults:
606 9f1a1bd0 Georgios D. Tsoukalas
                defaults = ()
607 9f1a1bd0 Georgios D. Tsoukalas
608 9f1a1bd0 Georgios D. Tsoukalas
            arglen = len(args)
609 9f1a1bd0 Georgios D. Tsoukalas
            deflen = len(defaults)
610 9f1a1bd0 Georgios D. Tsoukalas
611 9f1a1bd0 Georgios D. Tsoukalas
            if arglen != deflen:
612 9f1a1bd0 Georgios D. Tsoukalas
                a = (f.__name__, args[:arglen-deflen])
613 9f1a1bd0 Georgios D. Tsoukalas
                m = "Unspecified arguments in '%s': %s" % a
614 9f1a1bd0 Georgios D. Tsoukalas
                raise SpecifyException(m)
615 9f1a1bd0 Georgios D. Tsoukalas
616 9f1a1bd0 Georgios D. Tsoukalas
            args = dict(zip(args, defaults))
617 9f1a1bd0 Georgios D. Tsoukalas
            for a, c in args.items():
618 9f1a1bd0 Georgios D. Tsoukalas
                if not isinstance(c, Canonical):
619 9f1a1bd0 Georgios D. Tsoukalas
                    m = ("argument '%s=%s' is not an instance of 'Canonical'"
620 9f1a1bd0 Georgios D. Tsoukalas
                         % (a, repr(c)))
621 9f1a1bd0 Georgios D. Tsoukalas
                    raise SpecifyException(m)
622 9f1a1bd0 Georgios D. Tsoukalas
623 9f1a1bd0 Georgios D. Tsoukalas
            canonical = Null() if len(args) == 0 else Args(**args)
624 9f1a1bd0 Georgios D. Tsoukalas
            canonical_inputs[name] = canonical
625 9f1a1bd0 Georgios D. Tsoukalas
626 9f1a1bd0 Georgios D. Tsoukalas
            self = object.__new__(cls)
627 9f1a1bd0 Georgios D. Tsoukalas
            canonical = f(self)
628 9f1a1bd0 Georgios D. Tsoukalas
            if not isinstance(canonical, Canonical):
629 82b4be2b Georgios D. Tsoukalas
                m = ("method '%s' does not return a Canonical, but a(n) %s "
630 82b4be2b Georgios D. Tsoukalas
                                                    % (name, type(canonical)))
631 9f1a1bd0 Georgios D. Tsoukalas
                raise SpecifyException(m)
632 9f1a1bd0 Georgios D. Tsoukalas
            canonical_outputs[name] = canonical
633 9f1a1bd0 Georgios D. Tsoukalas
634 9f1a1bd0 Georgios D. Tsoukalas
        return Canonifier(cls.__name__, canonical_inputs, canonical_outputs)
635 9f1a1bd0 Georgios D. Tsoukalas
636 9f1a1bd0 Georgios D. Tsoukalas
    def __call__(self):
637 9f1a1bd0 Georgios D. Tsoukalas
        return self