settings.py 23.9 KB
Newer Older
1 2 3 4 5 6 7
# -*- coding: utf-8 -*-
#
# This file is part of the bliss project
#
# Copyright (c) 2016 Beamline Control Unit, ESRF
# Distributed under the GNU LGPLv3. See LICENSE for more info.

8
from .conductor import client
9
from bliss.common.utils import Null
10
from bliss import setup_globals
Matias Guijarro's avatar
Matias Guijarro committed
11 12 13
import weakref
import pickle

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
14

Matias Guijarro's avatar
Matias Guijarro committed
15 16 17
class InvalidValue(Null):
    def __str__(self):
        raise ValueError
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
18

Matias Guijarro's avatar
Matias Guijarro committed
19 20
    def __repr__(self):
        return '#ERR'
21

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
22

Matias Guijarro's avatar
Matias Guijarro committed
23
def get_cache():
24
    return client.get_cache(db=0)
Matias Guijarro's avatar
Matias Guijarro committed
25

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
26 27

def boolify(s, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
28
    if s == 'True' or s == 'true':
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
29
        return True
Matias Guijarro's avatar
Matias Guijarro committed
30
    if s == 'False' or s == 'false':
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
31
        return False
Matias Guijarro's avatar
Matias Guijarro committed
32 33
    raise ValueError('Not Boolean Value!')

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
34

Matias Guijarro's avatar
Matias Guijarro committed
35 36 37 38
def auto_conversion(var):
    '''guesses the str representation of the variables type'''
    if var is None:
        return None
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
39
    for caster in (boolify, int, float):
Matias Guijarro's avatar
Matias Guijarro committed
40 41
        try:
            return caster(var)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
42
        except (ValueError, TypeError):
Matias Guijarro's avatar
Matias Guijarro committed
43 44 45
            pass
    return var

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
46

47
def pickle_loads(var):
Matias Guijarro's avatar
Matias Guijarro committed
48 49
    if var is None:
        return None
Matias Guijarro's avatar
Matias Guijarro committed
50 51 52 53
    try:
        return pickle.loads(var)
    except Exception:
        return InvalidValue()
Matias Guijarro's avatar
Matias Guijarro committed
54

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
55 56

def ttl_func(cnx, name, value=-1):
Matias Guijarro's avatar
Matias Guijarro committed
57 58 59 60 61
    if value is None:
        return cnx.persist(name)
    elif value is -1:
        return cnx.ttl(name)
    else:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
62 63
        return cnx.expire(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
64

65
def read_decorator(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
66 67
    def _read(self, *args, **keys):
        value = func(self, *args, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
68
        if self._read_type_conversion:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
69
            if isinstance(value, list):
Matias Guijarro's avatar
Matias Guijarro committed
70
                value = [self._read_type_conversion(x) for x in value]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
71 72
            elif isinstance(value, dict):
                for k, v in value.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
73
                    value[k] = self._read_type_conversion(v)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
74
                if hasattr(self, 'default_values') and isinstance(self.default_values, dict):
75 76 77
                    tmp = dict(self._default_values)
                    tmp.update(value)
                    value = tmp
Matias Guijarro's avatar
Matias Guijarro committed
78
            else:
79 80
                if value is not None:
                    value = self._read_type_conversion(value)
81
        if value is None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
82
            if hasattr(self, '_default_value'):
83
                value = self._default_value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
84 85
            elif(hasattr(self, '_default_values') and
                 hasattr(self._default_values, 'get')):
86
                value = self._default_values.get(args[0])
Matias Guijarro's avatar
Matias Guijarro committed
87 88 89
        return value
    return _read

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
90

91
def write_decorator_dict(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
92
    def _write(self, values, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
93
        if self._write_type_conversion:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
94
            if not isinstance(values, dict) and values is not None:
Matias Guijarro's avatar
Matias Guijarro committed
95 96 97
                raise TypeError('can only be dict')

            if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
98
                for k, v in values.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
99
                    values[k] = self._write_type_conversion(v)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
100
        return func(self, values, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
101 102
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
103

104
def write_decorator_multiple(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
105
    def _write(self, values, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
106
        if self._write_type_conversion:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
107
            if not isinstance(values, (list, tuple)) and values is not None:
Matias Guijarro's avatar
Matias Guijarro committed
108 109 110
                raise TypeError('can only be tuple or list')
            if values is not None:
                values = [self._write_type_conversion(x) for x in values]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
111
        return func(self, values, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
112 113
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
114

115
def write_decorator(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
116
    def _write(self, value, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
117 118
        if self._write_type_conversion and value is not None:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
119
        return func(self, value, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
120 121
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
122 123

def scan(match='*', count=1000, connection=None):
124 125 126 127
    if connection is None:
        connection = get_cache()
    cursor = 0
    while 1:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
128 129
        cursor, values = connection.scan(cursor=cursor,
                                         match=match, count=count)
130 131 132 133
        for val in values:
            yield val
        if int(cursor) == 0:
            break
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
134 135


136
class SimpleSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
137 138 139 140
    def __init__(self, name, connection=None,
                 read_type_conversion=auto_conversion,
                 write_type_conversion=None,
                 default_value=None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
141 142 143 144 145 146
        if connection is None:
            connection = get_cache()
        self._cnx = weakref.ref(connection)
        self._name = name
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
147
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
148

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
149
    @read_decorator
150
    def get(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
151 152 153 154 155
        cnx = self._cnx()
        value = cnx.get(self._name)
        return value

    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
156
    def set(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
157
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
158 159 160 161
        cnx.set(self._name, value)

    def ttl(self, value=-1):
        return ttl_func(self._cnx(), self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
162

163 164 165
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
166 167

    def __add__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
168
        value = self.get()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
169
        if isinstance(other, SimpleSetting):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
170 171 172
            other = other.get()
        return value + other

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
173
    def __iadd__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
174 175
        cnx = self._cnx()
        if cnx is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
176
            if isinstance(other, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
177 178
                if other == 1:
                    cnx.incr(self._name)
Matias Guijarro's avatar
Matias Guijarro committed
179
                else:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
180 181 182
                    cnx.incrby(self._name, other)
            elif isinstance(other, float):
                cnx.incrbyfloat(self._name, other)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
183
            else:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
184
                cnx.append(self._name, other)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
185
            return self
Matias Guijarro's avatar
Matias Guijarro committed
186

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
187 188 189 190
    def __isub__(self, other):
        if isinstance(other, basestring):
            raise TypeError("unsupported operand type(s) for -=: %s" %
                            type(other).__name__)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
191
        return self.__iadd__(-other)
Matias Guijarro's avatar
Matias Guijarro committed
192

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
193
    def __getitem__(self, ran):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
194 195 196
        cnx = self._cnx()
        if cnx is not None:
            step = None
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
197 198
            if isinstance(ran, slice):
                i, j = ran.start, ran.stop
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
199
                step = ran.step
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
200
            elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
201 202 203 204
                i = j = ran
            else:
                raise TypeError('indices must be integers')

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
205
            value = cnx.getrange(self._name, i, j)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
206 207 208 209 210 211 212
            if step is not None:
                value = value[0:-1:step]
            return value

    def __repr__(self):
        cnx = self._cnx()
        value = cnx.get(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
213 214
        return '<SimpleSetting name=%s value=%s>' % (self._name, value)

Matias Guijarro's avatar
Matias Guijarro committed
215

216
class SimpleSettingProp(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
217 218 219 220 221
    def __init__(self, name, connection=None,
                 read_type_conversion=auto_conversion,
                 write_type_conversion=None,
                 default_value=None,
                 use_object_name=True):
Matias Guijarro's avatar
Matias Guijarro committed
222 223 224 225
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
226
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
227 228
        self._use_object_name = use_object_name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
229
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
230 231 232 233
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
234
        return SimpleSetting(name, self._cnx,
235 236 237
                             self._read_type_conversion,
                             self._write_type_conversion,
                             self._default_value)
Matias Guijarro's avatar
Matias Guijarro committed
238

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
239 240 241
    def __set__(self, obj, value):
        if isinstance(value, SimpleSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
242 243 244 245 246 247 248 249 250 251 252

        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

        if value is None:
            self._cnx.delete(name)
        else:
            if self._write_type_conversion:
                value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
253 254
            self._cnx.set(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
255 256

class QueueSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
257 258 259 260 261
    def __init__(self, name, connection=None,
                 read_type_conversion=auto_conversion,
                 write_type_conversion=None):
        if connection is None:
            connection = get_cache()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
262 263 264 265
        self._cnx = weakref.ref(connection)
        self._name = name
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
Matias Guijarro's avatar
Matias Guijarro committed
266

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
267
    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
268
    def get(self, first=0, last=-1):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
269 270
        cnx = self._cnx()
        if first == last:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
271
            l = cnx.lindex(self._name, first)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
272
        else:
273 274
            if last != -1:
                last -= 1
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
275
            l = cnx.lrange(self._name, first, last)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
276
        return l
Matias Guijarro's avatar
Matias Guijarro committed
277

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
278
    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
279
    def append(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
280
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
281
        return cnx.rpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
282

283 284 285 286
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
287
    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
288
    def prepend(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
289
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
290
        return cnx.lpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
291

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
292
    @write_decorator_multiple
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
293
    def extend(self, values):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
294
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
295
        return cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
296

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
297
    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
298
    def remove(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
299
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
300
        cnx.lrem(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
301

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
302
    @write_decorator_multiple
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
303 304
    def set(self, values):
        if not isinstance(values, (list, tuple)) and values is not None:
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
305
            raise TypeError('can only be tuple or list')
Matias Guijarro's avatar
Matias Guijarro committed
306

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
307 308 309
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
310
            cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
311

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
312
    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
313
    def set_item(self, value, pos=0):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
314
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
315
        cnx.lset(self._name, pos, value)
Matias Guijarro's avatar
Matias Guijarro committed
316

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
317
    @read_decorator
318
    def pop_front(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
319 320 321 322 323 324 325
        cnx = self._cnx()
        value = cnx.lpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    @read_decorator
326
    def pop_back(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
327 328 329 330 331 332
        cnx = self._cnx()
        value = cnx.rpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
333 334
    def ttl(self, value=-1):
        return ttl_func(self._cnx(), self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
335

336
    def __len__(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
337 338 339
        cnx = self._cnx()
        return cnx.llen(self._name)

340
    def __repr__(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
341
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
342 343
        value = cnx.lrange(self._name, 0, -1)
        return '<QueueSetting name=%s value=%s>' % (self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
344

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
345
    def __iadd__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
346 347 348
        self.extend(other)
        return self

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
349 350
    def __getitem__(self, ran):
        if isinstance(ran, slice):
351 352
            i = ran.start is not None and ran.start or 0
            j = ran.stop is not None and ran.stop or -1
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
353
        elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
354 355 356
            i = j = ran
        else:
            raise TypeError('indices must be integers')
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
357
        value = self.get(first=i, last=j)
358 359 360 361
        if value is None:
            raise StopIteration
        else:
            return value
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
362

363
    def __iter__(self):
364 365
        cnx = self._cnx()
        lsize = cnx.llen(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
366
        for first in xrange(0, lsize, 1024):
367
            last = first + 1024
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
368 369 370
            if last >= lsize:
                last = -1
            for value in self.get(first, last):
371
                yield value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
372 373 374 375 376 377 378

    def __setitem__(self, ran, value):
        if isinstance(ran, slice):
            for i, v in zip(range(ran.start, ran.stop), value):
                self.set_item(v, pos=i)
        elif isinstance(ran, int):
            self.set_item(value, pos=ran)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
379 380 381 382
        else:
            raise TypeError('indices must be integers')
        return self

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
383

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
384
class QueueSettingProp(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
385 386 387 388
    def __init__(self, name, connection=None,
                 read_type_conversion=auto_conversion,
                 write_type_conversion=None,
                 use_object_name=True):
Matias Guijarro's avatar
Matias Guijarro committed
389 390 391 392 393 394
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
        self._use_object_name = use_object_name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
395
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
396 397 398 399 400
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
401
        return QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
402 403
                            self._read_type_conversion,
                            self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
404

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
405 406 407
    def __set__(self, obj, values):
        if isinstance(values, QueueSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
408 409 410 411 412 413

        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
414
        proxy = QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
415 416
                             self._read_type_conversion,
                             self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
417 418
        proxy.set(values)

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
419

Matias Guijarro's avatar
Matias Guijarro committed
420
class HashSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
421
    def __init__(self, name, connection=None,
Matias Guijarro's avatar
Matias Guijarro committed
422
                 read_type_conversion=auto_conversion,
423
                 write_type_conversion=None,
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
424
                 default_values={}):
Matias Guijarro's avatar
Matias Guijarro committed
425 426 427 428 429 430
        if connection is None:
            connection = get_cache()
        self._cnx = weakref.ref(connection)
        self._name = name
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
431
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
432

433 434
    def __repr__(self):
        value = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
435 436 437
        return '<HashSetting name=%s value=%s>' % (self._name, value)

    def __delitem__(self, key):
438
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
439
        cnx.hdel(self._name, key)
Matias Guijarro's avatar
Matias Guijarro committed
440

441
    def __len__(self):
Matias Guijarro's avatar
Matias Guijarro committed
442 443 444
        cnx = self._cnx()
        return cnx.hlen(self._name)

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
445 446
    def ttl(self, value=-1):
        return ttl_func(self._cnx(), self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
447

Matias Guijarro's avatar
Matias Guijarro committed
448
    def raw_get(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
449
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
450 451 452
        return cnx.hget(self._name, key)

    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
453
    def get(self, key, default=None):
Matias Guijarro's avatar
Matias Guijarro committed
454 455
        v = self.raw_get(key)
        if v is None:
456 457 458 459
            if self._write_type_conversion:
                v = self._write_type_conversion(default)
            else:
                v = default
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
460
        return v
Matias Guijarro's avatar
Matias Guijarro committed
461

Matias Guijarro's avatar
Matias Guijarro committed
462 463 464
    def _raw_get_all(self):
        cnx = self._cnx()
        return cnx.hgetall(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
465

Matias Guijarro's avatar
Matias Guijarro committed
466 467 468
    def get_all(self):
        all_dict = dict(self._default_values)
        for k, raw_v in self._raw_get_all().iteritems():
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
469 470 471 472 473
            v = self._read_type_conversion(raw_v)
            if isinstance(v, InvalidValue):
                raise ValueError(
                    "%s: Invalid value '%s` (cannot deserialize %r)" % (self._name, k, raw_v))
            all_dict[k] = v
Matias Guijarro's avatar
Matias Guijarro committed
474
        return all_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
475

Matias Guijarro's avatar
Matias Guijarro committed
476
    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
477
    def pop(self, key, default=Null()):
478
        cnx = self._cnx().pipeline()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
479 480 481
        cnx.hget(self._name, key)
        cnx.hdel(self._name, key)
        (value, worked) = cnx.execute()
482
        if not worked:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
483
            if isinstance(default, Null):
484 485 486
                raise KeyError(key)
            else:
                value = default
Matias Guijarro's avatar
Matias Guijarro committed
487
        return value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
488

Matias Guijarro's avatar
Matias Guijarro committed
489
    def remove(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
490
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
491
        cnx.hdel(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
492

493
    def keys(self):
494
        return list(self.iterkeys())
Matias Guijarro's avatar
Matias Guijarro committed
495

496
    def values(self):
497
        return list(self.itervalues())
Matias Guijarro's avatar
Matias Guijarro committed
498

499
    def clear(self):
Matias Guijarro's avatar
Matias Guijarro committed
500 501 502
        cnx = self._cnx()
        cnx.delete(self._name)

503
    def copy(self):
Matias Guijarro's avatar
Matias Guijarro committed
504 505 506
        return self.get()

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
507
    def set(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
508 509 510
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
511
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
512 513

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
514
    def update(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
515
        cnx = self._cnx()
516
        if values:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
517
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
518

519
    def items(self):
Matias Guijarro's avatar
Matias Guijarro committed
520
        values = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
521
        return [(k, v) for k, v in values.iteritems()]
Matias Guijarro's avatar
Matias Guijarro committed
522 523

    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
524
    def fromkeys(self, keys):
Matias Guijarro's avatar
Matias Guijarro committed
525
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
526 527 528
        return cnx.hmget(self._name, keys)

    def has_key(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
529
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
530 531
        return cnx.hexists(self._name, key) or self._default_values.has_key(key)

Matias Guijarro's avatar
Matias Guijarro committed
532
    def iterkeys(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
533
        for k, v in self.iteritems():
534 535
            yield k

Matias Guijarro's avatar
Matias Guijarro committed
536
    def itervalues(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
537
        for k, v in self.iteritems():
538
            yield v
Matias Guijarro's avatar
Matias Guijarro committed
539

540
    def iteritems(self):
Matias Guijarro's avatar
Matias Guijarro committed
541 542
        cnx = self._cnx()
        next_id = 0
543
        seen_keys = set()
Matias Guijarro's avatar
Matias Guijarro committed
544
        while True:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
545 546
            next_id, pd = cnx.hscan(self._name, next_id)
            for k, v in pd.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
547 548
                if self._read_type_conversion:
                    v = self._read_type_conversion(v)
549
                seen_keys.add(k)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
550
                yield k, v
551
            if not next_id or next_id is '0':
Matias Guijarro's avatar
Matias Guijarro committed
552 553
                break

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
554 555 556 557
        for k, v in self._default_values.iteritems():
            if k in seen_keys:
                continue
            yield k, v
558

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
559
    def __getitem__(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
560 561
        value = self.get(key)
        if value is None:
562 563
            if not self._default_values.has_key(key):
                raise KeyError(key)
Matias Guijarro's avatar
Matias Guijarro committed
564 565
        return value

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
566
    def __setitem__(self, key, value):
Matias Guijarro's avatar
Matias Guijarro committed
567
        cnx = self._cnx()
568
        if value is None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
569
            cnx.hdel(self._name, key)
570
            return
Matias Guijarro's avatar
Matias Guijarro committed
571 572
        if self._write_type_conversion:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
573
        cnx.hset(self._name, key, value)
Matias Guijarro's avatar
Matias Guijarro committed
574

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
575
    def __contains__(self, key):
576 577 578 579 580 581 582
        try:
            self[key]
            return True
        except KeyError:
            return False


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
583 584 585 586 587 588
class HashSettingProp(object):
    def __init__(self, name, connection=None,
                 read_type_conversion=auto_conversion,
                 write_type_conversion=None,
                 default_values={},
                 use_object_name=True):
Matias Guijarro's avatar
Matias Guijarro committed
589 590 591 592
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
593
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
594 595
        self._use_object_name = use_object_name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
596
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
597 598 599 600
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
601 602

        return HashSetting(name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
603
                           self._read_type_conversion,
604 605
                           self._write_type_conversion,
                           self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
606

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
607
    def __set__(self, obj, values):
Matias Guijarro's avatar
Matias Guijarro committed
608 609 610 611 612
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
613 614
        if isinstance(values, HashSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
615

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
616
        proxy = HashSetting(name, self._cnx,
617 618 619
                            self._read_type_conversion,
                            self._write_type_conversion,
                            self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
620
        proxy.set(values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
621

622
    def get_proxy(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
623
        return HashSetting(self._name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
624
                           self._read_type_conversion,
625 626
                           self._write_type_conversion,
                           self._default_values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
627 628
# helper

Matias Guijarro's avatar
Matias Guijarro committed
629

630
def _change_to_obj_marshalling(keys):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
631 632 633 634 635
    read_type_conversion = keys.pop('read_type_conversion', pickle_loads)
    write_type_conversion = keys.pop('write_type_conversion', pickle.dumps)
    keys.update({'read_type_conversion': read_type_conversion,
                 'write_type_conversion': write_type_conversion})

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
636

637
class HashObjSetting(HashSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
638
    def __init__(self, name, **keys):
639
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
640 641
        HashSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
642

643
class HashObjSettingProp(HashSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
644
    def __init__(self, name, **keys):
645
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
646 647
        HashSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
648

649
class QueueObjSetting(QueueSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
650
    def __init__(self, name, **keys):
651
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
652 653
        QueueSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
654 655

class QueueObjSettingProp(QueueSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
656
    def __init__(self, name, **keys):
657
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
658 659
        QueueSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
660

661
class SimpleObjSetting(SimpleSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
662
    def __init__(self, name, **keys):
663
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
664 665
        SimpleSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
666 667

class SimpleObjSettingProp(SimpleSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
668
    def __init__(self, name, **keys):
669
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
670 671
        SimpleSettingProp.__init__(self, name, **keys)

Matias Guijarro's avatar
Matias Guijarro committed
672

673
class Struct(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
674 675
    def __init__(self, name, **keys):
        self._proxy = HashSetting(name, **keys)
676

677 678
    def __dir__(self):
        return self._proxy.keys()
679

680 681
    def __repr__(self):
        return "<Struct with attributes: %s>" % self._proxy.keys()
682

683 684
    def __getattribute__(self, name):
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
685
            return object.__getattribute__(self, name)
686 687
        else:
            return self._proxy.get(name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
688 689

    def __setattr__(self, name, value):
690
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
691
            return object.__setattr__(self, name, value)
692 693
        else:
            self._proxy[name] = value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
694 695

    def __delattr__(self, name):
696
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
697
            return object.__delattr__(self, name)
698 699
        else:
            self._proxy.remove(name)
700 701


702
class ParametersType(type):
703
    def __call__(cls, *args, **kwargs):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
704
        class_dict = {'__slots__': tuple(cls.SLOTS), 'SLOTS': cls.SLOTS}
705 706 707 708 709 710
        new_cls = type(cls.__name__, (cls,), class_dict)
        return type.__call__(new_cls, *args, **kwargs)

    def __new__(cls, name, bases, attrs):
        attrs['__slots__'] = tuple(attrs['SLOTS'])
        return type.__new__(cls, name, bases, attrs)
Matias Guijarro's avatar
Matias Guijarro committed
711

712

713 714
class ParamDescriptor(object):
    OBJECT_PREFIX = 'object:'
715

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
716 717
    def __init__(self, proxy, name, value, assign=True):
        self.proxy = proxy
718 719 720
        self.name = name
        if assign:
            self.assign(value)
721

722 723 724
    def assign(self, value):
        if hasattr(value, 'name') and hasattr(setup_globals, value.name):
            value = '%s%s' % (ParamDescriptor.OBJECT_PREFIX, value.name)
Matias Guijarro's avatar
Matias Guijarro committed
725 726 727
        try:
            self.proxy[self.name] = value
        except Exception:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
728 729
            raise ValueError("%s.%s: cannot set value" %
                             (self.proxy._name, self.name))
730 731 732

    def __get__(self, obj, obj_type):
        value = self.proxy[self.name]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
733
        if isinstance(value, (str, unicode)) and value.startswith(ParamDescriptor.OBJECT_PREFIX):
734 735
            value = value[len(ParamDescriptor.OBJECT_PREFIX):]
            return getattr(setup_globals, value)
Matias Guijarro's avatar
Matias Guijarro committed
736
        return value
737

738 739 740 741 742
    def __set__(self, obj, value):
        return self.assign(value)

    def __delete__(self, *args):
        del self.proxy[self.name]
743 744


745 746
class Parameters(object):
    __metaclass__ = ParametersType
747
    DESCRIPTOR = ParamDescriptor
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
748
    SLOTS = ['_proxy', '__current_config']
749

750 751
    def __init__(self, name, **keys):
        self.__current_config = SimpleSetting(name, default_value='default')
752 753 754 755
        hash_name = '%s:%s' % (name, self.__current_config.get())
        self._proxy = HashSetting(hash_name, **keys)
        for key in self._proxy.iterkeys():
            self.add(key)
756 757

    def __dir__(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
758 759
        return self._proxy.keys() + ['add', 'remove', 'switch', 'configs']

760
    def __repr__(self):
761
        rep_str = "Parameters (%s)\n" % self.__current_config.get()
762 763
        d = dict(self._proxy.iteritems())
        max_len = max((len(x) for x in d.keys()))
764
        str_format = '  .%-' + '%ds' % max_len + ' = %r\n'
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
765 766
        for key, value in sorted(d.iteritems()):
            rep_str += str_format % (key, value)
767
        return rep_str
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
768 769

    def add(self, name, value=None):
770 771 772 773 774 775
        setattr(self.__class__, name, self.DESCRIPTOR(self._proxy, name, value,
                                                      value is not None))

    def remove(self, name):
        self._proxy.remove(name)
        delattr(self.__class__, name)
776

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
777 778
    def switch(self, name):
        for key, value in dict(self.__class__.__dict__).iteritems():
779
            if isinstance(value, self.DESCRIPTOR):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
780 781
                delattr(self.__class__, key)

782 783 784 785
        self.__current_config.set(name)

        basename = ":".join(self._proxy._name.split(':')[:-1])
        self._proxy._name = '%s:%s' % (basename, name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
786

787 788
        for key in self._proxy.keys():
            self.add(key)