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

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

Matias Guijarro's avatar
Matias Guijarro committed
14 15 16 17 18
class InvalidValue(Null):
    def __str__(self):
        raise ValueError
    def __repr__(self):
        return '#ERR'
19

Matias Guijarro's avatar
Matias Guijarro committed
20
def get_cache():
21
    return client.get_cache(db=0)
Matias Guijarro's avatar
Matias Guijarro committed
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36

def boolify(s,**keys):
    if s == 'True' or s == 'true':
            return True
    if s == 'False' or s == 'false':
            return False
    raise ValueError('Not Boolean Value!')

def auto_conversion(var):
    '''guesses the str representation of the variables type'''
    if var is None:
        return None
    for caster in (boolify,int, float):
        try:
            return caster(var)
37
        except (ValueError,TypeError):
Matias Guijarro's avatar
Matias Guijarro committed
38 39 40
            pass
    return var

41
def pickle_loads(var):
Matias Guijarro's avatar
Matias Guijarro committed
42 43
    if var is None:
        return None
Matias Guijarro's avatar
Matias Guijarro committed
44 45 46 47
    try:
        return pickle.loads(var)
    except Exception:
        return InvalidValue()
Matias Guijarro's avatar
Matias Guijarro committed
48 49 50 51 52 53 54 55 56

def ttl_func(cnx,name,value = -1):
    if value is None:
        return cnx.persist(name)
    elif value is -1:
        return cnx.ttl(name)
    else:
        return cnx.expire(name,value)

57 58
def read_decorator(func):
    def _read(self,*args,**keys):
Matias Guijarro's avatar
Matias Guijarro committed
59 60
        value = func(self,*args,**keys)
        if self._read_type_conversion:
61
            if isinstance(value,list):
Matias Guijarro's avatar
Matias Guijarro committed
62
                value = [self._read_type_conversion(x) for x in value]
63
            elif isinstance(value,dict):
Matias Guijarro's avatar
Matias Guijarro committed
64 65
                for k,v in value.iteritems():
                    value[k] = self._read_type_conversion(v)
66 67 68 69
                if hasattr(self,'default_values') and isinstance(self.default_values,dict):
                    tmp = dict(self._default_values)
                    tmp.update(value)
                    value = tmp
Matias Guijarro's avatar
Matias Guijarro committed
70 71
            else:
                value = self._read_type_conversion(value)
72 73 74 75 76 77
        if value is None:
            if hasattr(self,'_default_value'):
                value = self._default_value
            elif(hasattr(self,'_default_values') and 
                 hasattr(self._default_values,'get')):
                value = self._default_values.get(args[0])
Matias Guijarro's avatar
Matias Guijarro committed
78 79 80
        return value
    return _read

81 82
def write_decorator_dict(func):
    def _write(self,values,**keys):
Matias Guijarro's avatar
Matias Guijarro committed
83 84 85 86 87 88 89 90 91 92
        if self._write_type_conversion:
            if not isinstance(values,dict) and values is not None:
                raise TypeError('can only be dict')

            if values is not None:
                for k,v in values.iteritems():
                    values[k] = self._write_type_conversion(v)
        return func(self,values,**keys)
    return _write

93
def write_decorator_multiple(func):
Matias Guijarro's avatar
Matias Guijarro committed
94 95 96 97 98 99 100 101 102
    def _write(self,values,**keys):
        if self._write_type_conversion:
            if not isinstance(values,(list,tuple)) and values is not None:
                raise TypeError('can only be tuple or list')
            if values is not None:
                values = [self._write_type_conversion(x) for x in values]
        return func(self,values,**keys)
    return _write

103
def write_decorator(func):
Matias Guijarro's avatar
Matias Guijarro committed
104 105 106 107 108 109
    def _write(self,value,**keys):
        if self._write_type_conversion and value is not None:
            value = self._write_type_conversion(value)
        return func(self,value,**keys)
    return _write

110 111 112 113 114 115 116 117 118 119 120 121
def scan(match='*',count=1000,connection=None):
    if connection is None:
        connection = get_cache()
    cursor = 0
    while 1:
        cursor,values = connection.scan(cursor=cursor,
                                        match=match,count=count)
        for val in values:
            yield val
        if int(cursor) == 0:
            break
        
