settings.py 24.7 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
import weakref
import pickle
13
import numpy
Matias Guijarro's avatar
Matias Guijarro committed
14

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
15

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

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
23

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
27 28

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
35

Matias Guijarro's avatar
Matias Guijarro committed
36 37 38 39
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
40
    for caster in (boolify, int, float):
Matias Guijarro's avatar
Matias Guijarro committed
41 42
        try:
            return caster(var)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
43
        except (ValueError, TypeError):
Matias Guijarro's avatar
Matias Guijarro committed
44 45 46
            pass
    return var

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
47

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
56 57

def ttl_func(cnx, name, value=-1):
Matias Guijarro's avatar
Matias Guijarro committed
58 59 60 61 62
    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
63 64
        return cnx.expire(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
65

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
91

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

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
106

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
117

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
125 126

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


139
class SimpleSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
140 141 142 143
    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
144 145 146 147 148 149
        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
150
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
151

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

    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
159
    def set(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
160
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
161 162 163 164
        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
165

166 167 168
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
169 170

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

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
190 191 192 193
    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
194
        return self.__iadd__(-other)
Matias Guijarro's avatar
Matias Guijarro committed
195

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
208
            value = cnx.getrange(self._name, i, j)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
209 210 211 212 213 214 215
            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
216 217
        return '<SimpleSetting name=%s value=%s>' % (self._name, value)

Matias Guijarro's avatar
Matias Guijarro committed
218

219
class SimpleSettingProp(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
220 221 222 223 224
    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
225 226 227 228
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
229
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
230 231
        self._use_object_name = use_object_name

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

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

        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
256 257
            self._cnx.set(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
258 259

class QueueSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
260 261 262 263 264
    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
265 266 267 268
        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
269

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
282
    @write_decorator
283 284 285
    def append(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
286
        return cnx.rpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
287

288 289 290
    def clear(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
291 292
        cnx.delete(self._name)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
293
    @write_decorator
294 295 296
    def prepend(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
297
        return cnx.lpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
298

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
299
    @write_decorator_multiple
300
    def extend(self, values, cnx=None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
301
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
302
        return cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
303

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
304
    @write_decorator
305 306 307
    def remove(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
308
        cnx.lrem(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
309

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
310
    @write_decorator_multiple
311 312 313
    def set(self, values, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
314 315
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
316
            cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
317

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
318
    @write_decorator
319 320 321
    def set_item(self, value, pos=0, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
322
        cnx.lset(self._name, pos, value)
Matias Guijarro's avatar
Matias Guijarro committed
323

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
324
    @read_decorator
325 326 327
    def pop_front(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
328 329 330 331 332 333
        value = cnx.lpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    @read_decorator
334 335 336
    def pop_back(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
337 338 339 340 341
        value = cnx.rpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

342 343 344 345
    def ttl(self, value=-1, cnx=None):
        if cnx is None:
            cnx = self._cnx()
        return ttl_func(cnx, self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
346

347 348 349
    def __len__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
350 351
        return cnx.llen(self._name)

352 353 354
    def __repr__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
355 356
        value = cnx.lrange(self._name, 0, -1)
        return '<QueueSetting name=%s value=%s>' % (self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
357

358 359
    def __iadd__(self, other, cnx=None):
        self.extend(other, cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
360 361
        return self

362
    def __getitem__(self, ran, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
363
        if isinstance(ran, slice):
364 365
            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
366
        elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
367 368 369
            i = j = ran
        else:
            raise TypeError('indices must be integers')
370
        value = self.get(first=i, last=j, cnx=cnx)
371
        if value is None:
372
            raise IndexError
373 374
        else:
            return value
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
375

376
    def __iter__(self, cnx = None):
377 378
        if cnx is None:
            cnx = self._cnx()
379
        lsize = cnx.llen(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
380
        for first in xrange(0, lsize, 1024):
381
            last = first + 1024
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
382 383 384
            if last >= lsize:
                last = -1
            for value in self.get(first, last):
385
                yield value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
386

387
    def __setitem__(self, ran, value, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
388 389
        if isinstance(ran, slice):
            for i, v in zip(range(ran.start, ran.stop), value):
390
                self.set_item(v, pos=i, cnx=cnx)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
391
        elif isinstance(ran, int):
392
            self.set_item(value, pos=ran, cnx=cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
393 394 395 396
        else:
            raise TypeError('indices must be integers')
        return self

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
397

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
398
class QueueSettingProp(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
399 400 401 402
    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
403 404 405 406 407 408
        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
409
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
410 411 412 413 414
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
415
        return QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
416 417
                            self._read_type_conversion,
                            self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
418

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
419 420 421
    def __set__(self, obj, values):
        if isinstance(values, QueueSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
422 423 424 425 426 427

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
428
        proxy = QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
429 430
                             self._read_type_conversion,
                             self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
431 432
        proxy.set(values)

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
433

Matias Guijarro's avatar
Matias Guijarro committed
434
class HashSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
435
    def __init__(self, name, connection=None,
Matias Guijarro's avatar
Matias Guijarro committed
436
                 read_type_conversion=auto_conversion,
437
                 write_type_conversion=None,
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
438
                 default_values={}):
Matias Guijarro's avatar
Matias Guijarro committed
439 440 441 442 443 444
        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
445
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
446

447 448
    def __repr__(self):
        value = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
449 450 451
        return '<HashSetting name=%s value=%s>' % (self._name, value)

    def __delitem__(self, key):
452
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
453
        cnx.hdel(self._name, key)
Matias Guijarro's avatar
Matias Guijarro committed
454

455
    def __len__(self):
Matias Guijarro's avatar
Matias Guijarro committed
456 457 458
        cnx = self._cnx()
        return cnx.hlen(self._name)

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

462
    def raw_get(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
463
        cnx = self._cnx()
464
        return cnx.hget(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
465 466

    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
467
    def get(self, key, default=None):
Matias Guijarro's avatar
Matias Guijarro committed
468 469
        v = self.raw_get(key)
        if v is None:
470 471
            if self._read_type_conversion:
                v = self._read_type_conversion(default)
472 473
            else:
                v = default
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
474
        return v
Matias Guijarro's avatar
Matias Guijarro committed
475

Matias Guijarro's avatar
Matias Guijarro committed
476 477 478
    def _raw_get_all(self):
        cnx = self._cnx()
        return cnx.hgetall(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
479

Matias Guijarro's avatar
Matias Guijarro committed
480 481 482
    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
483 484 485 486 487
            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
488
        return all_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
489

Matias Guijarro's avatar
Matias Guijarro committed
490
    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
491
    def pop(self, key, default=Null()):
492
        cnx = self._cnx().pipeline()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
493 494 495
        cnx.hget(self._name, key)
        cnx.hdel(self._name, key)
        (value, worked) = cnx.execute()
496
        if not worked:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
497
            if isinstance(default, Null):
498 499 500
                raise KeyError(key)
            else:
                value = default
Matias Guijarro's avatar
Matias Guijarro committed
501
        return value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
502

Matias Guijarro's avatar
Matias Guijarro committed
503
    def remove(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
504
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
505
        cnx.hdel(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
506

507
    def keys(self):
508
        return list(self.iterkeys())
Matias Guijarro's avatar
Matias Guijarro committed
509

510
    def values(self):
511
        return list(self.itervalues())
Matias Guijarro's avatar
Matias Guijarro committed
512

513
    def clear(self):
Matias Guijarro's avatar
Matias Guijarro committed
514 515 516
        cnx = self._cnx()
        cnx.delete(self._name)

517
    def copy(self):
Matias Guijarro's avatar
Matias Guijarro committed
518 519 520
        return self.get()

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
521
    def set(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
522 523 524
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
525
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
526 527

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
528
    def update(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
529
        cnx = self._cnx()
530
        if values:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
531
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
532

533
    def items(self):
Matias Guijarro's avatar
Matias Guijarro committed
534
        values = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
535
        return [(k, v) for k, v in values.iteritems()]
Matias Guijarro's avatar
Matias Guijarro committed
536 537

    @read_decorator
538
    def fromkeys(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
539
        cnx = self._cnx()
540
        return cnx.hmget(self._name, *keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
541 542

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

Matias Guijarro's avatar
Matias Guijarro committed
546
    def iterkeys(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
547
        for k, v in self.iteritems():
548 549
            yield k

Matias Guijarro's avatar
Matias Guijarro committed
550
    def itervalues(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
551
        for k, v in self.iteritems():
552
            yield v
Matias Guijarro's avatar
Matias Guijarro committed
553

554
    def iteritems(self):
Matias Guijarro's avatar
Matias Guijarro committed
555 556
        cnx = self._cnx()
        next_id = 0
557
        seen_keys = set()
Matias Guijarro's avatar
Matias Guijarro committed
558
        while True:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
559 560
            next_id, pd = cnx.hscan(self._name, next_id)
            for k, v in pd.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
561 562
                if self._read_type_conversion:
                    v = self._read_type_conversion(v)
563
                seen_keys.add(k)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
564
                yield k, v
565
            if not next_id or next_id is '0':
Matias Guijarro's avatar
Matias Guijarro committed
566 567
                break

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
568 569 570 571
        for k, v in self._default_values.iteritems():
            if k in seen_keys:
                continue
            yield k, v
572

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
573
    def __getitem__(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
574 575
        value = self.get(key)
        if value is None:
576 577
            if not self._default_values.has_key(key):
                raise KeyError(key)
Matias Guijarro's avatar
Matias Guijarro committed
578 579
        return value

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
580
    def __setitem__(self, key, value):
Matias Guijarro's avatar
Matias Guijarro committed
581
        cnx = self._cnx()
582
        if value is None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
583
            cnx.hdel(self._name, key)
584
            return
Matias Guijarro's avatar
Matias Guijarro committed
585 586
        if self._write_type_conversion:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
587
        cnx.hset(self._name, key, value)
Matias Guijarro's avatar
Matias Guijarro committed
588

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
589
    def __contains__(self, key):
590 591 592 593 594 595 596
        try:
            self[key]
            return True
        except KeyError:
            return False


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
597 598 599 600 601 602
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
603 604 605 606
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
607
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
608 609
        self._use_object_name = use_object_name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
610
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
611 612 613 614
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
615 616

        return HashSetting(name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
617
                           self._read_type_conversion,
618 619
                           self._write_type_conversion,
                           self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
620

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
621
    def __set__(self, obj, values):
Matias Guijarro's avatar
Matias Guijarro committed
622 623 624 625 626
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
627 628
        if isinstance(values, HashSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
629

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
630
        proxy = HashSetting(name, self._cnx,
631 632 633
                            self._read_type_conversion,
                            self._write_type_conversion,
                            self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
634
        proxy.set(values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
635

636
    def get_proxy(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
637
        return HashSetting(self._name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
638
                           self._read_type_conversion,
639 640
                           self._write_type_conversion,
                           self._default_values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
641 642
# helper

Matias Guijarro's avatar
Matias Guijarro committed
643

644
def _change_to_obj_marshalling(keys):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
645 646 647 648 649
    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
650

651
class HashObjSetting(HashSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
652
    def __init__(self, name, **keys):
653
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
654 655
        HashSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
656

657
class HashObjSettingProp(HashSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
658
    def __init__(self, name, **keys):
659
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
660 661
        HashSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
662

663
class QueueObjSetting(QueueSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
664
    def __init__(self, name, **keys):
665
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
666 667
        QueueSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
668 669

class QueueObjSettingProp(QueueSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
670
    def __init__(self, name, **keys):
671
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
672 673
        QueueSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
674

675
class SimpleObjSetting(SimpleSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
676
    def __init__(self, name, **keys):
677
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
678 679
        SimpleSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
680 681

class SimpleObjSettingProp(SimpleSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
682
    def __init__(self, name, **keys):
683
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
684 685
        SimpleSettingProp.__init__(self, name, **keys)

Matias Guijarro's avatar
Matias Guijarro committed
686

687
class Struct(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
688 689
    def __init__(self, name, **keys):
        self._proxy = HashSetting(name, **keys)
690

691 692
    def __dir__(self):
        return self._proxy.keys()
693

694 695
    def __repr__(self):
        return "<Struct with attributes: %s>" % self._proxy.keys()
696

697 698
    def __getattribute__(self, name):
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
699
            return object.__getattribute__(self, name)
700 701
        else:
            return self._proxy.get(name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
702 703

    def __setattr__(self, name, value):
704
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
705
            return object.__setattr__(self, name, value)
706 707
        else:
            self._proxy[name] = value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
708 709

    def __delattr__(self, name):
710
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
711
            return object.__delattr__(self, name)
712 713
        else:
            self._proxy.remove(name)
714 715


716
class ParametersType(type):
717
    def __call__(cls, *args, **kwargs):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
718
        class_dict = {'__slots__': tuple(cls.SLOTS), 'SLOTS': cls.SLOTS}
719 720 721 722 723 724
        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
725

726

727 728
class ParamDescriptor(object):
    OBJECT_PREFIX = 'object:'
729

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
730 731
    def __init__(self, proxy, name, value, assign=True):
        self.proxy = proxy
732 733 734
        self.name = name
        if assign:
            self.assign(value)
735

736 737 738
    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
739 740 741
        try:
            self.proxy[self.name] = value
        except Exception:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
742 743
            raise ValueError("%s.%s: cannot set value" %
                             (self.proxy._name, self.name))
744 745 746

    def __get__(self, obj, obj_type):
        value = self.proxy[self.name]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
747
        if isinstance(value, (str, unicode)) and value.startswith(ParamDescriptor.OBJECT_PREFIX):
748 749
            value = value[len(ParamDescriptor.OBJECT_PREFIX):]
            return getattr(setup_globals, value)
Matias Guijarro's avatar
Matias Guijarro committed
750
        return value
751

752 753 754 755 756
    def __set__(self, obj, value):
        return self.assign(value)

    def __delete__(self, *args):
        del self.proxy[self.name]
757 758


759 760
class Parameters(object):
    __metaclass__ = ParametersType
761
    DESCRIPTOR = ParamDescriptor
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
762
    SLOTS = ['_proxy', '__current_config']
763

764 765
    def __init__(self, name, **keys):
        self.__current_config = SimpleSetting(name, default_value='default')
766 767 768 769
        hash_name = '%s:%s' % (name, self.__current_config.get())
        self._proxy = HashSetting(hash_name, **keys)
        for key in self._proxy.iterkeys():
            self.add(key)
770 771

    def __dir__(self):
772 773
        keys = [x for x in self._proxy.keys() if not x.startswith('_')]
        return keys + ['add', 'remove', 'switch', 'configs']
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
774

775 776
    def __repr__(self):
        d = dict(self._proxy.iteritems())
777 778 779 780
        return self._repr(d)
    
    def _repr(self, d):
        rep_str = "Parameters (%s)\n" % self.__current_config.get()
781
        max_len = max((len(x) for x in d.keys()))
782
        str_format = '  .%-' + '%ds' % max_len + ' = %r\n'
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
783 </