settings.py 25 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

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

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

170 171 172
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
173 174

    def __add__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
175
        value = self.get()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
176
        if isinstance(other, SimpleSetting):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
177 178 179
            other = other.get()
        return value + other

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
194 195 196 197
    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
198
        return self.__iadd__(-other)
Matias Guijarro's avatar
Matias Guijarro committed
199

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

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

Matias Guijarro's avatar
Matias Guijarro committed
222

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

236 237 238 239
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
240
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
241 242 243 244
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
245
        return SimpleSetting(name, self._cnx,
246 247 248
                             self._read_type_conversion,
                             self._write_type_conversion,
                             self._default_value)
Matias Guijarro's avatar
Matias Guijarro committed
249

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
250 251 252
    def __set__(self, obj, value):
        if isinstance(value, SimpleSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
253 254 255 256 257 258 259 260 261 262 263

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

Matias Guijarro's avatar
Matias Guijarro committed
266 267

class QueueSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
268 269 270 271 272
    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
273 274 275 276
        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
277

278 279 280 281
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
282
    @read_decorator
283 284 285
    def get(self, first=0, last=-1, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
286
        if first == last:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
287
            l = cnx.lindex(self._name, first)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
288
        else:
289 290
            if last != -1:
                last -= 1
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
291
            l = cnx.lrange(self._name, first, last)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
292
        return l
Matias Guijarro's avatar
Matias Guijarro committed
293

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

300 301 302
    def clear(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
303 304
        cnx.delete(self._name)

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
311
    @write_decorator_multiple
312
    def extend(self, values, cnx=None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
313
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
314
        return cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
315

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
316
    @write_decorator
317 318 319
    def remove(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
320
        cnx.lrem(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
321

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
322
    @write_decorator_multiple
323 324 325
    def set(self, values, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
326 327
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
328
            cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
329

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
330
    @write_decorator
331 332 333
    def set_item(self, value, pos=0, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
334
        cnx.lset(self._name, pos, value)
Matias Guijarro's avatar
Matias Guijarro committed
335

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
336
    @read_decorator
337 338 339
    def pop_front(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
340 341 342 343 344 345
        value = cnx.lpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    @read_decorator
346 347 348
    def pop_back(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
349 350 351 352 353
        value = cnx.rpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

354 355 356 357
    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
358

359 360 361
    def __len__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
362 363
        return cnx.llen(self._name)

364 365 366
    def __repr__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
367 368
        value = cnx.lrange(self._name, 0, -1)
        return '<QueueSetting name=%s value=%s>' % (self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
369

370 371
    def __iadd__(self, other, cnx=None):
        self.extend(other, cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
372 373
        return self

374
    def __getitem__(self, ran, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
375
        if isinstance(ran, slice):
376 377
            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
378
        elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
379 380 381
            i = j = ran
        else:
            raise TypeError('indices must be integers')
382
        value = self.get(first=i, last=j, cnx=cnx)
383
        if value is None:
384
            raise IndexError
385 386
        else:
            return value
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
387

388
    def __iter__(self, cnx = None):
389 390
        if cnx is None:
            cnx = self._cnx()
391
        lsize = cnx.llen(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
392
        for first in xrange(0, lsize, 1024):
393
            last = first + 1024
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
394 395 396
            if last >= lsize:
                last = -1
            for value in self.get(first, last):
397
                yield value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
398

399
    def __setitem__(self, ran, value, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
400 401
        if isinstance(ran, slice):
            for i, v in zip(range(ran.start, ran.stop), value):
402
                self.set_item(v, pos=i, cnx=cnx)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
403
        elif isinstance(ran, int):
404
            self.set_item(value, pos=ran, cnx=cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
405 406 407 408
        else:
            raise TypeError('indices must be integers')
        return self

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
409

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
410
class QueueSettingProp(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
411 412 413 414
    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
415 416 417 418 419 420
        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

421 422 423 424
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
425
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
426 427 428 429 430
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
431
        return QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
432 433
                            self._read_type_conversion,
                            self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
434

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
435 436 437
    def __set__(self, obj, values):
        if isinstance(values, QueueSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
438 439 440 441 442 443

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
444
        proxy = QueueSetting(name, self._cnx,
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
445 446
                             self._read_type_conversion,
                             self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
447 448
        proxy.set(values)

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
449

Matias Guijarro's avatar
Matias Guijarro committed
450
class HashSetting(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
451
    def __init__(self, name, connection=None,
Matias Guijarro's avatar
Matias Guijarro committed
452
                 read_type_conversion=auto_conversion,
453
                 write_type_conversion=None,
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
454
                 default_values={}):
Matias Guijarro's avatar
Matias Guijarro committed
455 456 457 458 459 460
        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
461
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
462

463 464 465 466
    @property
    def name(self):
        return self._name

467 468
    def __repr__(self):
        value = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
469 470 471
        return '<HashSetting name=%s value=%s>' % (self._name, value)

    def __delitem__(self, key):
472
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
473
        cnx.hdel(self._name, key)
Matias Guijarro's avatar
Matias Guijarro committed
474

475
    def __len__(self):
Matias Guijarro's avatar
Matias Guijarro committed
476 477 478
        cnx = self._cnx()
        return cnx.hlen(self._name)

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

482
    def raw_get(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
483
        cnx = self._cnx()
484
        return cnx.hget(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
485 486

    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
487
    def get(self, key, default=None):
Matias Guijarro's avatar
Matias Guijarro committed
488 489
        v = self.raw_get(key)
        if v is None:
490 491
            if self._read_type_conversion:
                v = self._read_type_conversion(default)
492 493
            else:
                v = default
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
494
        return v
Matias Guijarro's avatar
Matias Guijarro committed
495

Matias Guijarro's avatar
Matias Guijarro committed
496 497 498
    def _raw_get_all(self):
        cnx = self._cnx()
        return cnx.hgetall(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
499

Matias Guijarro's avatar
Matias Guijarro committed
500 501 502
    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
503 504 505 506 507
            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
508
        return all_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
509

Matias Guijarro's avatar
Matias Guijarro committed
510
    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
511
    def pop(self, key, default=Null()):
512
        cnx = self._cnx().pipeline()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
513 514 515
        cnx.hget(self._name, key)
        cnx.hdel(self._name, key)
        (value, worked) = cnx.execute()
516
        if not worked:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
517
            if isinstance(default, Null):
518 519 520
                raise KeyError(key)
            else:
                value = default
Matias Guijarro's avatar
Matias Guijarro committed
521
        return value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
522

Matias Guijarro's avatar
Matias Guijarro committed
523
    def remove(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
524
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
525
        cnx.hdel(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
526

527
    def keys(self):
528
        return list(self.iterkeys())
Matias Guijarro's avatar
Matias Guijarro committed
529

530
    def values(self):
531
        return list(self.itervalues())
Matias Guijarro's avatar
Matias Guijarro committed
532

533
    def clear(self):
Matias Guijarro's avatar
Matias Guijarro committed
534 535 536
        cnx = self._cnx()
        cnx.delete(self._name)

537
    def copy(self):
Matias Guijarro's avatar
Matias Guijarro committed
538 539 540
        return self.get()

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
541
    def set(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
542 543 544
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
545
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
546 547

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
548
    def update(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
549
        cnx = self._cnx()
550
        if values:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
551
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
552

553
    def items(self):
Matias Guijarro's avatar
Matias Guijarro committed
554
        values = self.get_all()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
555
        return [(k, v) for k, v in values.iteritems()]
Matias Guijarro's avatar
Matias Guijarro committed
556 557

    @read_decorator
558
    def fromkeys(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
559
        cnx = self._cnx()
560
        return cnx.hmget(self._name, *keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
561 562

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

Matias Guijarro's avatar
Matias Guijarro committed
566
    def iterkeys(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
567
        for k, v in self.iteritems():
568 569
            yield k

Matias Guijarro's avatar
Matias Guijarro committed
570
    def itervalues(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
571
        for k, v in self.iteritems():
572
            yield v
Matias Guijarro's avatar
Matias Guijarro committed
573

574
    def iteritems(self):
Matias Guijarro's avatar
Matias Guijarro committed
575 576
        cnx = self._cnx()
        next_id = 0
577
        seen_keys = set()
Matias Guijarro's avatar
Matias Guijarro committed
578
        while True:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
579 580
            next_id, pd = cnx.hscan(self._name, next_id)
            for k, v in pd.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
581 582
                if self._read_type_conversion:
                    v = self._read_type_conversion(v)
583
                seen_keys.add(k)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
584
                yield k, v
585
            if not next_id or next_id is '0':
Matias Guijarro's avatar
Matias Guijarro committed
586 587
                break

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
588 589 590 591
        for k, v in self._default_values.iteritems():
            if k in seen_keys:
                continue
            yield k, v
592

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
593
    def __getitem__(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
594 595
        value = self.get(key)
        if value is None:
596 597
            if not self._default_values.has_key(key):
                raise KeyError(key)
Matias Guijarro's avatar
Matias Guijarro committed
598 599
        return value

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
600
    def __setitem__(self, key, value):
Matias Guijarro's avatar
Matias Guijarro committed
601
        cnx = self._cnx()
602
        if value is None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
603
            cnx.hdel(self._name, key)
604
            return
Matias Guijarro's avatar
Matias Guijarro committed
605 606
        if self._write_type_conversion:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
607
        cnx.hset(self._name, key, value)
Matias Guijarro's avatar
Matias Guijarro committed
608

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
609
    def __contains__(self, key):
610 611 612 613 614 615 616
        try:
            self[key]
            return True
        except KeyError:
            return False


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
617 618 619 620 621 622
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
623 624 625 626
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
627
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
628 629
        self._use_object_name = use_object_name

630 631 632 633
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
634
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
635 636 637 638
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
639 640

        return HashSetting(name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
641
                           self._read_type_conversion,
642 643
                           self._write_type_conversion,
                           self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
644

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
645
    def __set__(self, obj, values):
Matias Guijarro's avatar
Matias Guijarro committed
646 647 648 649 650
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
651 652
        if isinstance(values, HashSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
653

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
654
        proxy = HashSetting(name, self._cnx,
655 656 657
                            self._read_type_conversion,
                            self._write_type_conversion,
                            self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
658
        proxy.set(values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
659

660
    def get_proxy(self):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
661
        return HashSetting(self._name, self._cnx,
Matias Guijarro's avatar
Matias Guijarro committed
662
                           self._read_type_conversion,
663 664
                           self._write_type_conversion,
                           self._default_values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
665 666
# helper

Matias Guijarro's avatar
Matias Guijarro committed
667

668
def _change_to_obj_marshalling(keys):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
669 670 671 672 673
    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
674

675
class HashObjSetting(HashSetting):
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
        HashSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
680

681
class HashObjSettingProp(HashSettingProp):
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
        HashSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
686

687
class QueueObjSetting(QueueSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
688
    def __init__(self, name, **keys):
689
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
690 691
        QueueSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
692 693

class QueueObjSettingProp(QueueSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
694
    def __init__(self, name, **keys):
695
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
696 697
        QueueSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
698

699
class SimpleObjSetting(SimpleSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
700
    def __init__(self, name, **keys):
701
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
702 703
        SimpleSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
704 705

class SimpleObjSettingProp(SimpleSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
706
    def __init__(self, name, **keys):
707
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
708 709
        SimpleSettingProp.__init__(self, name, **keys)

Matias Guijarro's avatar
Matias Guijarro committed
710

711
class Struct(object):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
712 713
    def __init__(self, name, **keys):
        self._proxy = HashSetting(name, **keys)
714

715 716
    def __dir__(self):
        return self._proxy.keys()
717

718 719
    def __repr__(self):
        return "<Struct with attributes: %s>" % self._proxy.keys()
720

721 722
    def __getattribute__(self, name):
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
723
            return object.__getattribute__(self, name)
724 725
        else:
            return self._proxy.get(name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
726 727

    def __setattr__(self, name, value):
728
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
729
            return object.__setattr__(self, name, value)
730 731
        else:
            self._proxy[name] = value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
732 733

    def __delattr__(self, name):
734
        if name.startswith('_'):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
735
            return object.__delattr__(self, name)
736 737
        else:
            self._proxy.remove(name)
738 739


740
class ParametersType(type):
741
    def __call__(cls, *args, **kwargs):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
742
        class_dict = {'__slots__': tuple(cls.SLOTS), 'SLOTS': cls.SLOTS}
743 744 745 746 747 748
        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
749

750

751 752
class ParamDescriptor(object):
    OBJECT_PREFIX = 'object:'
753

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
754 755
    def __init__(self, proxy, name, value, assign=True):
        self.proxy = proxy
756 757 758
        self.name = name
        if assign:
            self.assign(value)
759

760 761 762
    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
763 764 765
        try:
            self.proxy[self.name] = value
        except Exception:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
766 767
            raise ValueError("%s.%s: cannot set value" %
                             (self.proxy._name, self.name))
768 769 770

    def __get__(self, obj, obj_type):
        value = self.proxy[self.name]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
771
        if isinstance(value, (str, unicode)) and value.startswith(ParamDescriptor.OBJECT_PREFIX):
772 773
            value = value[len(ParamDescriptor.OBJECT_PREFIX):]
            return getattr(setup_globals, value)
Matias Guijarro's avatar
Matias Guijarro committed
774
        return value
775

776 777 778 779 780
    def __set__(self, obj, value):
        return self.assign(value)

    def __delete__(self, *args):
        del self.proxy[self.name]
781 782


783 784
class Parameters(object):
    __metaclass__ = ParametersType
785
    DESCRIPTOR = ParamDescriptor
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
786
    SLOTS = ['_proxy', '__current_config']
787

788 789
    def __init__(self, name, **keys):
        self.__current_config = SimpleSetting(name, default_value='default')
790 791 792 793
        hash_name = '%s:%s' % (name, self.__current_config.get())
        self._proxy = HashSetting(hash_name, **keys)
        for key in self._proxy.iterkeys():
            self.add(key)