122
class SimpleSetting(object):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
123 124
    def __init__(self,name,connection = None,
                 read_type_conversion = auto_conversion,
125
                 write_type_conversion = None,
126
                 default_value = None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
127 128 129 130 131 132
        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
133
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
134

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
135
    @read_decorator
136
    def get(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
137 138 139 140 141
        cnx = self._cnx()
        value = cnx.get(self._name)
        return value

    @write_decorator
142
    def set(self,value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
143 144 145 146 147
        cnx = self._cnx()
        cnx.set(self._name,value)

    def ttl(self,value = -1):
        return ttl_func(self._cnx(),self._name,value)
148 149 150 151 152
    
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)
    
153
    def __add__(self,other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
154
        value = self.get()
155
        if isinstance(other,SimpleSetting):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
156 157 158 159 160 161
            other = other.get()
        return value + other

    def __iadd__(self,other):
        cnx = self._cnx()
        if cnx is not None:
162
            if isinstance(other,int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
163 164
                if other == 1:
                    cnx.incr(self._name)
Matias Guijarro's avatar
Matias Guijarro committed
165
                else:
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
166
                    cnx.incrby(self._name,other)
167
            elif isinstance(other,float):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
168 169 170 171
                cnx.incrbyfloat(self._name,other)
            else:
                cnx.append(self._name,other)
            return self
Matias Guijarro's avatar
Matias Guijarro committed
172

173
    def __isub__(self,other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
174 175 176
        if isinstance(other,basestring):
            raise TypeError("unsupported operand type(s) for -=: %s" % type(other).__name__)
        return self.__iadd__(-other)
Matias Guijarro's avatar
Matias Guijarro committed
177

178
    def __getitem__(self,ran):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
        cnx = self._cnx()
        if cnx is not None:
            step = None
            if isinstance(ran,slice):
                i,j = ran.start,ran.stop
                step = ran.step
            elif isinstance(ran,int):
                i = j = ran
            else:
                raise TypeError('indices must be integers')

            value = cnx.getrange(self._name,i,j)
            if step is not None:
                value = value[0:-1:step]
            return value


    def __repr__(self):
        cnx = self._cnx()
        value = cnx.get(self._name)
199
        return '<SimpleSetting name=%s value=%s>' % (self._name,value)
Matias Guijarro's avatar
Matias Guijarro committed
200

201
class SimpleSettingProp(object):
Matias Guijarro's avatar
Matias Guijarro committed
202 203 204
    def __init__(self,name,connection = None,
                 read_type_conversion = auto_conversion,
                 write_type_conversion = None,
205
                 default_value = None,
206
                 use_object_name = True):
Matias Guijarro's avatar
Matias Guijarro committed
207 208 209 210
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
211
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
212 213
        self._use_object_name = use_object_name

214
    def __get__(self,obj,type = None):
Matias Guijarro's avatar
Matias Guijarro committed
215 216 217 218
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
219 220 221 222
        return SimpleSetting(name,self._cnx,
                             self._read_type_conversion,
                             self._write_type_conversion,
                             self._default_value)
Matias Guijarro's avatar
Matias Guijarro committed
223

224
    def __set__(self,obj,value):
225
        if isinstance(value,SimpleSetting): return
Matias Guijarro's avatar
Matias Guijarro committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239

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

class QueueSetting(object):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
240 241
    def __init__(self,name,connection = None,
                 read_type_conversion = auto_conversion,
242
                 write_type_conversion = None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
243 244 245 246 247
        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
Matias Guijarro's avatar
Matias Guijarro committed
248

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
249
    @read_decorator
250
    def get(self,first=0,last=-1):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
251 252 253 254
        cnx = self._cnx()
        if first == last:
            l = cnx.lindex(self._name,first)
        else:
255 256
            if last != -1:
                last -= 1
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
257 258
            l = cnx.lrange(self._name,first,last)
        return l
Matias Guijarro's avatar
Matias Guijarro committed
259

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
260
    @write_decorator
261
    def append(self,value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
262
        cnx = self._cnx()
263
        return cnx.rpush(self._name,value)
Matias Guijarro's avatar
Matias Guijarro committed
264

265 266 267 268
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
269
    @write_decorator
270
    def prepend(self,value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
271
        cnx = self._cnx()
272
        return cnx.lpush(self._name,value)
Matias Guijarro's avatar
Matias Guijarro committed
273

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
274
    @write_decorator_multiple
275
    def extend(self,values):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
276
        cnx = self._cnx()
277
        return cnx.rpush(self._name,*values)
Matias Guijarro's avatar
Matias Guijarro committed
278

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
279
    @write_decorator
280
    def remove(self,value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
281
        cnx = self._cnx()
282
        cnx.lrem(self._name,value)
Matias Guijarro's avatar
Matias Guijarro committed
283

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
284
    @write_decorator_multiple
285
    def set(self,values):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
286 287
        if not isinstance(values,(list,tuple)) and values is not None:
            raise TypeError('can only be tuple or list')
Matias Guijarro's avatar
Matias Guijarro committed
288

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
289 290 291 292
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
            cnx.rpush(self._name,*values)
Matias Guijarro's avatar
Matias Guijarro committed
293

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
294
    @write_decorator
295
    def set_item(self,value,pos = 0):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
296 297
        cnx = self._cnx()
        cnx.lset(self._name,pos,value)
Matias Guijarro's avatar
Matias Guijarro committed
298

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
299
    @read_decorator
300
    def pop_front(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
301 302 303 304 305 306 307
        cnx = self._cnx()
        value = cnx.lpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    @read_decorator
308
    def pop_back(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
309 310 311 312 313 314 315 316 317
        cnx = self._cnx()
        value = cnx.rpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    def ttl(self,value = -1):
        return ttl_func(self._cnx(),self._name,value)

318
    def __len__(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
319 320 321
        cnx = self._cnx()
        return cnx.llen(self._name)

322
    def __repr__(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
323 324
        cnx = self._cnx()
        value = cnx.lrange(self._name,0,-1)
325
        return '<QueueSetting name=%s value=%s>' % (self._name,value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
326 327 328 329 330

    def __iadd__(self,other):
        self.extend(other)
        return self

331
    def __getitem__(self,ran):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
332
        if isinstance(ran,slice):
333 334
            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
Sebastien Petitdemange committed
335 336 337 338
        elif isinstance(ran,int):
            i = j = ran
        else:
            raise TypeError('indices must be integers')
339 340 341 342 343
        value = self.get(first = i,last = j)
        if value is None:
            raise StopIteration
        else:
            return value
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
344

345
    def __iter__(self):
346 347
        cnx = self._cnx()
        lsize = cnx.llen(self._name)
348
        for first in xrange(0,lsize,1024):
349 350 351 352 353
            last = first + 1024
            if last >= lsize: last = -1
            for value in self.get(first,last):
                yield value
        
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
354
    def __setitem__(self,ran,value):
355 356
        if isinstance(ran,slice):
            for i,v in zip(range(ran.start,ran.stop),value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
357 358 359 360 361 362 363 364
                self.set_item(v,pos=i)
        elif isinstance(ran,int):
            self.set_item(value,pos=ran)
        else:
            raise TypeError('indices must be integers')
        return self

class QueueSettingProp(object):
Matias Guijarro's avatar
Matias Guijarro committed
365 366 367
    def __init__(self,name,connection = None,
                 read_type_conversion = auto_conversion,
                 write_type_conversion = None,
368
                 use_object_name = True):
Matias Guijarro's avatar
Matias Guijarro committed
369 370 371 372 373 374
        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

375
    def __get__(self,obj,type = None):
Matias Guijarro's avatar
Matias Guijarro committed
376 377 378 379 380
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
381 382 383
        return QueueSetting(name,self._cnx,
                            self._read_type_conversion,
                            self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
384

385
    def __set__(self,obj,values):
386
        if isinstance(values,QueueSetting): return
Matias Guijarro's avatar
Matias Guijarro committed
387 388 389 390 391 392

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

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
393 394 395
        proxy = QueueSetting(name,self._cnx,
                             self._read_type_conversion,
                             self._write_type_conversion)
Matias Guijarro's avatar
Matias Guijarro committed
396 397 398 399 400
        proxy.set(values)

class HashSetting(object):
    def __init__(self,name,connection=None,
                 read_type_conversion=auto_conversion,
401 402
                 write_type_conversion=None,
                 default_values = {}):
Matias Guijarro's avatar
Matias Guijarro committed
403 404 405 406 407 408
        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
409
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
410

411 412
    def __repr__(self):
        value = self.get_all()
Matias Guijarro's avatar
Matias Guijarro committed
413
        return '<HashSetting name=%s value=%s>' % (self._name,value)
414
    
415
    def __delitem__(self,key):
416 417
        cnx = self._cnx()
        cnx.hdel(self._name,key)
Matias Guijarro's avatar
Matias Guijarro committed
418

419
    def __len__(self):
Matias Guijarro's avatar
Matias Guijarro committed
420 421 422 423 424 425
        cnx = self._cnx()
        return cnx.hlen(self._name)

    def ttl(self,value = -1):
        return ttl_func(self._cnx(),self._name,value)

Matias Guijarro's avatar
Matias Guijarro committed
426
    def raw_get(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
427
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
428 429 430 431 432 433
        return cnx.hget(self._name, key)

    @read_decorator
    def get(self, key, default = None):
        v = self.raw_get(key)
        if v is None:
434 435 436 437
            if self._write_type_conversion:
                v = self._write_type_conversion(default)
            else:
                v = default
Matias Guijarro's avatar
Matias Guijarro committed
438
        return v 
Matias Guijarro's avatar
Matias Guijarro committed
439

Matias Guijarro's avatar
Matias Guijarro committed
440 441 442 443 444 445 446 447 448 449 450 451 452
    def _raw_get_all(self):
        cnx = self._cnx()
        return cnx.hgetall(self._name)
    
    def get_all(self):
        all_dict = dict(self._default_values)
        for k, raw_v in self._raw_get_all().iteritems():
          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
        return all_dict
    
Matias Guijarro's avatar
Matias Guijarro committed
453
    @read_decorator
Matias Guijarro's avatar
Matias Guijarro committed
454
    def pop(self, key, default = Null()):
455 456 457 458 459 460 461 462 463
        cnx = self._cnx().pipeline()
        cnx.hget(self._name,key)
        cnx.hdel(self._name,key)
        (value,worked) = cnx.execute()
        if not worked:
            if isinstance(default,Null):
                raise KeyError(key)
            else:
                value = default
Matias Guijarro's avatar
Matias Guijarro committed
464 465
        return value
    
Matias Guijarro's avatar
Matias Guijarro committed
466
    def remove(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
467
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
468
        cnx.hdel(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
469

470
    def keys(self):
471
        return list(self.iterkeys())
Matias Guijarro's avatar
Matias Guijarro committed
472

473
    def values(self):
474
        return list(self.itervalues())
Matias Guijarro's avatar
Matias Guijarro committed
475

476
    def clear(self):
Matias Guijarro's avatar
Matias Guijarro committed
477 478 479
        cnx = self._cnx()
        cnx.delete(self._name)

480
    def copy(self):
Matias Guijarro's avatar
Matias Guijarro committed
481 482 483
        return self.get()

    @write_decorator_dict
484
    def set(self,values):
Matias Guijarro's avatar
Matias Guijarro committed
485 486 487 488 489 490
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
            cnx.hmset(self._name,values)

    @write_decorator_dict
491
    def update(self,values):
Matias Guijarro's avatar
Matias Guijarro committed
492
        cnx = self._cnx()
493 494
        if values:
            cnx.hmset(self._name,values)
Matias Guijarro's avatar
Matias Guijarro committed
495

496
    def items(self):
Matias Guijarro's avatar
Matias Guijarro committed
497 498 499 500
        values = self.get_all()
        return [(k,v) for k,v in values.iteritems()]

    @read_decorator
501
    def fromkeys(self,keys):
Matias Guijarro's avatar
Matias Guijarro committed
502 503 504
        cnx = self._cnx()
        return cnx.hmget(self._name,keys)
    
505
    def has_key(self,key):
Matias Guijarro's avatar
Matias Guijarro committed
506
        cnx = self._cnx()
507
        return cnx.hexists(self._name,key) or self._default_values.has_key(key)
Matias Guijarro's avatar
Matias Guijarro committed
508 509
    
    def iterkeys(self):
510 511 512
        for k,v in self.iteritems():
            yield k

Matias Guijarro's avatar
Matias Guijarro committed
513
    def itervalues(self):
514 515
        for k,v in self.iteritems():
            yield v
Matias Guijarro's avatar
Matias Guijarro committed
516

517
    def iteritems(self):
Matias Guijarro's avatar
Matias Guijarro committed
518 519
        cnx = self._cnx()
        next_id = 0
520
        seen_keys = set()
Matias Guijarro's avatar
Matias Guijarro committed
521 522 523 524 525
        while True:
            next_id,pd = cnx.hscan(self._name,next_id)
            for k,v in pd.iteritems():
                if self._read_type_conversion:
                    v = self._read_type_conversion(v)
526
                seen_keys.add(k)
Matias Guijarro's avatar
Matias Guijarro committed
527
                yield k,v
528
            if not next_id or next_id is '0':
Matias Guijarro's avatar
Matias Guijarro committed
529 530
                break

531 532 533 534
        for k,v in self._default_values.iteritems():
            if k in seen_keys: continue
            yield k,v

Matias Guijarro's avatar
Matias Guijarro committed
535 536 537
    def __getitem__(self,key):
        value = self.get(key)
        if value is None:
538 539
            if not self._default_values.has_key(key):
                raise KeyError(key)
Matias Guijarro's avatar
Matias Guijarro committed
540 541
        return value

542
    def __setitem__(self,key,value):
Matias Guijarro's avatar
Matias Guijarro committed
543
        cnx = self._cnx()
544 545 546
        if value is None:
            cnx.hdel(self._name,key)
            return
Matias Guijarro's avatar
Matias Guijarro committed
547 548 549 550 551 552 553 554
        if self._write_type_conversion:
            value = self._write_type_conversion(value)
        cnx.hset(self._name,key,value)

class HashSettingProp(object):        
    def __init__(self,name,connection = None,
                 read_type_conversion = auto_conversion,
                 write_type_conversion = None,
555
                 default_values = {},
556
                 use_object_name = True):
Matias Guijarro's avatar
Matias Guijarro committed
557 558 559 560
        self._name = name
        self._cnx = connection or get_cache()
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
561
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
562 563 564 565 566 567 568 569 570 571
        self._use_object_name = use_object_name

    def __get__(self,obj,type = None):
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name
    
        return HashSetting(name,self._cnx,
                           self._read_type_conversion,
572 573
                           self._write_type_conversion,
                           self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
574

575
    def __set__(self,obj,values):
Matias Guijarro's avatar
Matias Guijarro committed
576 577 578 579 580
        if self._use_object_name:
            name = obj.name + ':' + self._name
        else:
            name = self._name

581
        if isinstance(values,HashSetting): return
Matias Guijarro's avatar
Matias Guijarro committed
582

583 584 585 586
        proxy = HashSetting(name,self._cnx,
                            self._read_type_conversion,
                            self._write_type_conversion,
                            self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
587 588
        proxy.set(values)
    
589
    def get_proxy(self):
Matias Guijarro's avatar
Matias Guijarro committed
590 591
        return HashSetting(self._name,self._cnx,
                           self._read_type_conversion,
592 593
                           self._write_type_conversion,
                           self._default_values)
Matias Guijarro's avatar
Matias Guijarro committed
594 595
#helper

596
def _change_to_obj_marshalling(keys):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
597 598 599 600 601
    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})

602 603 604
class HashObjSetting(HashSetting):
    def __init__(self,name,**keys):
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
605 606
        HashSetting.__init__(self,name,**keys)

607
class HashObjSettingProp(HashSettingProp):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
608
    def __init__(self,name,**keys):
609
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
610 611
        HashSettingProp.__init__(self,name,**keys)

612 613 614
class QueueObjSetting(QueueSetting):
    def __init__(self,name,**keys):
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
615 616 617
        QueueSetting.__init__(self,name,**keys)

class QueueObjSettingProp(QueueSettingProp):
618 619
    def __init__(self,name,**keys):
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
620 621
        QueueSettingProp.__init__(self,name,**keys)

622 623 624
class SimpleObjSetting(SimpleSetting):
    def __init__(self,name,**keys):
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
625 626 627
        SimpleSetting.__init__(self,name,**keys)

class SimpleObjSettingProp(SimpleSettingProp):
628 629
    def __init__(self,name,**keys):
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
630
        SimpleSettingProp.__init__(self,name,**keys)
Matias Guijarro's avatar
Matias Guijarro committed
631

632 633 634
class Struct(object):
    def __init__(self,name,**keys):
        self._proxy = HashSetting(name,**keys)
635

636 637
    def __dir__(self):
        return self._proxy.keys()
638

639 640
    def __repr__(self):
        return "<Struct with attributes: %s>" % self._proxy.keys()
641

642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658
    def __getattribute__(self, name):
        if name.startswith('_'):
            return object.__getattribute__(self,name)
        else:
            return self._proxy.get(name)
            
    def __setattr__(self,name,value):
        if name.startswith('_'):
            return object.__setattr__(self,name,value)
        else:
            self._proxy[name] = value
 
    def __delattr__(self,name):
        if name.startswith('_'):
            return object.__delattr__(self,name)
        else:
            self._proxy.remove(name)
659 660


661
class ParametersType(type):
662 663 664 665 666 667 668 669
    def __call__(cls, *args, **kwargs):
        class_dict = { '__slots__': tuple(cls.SLOTS), 'SLOTS':cls.SLOTS }
        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
670

671

672 673
class ParamDescriptor(object):
    OBJECT_PREFIX = 'object:'
674

675 676 677 678 679
    def __init__(self, proxy, name, value, assign = True):
        self.proxy = proxy 
        self.name = name
        if assign:
            self.assign(value)
680

681 682 683
    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
684 685 686 687
        try:
            self.proxy[self.name] = value
        except Exception:
            raise ValueError("%s.%s: cannot set value" % (self.proxy._name, self.name))
688 689 690 691 692 693

    def __get__(self, obj, obj_type):
        value = self.proxy[self.name]
        if isinstance(value, (str,unicode)) and value.startswith(ParamDescriptor.OBJECT_PREFIX):
            value = value[len(ParamDescriptor.OBJECT_PREFIX):]
            return getattr(setup_globals, value)
Matias Guijarro's avatar
Matias Guijarro committed
694
        return value
695

696 697 698 699 700
    def __set__(self, obj, value):
        return self.assign(value)

    def __delete__(self, *args):
        del self.proxy[self.name]
701 702


703 704
class Parameters(object):
    __metaclass__ = ParametersType
705
    DESCRIPTOR = ParamDescriptor
706 707
    SLOTS = ['_proxy','__current_config']

708 709
    def __init__(self, name, **keys):
        self.__current_config = SimpleSetting(name, default_value='default')
710 711 712 713
        hash_name = '%s:%s' % (name, self.__current_config.get())
        self._proxy = HashSetting(hash_name, **keys)
        for key in self._proxy.iterkeys():
            self.add(key)
714 715

    def __dir__(self):
716 717
        return self._proxy.keys() + ['add','remove','switch','configs']
    
718
    def __repr__(self):
719
        rep_str = "Parameters (%s)\n" % self.__current_config.get()
720 721
        d = dict(self._proxy.iteritems())
        max_len = max((len(x) for x in d.keys()))
722
        str_format = '  .%-' + '%ds' % max_len + ' = %r\n'
723
        for key,value in sorted(d.iteritems()):
724 725
            rep_str += str_format % (key,value)
        return rep_str
726 727 728 729 730 731 732 733
    
    def add(self, name, value = None):
        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)
734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751

    def switch(self,name):
        for key,value in dict(self.__class__.__dict__).iteritems():
            if isinstance(value, self.DESCRIPTOR):
                delattr(self.__class__,key)
                
        self.__current_config.set(name)

        basename = ":".join(self._proxy._name.split(':')[:-1])
        self._proxy._name = '%s:%s' % (basename, name)
        
        for key in self._proxy.keys():
            self.add(key)
        
    @property
    def configs(self):
        basename = ":".join(self._proxy._name.split(':')[:-1])
        return list((x.split(':')[-1] for x in scan(match='%s:*' % basename)))
Matias Guijarro's avatar
Matias Guijarro committed
752

753 754
if __name__ == "__main__":
    class A(object):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
755 756 757 758 759 760
        x = SimpleSettingProp('counter')
        y = SimpleObjSettingProp('obj')
        q = QueueSettingProp('seb')
        ol = QueueObjSettingProp('seb-list')
        h = HashSettingProp('seb-hash')
        oh = HashObjSettingProp('seb-hash-object')
Matias Guijarro's avatar
Matias Guijarro committed
761 762 763 764 765
        def __init__(self,name):
            self.name = name

    a = A('m0')
    p = Struct('optics:zap:params')