settings.py 24.4 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:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
99
                for k, v in values.iteritems():
Matias Guijarro's avatar
Matias Guijarro committed
100
                    values[k] = self._write_type_conversion(v)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
101
        return func(self, values, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
102 103
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
104

105
def write_decorator_multiple(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
106
    def _write(self, values, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
107
        if self._write_type_conversion:
108 109
            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
110 111
            if values is not None:
                values = [self._write_type_conversion(x) for x in values]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
112
        return func(self, values, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
113 114
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
115

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
123 124

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


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

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

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

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

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

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

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

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

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

Matias Guijarro's avatar
Matias Guijarro committed
216

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

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

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

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

Matias Guijarro's avatar
Matias Guijarro committed
256 257

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

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

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

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

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

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

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
308
    @write_decorator_multiple
309 310 311
    def set(self, values, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
312 313
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
314
            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 set_item(self, value, pos=0, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
320
        cnx.lset(self._name, pos, value)
Matias Guijarro's avatar
Matias Guijarro committed
321

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

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

340 341 342 343
    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
344

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

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

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

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

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

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
395

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

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

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

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

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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
431

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


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

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
608
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
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

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

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

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

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

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

Matias Guijarro's avatar
Matias Guijarro committed
641

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

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
654

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
660

661
class QueueObjSetting(QueueSetting):
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
        QueueSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
666 667

class QueueObjSettingProp(QueueSettingProp):
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
        QueueSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
672

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
678 679

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

Matias Guijarro's avatar
Matias Guijarro committed
684

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

689 690
    def __dir__(self):
        return self._proxy.keys()
691

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

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

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

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


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

724

725 726
class ParamDescriptor(object):
    OBJECT_PREFIX = 'object:'
727

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

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

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

750 751 752 753 754
    def __set__(self, obj, value):
        return self.assign(value)

    def __delete__(self, *args):
        del self.proxy[self.name]
755 756


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

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

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

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

    def add(self, name, value=None):
782 783 784 785 786 787
        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)