settings.py 25.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
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
    def __repr__(self):
21
        return "#ERR"
22

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
23

24 25 26 27 28 29 30
class DefaultValue(object):
    def __init__(self, wrapped_value):
        self.__value = wrapped_value

    @property
    def value(self):
        return self.__value
Matias Guijarro's avatar
Matias Guijarro committed
31

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
32 33

def boolify(s, **keys):
34
    if s == "True" or s == "true":
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
35
        return True
36
    if s == "False" or s == "false":
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
37
        return False
38
    raise ValueError("Not Boolean Value!")
Matias Guijarro's avatar
Matias Guijarro committed
39

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
40

41 42 43
def auto_coerce_from_redis(s):
    """Convert variable to a new type from the str representation"""
    if s is None:
Matias Guijarro's avatar
Matias Guijarro committed
44
        return None
45 46 47 48 49 50 51 52
    # Default is unicode string
    try:
        if isinstance(s, bytes):
            s = s.decode()
    # Pickled data fails at first byte
    except UnicodeDecodeError:
        pass
    # Cast to standard types
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
53
    for caster in (boolify, int, float):
Matias Guijarro's avatar
Matias Guijarro committed
54
        try:
55
            return caster(s)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
56
        except (ValueError, TypeError):
Matias Guijarro's avatar
Matias Guijarro committed
57
            pass
58
    return s
Matias Guijarro's avatar
Matias Guijarro committed
59

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
60

61
def pickle_loads(var):
Matias Guijarro's avatar
Matias Guijarro committed
62 63
    if var is None:
        return None
Matias Guijarro's avatar
Matias Guijarro committed
64 65 66 67
    try:
        return pickle.loads(var)
    except Exception:
        return InvalidValue()
Matias Guijarro's avatar
Matias Guijarro committed
68

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
69

70 71
def get_redis_connection():
    return client.get_redis_connection(db=0)
72 73


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
74
def ttl_func(cnx, name, value=-1):
Matias Guijarro's avatar
Matias Guijarro committed
75 76 77 78 79
    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
80 81
        return cnx.expire(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
82

83
def read_decorator(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
84 85
    def _read(self, *args, **keys):
        value = func(self, *args, **keys)
Matias Guijarro's avatar
Matias Guijarro committed
86
        if self._read_type_conversion:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
87
            if isinstance(value, list):
Matias Guijarro's avatar
Matias Guijarro committed
88
                value = [self._read_type_conversion(x) for x in value]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
89
            elif isinstance(value, dict):
Vincent Michel's avatar
Vincent Michel committed
90
                for k, v in value.items():
Matias Guijarro's avatar
Matias Guijarro committed
91
                    value[k] = self._read_type_conversion(v)
92 93 94
                if hasattr(self, "default_values") and isinstance(
                    self.default_values, dict
                ):
95 96 97
                    tmp = dict(self._default_values)
                    tmp.update(value)
                    value = tmp
Matias Guijarro's avatar
Matias Guijarro committed
98
            else:
99 100 101
                if isinstance(value, DefaultValue):
                    value = value.value
                elif value is not None:
102
                    value = self._read_type_conversion(value)
103
        if value is None:
104
            if hasattr(self, "_default_value"):
105
                value = self._default_value
106 107 108
            elif hasattr(self, "_default_values") and hasattr(
                self._default_values, "get"
            ):
109
                value = self._default_values.get(args[0])
Matias Guijarro's avatar
Matias Guijarro committed
110
        return value
111

Matias Guijarro's avatar
Matias Guijarro committed
112 113
    return _read

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
114

115
def write_decorator_dict(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
116
    def _write(self, values, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
117
        if self._write_type_conversion:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
118
            if not isinstance(values, dict) and values is not None:
119
                raise TypeError("can only be dict")
Matias Guijarro's avatar
Matias Guijarro committed
120 121

            if values is not None:
122
                new_dict = dict()
Vincent Michel's avatar
Vincent Michel committed
123
                for k, v in values.items():
124 125
                    new_dict[k] = self._write_type_conversion(v)
                values = new_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
126
        return func(self, values, **keys)
127

Matias Guijarro's avatar
Matias Guijarro committed
128 129
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
130

131
def write_decorator_multiple(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
132
    def _write(self, values, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
133
        if self._write_type_conversion:
134 135 136 137 138
            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
139 140
            if values is not None:
                values = [self._write_type_conversion(x) for x in values]
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
141
        return func(self, values, **keys)
142

Matias Guijarro's avatar
Matias Guijarro committed
143 144
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
145

146
def write_decorator(func):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
147
    def _write(self, value, **keys):
Matias Guijarro's avatar
Matias Guijarro committed
148 149
        if self._write_type_conversion and value is not None:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
150
        return func(self, value, **keys)
151

Matias Guijarro's avatar
Matias Guijarro committed
152 153
    return _write

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
154

155
def scan(match="*", count=1000, connection=None):
156
    if connection is None:
157
        connection = get_redis_connection()
158 159
    cursor = 0
    while 1:
160
        cursor, values = connection.scan(cursor=cursor, match=match, count=count)
161
        for val in values:
162
            yield val.decode()
163 164
        if int(cursor) == 0:
            break
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
165 166


167
class SimpleSetting(object):
168 169 170 171
    def __init__(
        self,
        name,
        connection=None,
172 173
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
174 175
        default_value=None,
    ):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
176
        if connection is None:
177
            connection = get_redis_connection()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
178 179 180 181
        self._cnx = weakref.ref(connection)
        self._name = name
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
182
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
183

184 185 186
    @property
    def name(self):
        return self._name
187

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
188
    @read_decorator
189
    def get(self):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
190 191 192 193 194
        cnx = self._cnx()
        value = cnx.get(self._name)
        return value

    @write_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
195
    def set(self, value):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
196
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
197 198 199 200
        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
201

202 203 204
    def clear(self):
        cnx = self._cnx()
        cnx.delete(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
205 206

    def __add__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
207
        value = self.get()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
208
        if isinstance(other, SimpleSetting):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
209 210 211
            other = other.get()
        return value + other

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
212
    def __iadd__(self, other):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
213 214
        cnx = self._cnx()
        if cnx is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
215
            if isinstance(other, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
216 217
                if other == 1:
                    cnx.incr(self._name)
Matias Guijarro's avatar
Matias Guijarro committed
218
                else:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
219 220 221
                    cnx.incrby(self._name, other)
            elif isinstance(other, float):
                cnx.incrbyfloat(self._name, other)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
222
            else:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
223
                cnx.append(self._name, other)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
224
            return self
Matias Guijarro's avatar
Matias Guijarro committed
225

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
226
    def __isub__(self, other):
Vincent Michel's avatar
Vincent Michel committed
227
        if isinstance(other, str):
228 229 230
            raise TypeError(
                "unsupported operand type(s) for -=: %s" % type(other).__name__
            )
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
231
        return self.__iadd__(-other)
Matias Guijarro's avatar
Matias Guijarro committed
232

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
233
    def __getitem__(self, ran):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
234 235 236
        cnx = self._cnx()
        if cnx is not None:
            step = None
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
237 238
            if isinstance(ran, slice):
                i, j = ran.start, ran.stop
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
239
                step = ran.step
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
240
            elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
241 242
                i = j = ran
            else:
243
                raise TypeError("indices must be integers")
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
244

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
245
            value = cnx.getrange(self._name, i, j)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
246 247 248 249 250 251 252
            if step is not None:
                value = value[0:-1:step]
            return value

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

Matias Guijarro's avatar
Matias Guijarro committed
255

256
class SimpleSettingProp(object):
257 258 259 260
    def __init__(
        self,
        name,
        connection=None,
261 262
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
263 264 265
        default_value=None,
        use_object_name=True,
    ):
Matias Guijarro's avatar
Matias Guijarro committed
266
        self._name = name
267
        self._cnx = connection or get_redis_connection()
Matias Guijarro's avatar
Matias Guijarro committed
268 269
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
270
        self._default_value = default_value
Matias Guijarro's avatar
Matias Guijarro committed
271 272
        self._use_object_name = use_object_name

273 274 275 276
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
277
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
278
        if self._use_object_name:
279
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
280 281
        else:
            name = self._name
282 283 284 285 286 287 288
        return SimpleSetting(
            name,
            self._cnx,
            self._read_type_conversion,
            self._write_type_conversion,
            self._default_value,
        )
Matias Guijarro's avatar
Matias Guijarro committed
289

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
290 291 292
    def __set__(self, obj, value):
        if isinstance(value, SimpleSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
293 294

        if self._use_object_name:
295
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
296 297 298 299 300 301 302 303
        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
304 305
            self._cnx.set(name, value)

Matias Guijarro's avatar
Matias Guijarro committed
306 307

class QueueSetting(object):
308 309 310 311
    def __init__(
        self,
        name,
        connection=None,
312 313
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
314
    ):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
315
        if connection is None:
316
            connection = get_redis_connection()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
317 318 319 320
        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
321

322 323 324 325
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
326
    @read_decorator
327 328 329
    def get(self, first=0, last=-1, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
330
        if first == last:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
331
            l = cnx.lindex(self._name, first)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
332
        else:
333 334
            if last != -1:
                last -= 1
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
335
            l = cnx.lrange(self._name, first, last)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
336
        return l
Matias Guijarro's avatar
Matias Guijarro committed
337

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
338
    @write_decorator
339 340 341
    def append(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
342
        return cnx.rpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
343

344 345 346
    def clear(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
347 348
        cnx.delete(self._name)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
349
    @write_decorator
350 351 352
    def prepend(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
353
        return cnx.lpush(self._name, value)
Matias Guijarro's avatar
Matias Guijarro committed
354

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
355
    @write_decorator_multiple
356
    def extend(self, values, cnx=None):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
357
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
358
        return cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
359

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
360
    @write_decorator
361 362 363
    def remove(self, value, cnx=None):
        if cnx is None:
            cnx = self._cnx()
364
        cnx.lrem(self._name, 0, value)
Matias Guijarro's avatar
Matias Guijarro committed
365

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
366
    @write_decorator_multiple
367 368 369
    def set(self, values, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
370 371
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
372
            cnx.rpush(self._name, *values)
Matias Guijarro's avatar
Matias Guijarro committed
373

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
374
    @write_decorator
375 376 377
    def set_item(self, value, pos=0, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
378
        cnx.lset(self._name, pos, value)
Matias Guijarro's avatar
Matias Guijarro committed
379

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
380
    @read_decorator
381 382 383
    def pop_front(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
384 385 386 387 388 389
        value = cnx.lpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

    @read_decorator
390 391 392
    def pop_back(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
393 394 395 396 397
        value = cnx.rpop(self._name)
        if self._read_type_conversion:
            value = self._read_type_conversion(value)
        return value

398 399 400 401
    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
402

403 404 405
    def __len__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
406 407
        return cnx.llen(self._name)

408 409 410
    def __repr__(self, cnx=None):
        if cnx is None:
            cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
411
        value = cnx.lrange(self._name, 0, -1)
412
        return "<QueueSetting name=%s value=%s>" % (self._name, value)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
413

414 415
    def __iadd__(self, other, cnx=None):
        self.extend(other, cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
416 417
        return self

418
    def __getitem__(self, ran, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
419
        if isinstance(ran, slice):
420 421
            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
422
        elif isinstance(ran, int):
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
423 424
            i = j = ran
        else:
425
            raise TypeError("indices must be integers")
426
        value = self.get(first=i, last=j, cnx=cnx)
427
        if value is None:
428
            raise IndexError
429 430
        else:
            return value
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
431

432
    def __iter__(self, cnx=None):
433 434
        if cnx is None:
            cnx = self._cnx()
435
        lsize = cnx.llen(self._name)
Vincent Michel's avatar
Vincent Michel committed
436
        for first in range(0, lsize, 1024):
437
            last = first + 1024
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
438 439 440
            if last >= lsize:
                last = -1
            for value in self.get(first, last):
441
                yield value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
442

443
    def __setitem__(self, ran, value, cnx=None):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
444
        if isinstance(ran, slice):
Vincent Michel's avatar
Vincent Michel committed
445
            for i, v in zip(list(range(ran.start, ran.stop)), value):
446
                self.set_item(v, pos=i, cnx=cnx)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
447
        elif isinstance(ran, int):
448
            self.set_item(value, pos=ran, cnx=cnx)
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
449
        else:
450
            raise TypeError("indices must be integers")
Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
451 452
        return self

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
453

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
454
class QueueSettingProp(object):
455 456 457 458
    def __init__(
        self,
        name,
        connection=None,
459 460
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
461 462
        use_object_name=True,
    ):
Matias Guijarro's avatar
Matias Guijarro committed
463
        self._name = name
464
        self._cnx = connection or get_redis_connection()
Matias Guijarro's avatar
Matias Guijarro committed
465 466 467 468
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
        self._use_object_name = use_object_name

469 470 471 472
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
473
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
474
        if self._use_object_name:
475
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
476 477 478
        else:
            name = self._name

479 480 481
        return QueueSetting(
            name, self._cnx, self._read_type_conversion, self._write_type_conversion
        )
Matias Guijarro's avatar
Matias Guijarro committed
482

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
483 484 485
    def __set__(self, obj, values):
        if isinstance(values, QueueSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
486 487

        if self._use_object_name:
488
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
489 490 491
        else:
            name = self._name

492 493 494
        proxy = QueueSetting(
            name, self._cnx, self._read_type_conversion, self._write_type_conversion
        )
Matias Guijarro's avatar
Matias Guijarro committed
495 496
        proxy.set(values)

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
497

Matias Guijarro's avatar
Matias Guijarro committed
498
class HashSetting(object):
499 500 501 502
    def __init__(
        self,
        name,
        connection=None,
503 504
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
505 506
        default_values={},
    ):
Matias Guijarro's avatar
Matias Guijarro committed
507
        if connection is None:
508
            connection = get_redis_connection()
Matias Guijarro's avatar
Matias Guijarro committed
509 510 511 512
        self._cnx = weakref.ref(connection)
        self._name = name
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
513
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
514

515 516 517 518
    @property
    def name(self):
        return self._name

519 520
    def __repr__(self):
        value = self.get_all()
521
        return "<HashSetting name=%s value=%s>" % (self._name, value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
522 523

    def __delitem__(self, key):
524
        cnx = self._cnx()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
525
        cnx.hdel(self._name, key)
Matias Guijarro's avatar
Matias Guijarro committed
526

527
    def __len__(self):
Matias Guijarro's avatar
Matias Guijarro committed
528 529 530
        cnx = self._cnx()
        return cnx.hlen(self._name)

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

534
    def raw_get(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
535
        cnx = self._cnx()
536
        return cnx.hget(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
537 538

    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
539
    def get(self, key, default=None):
Matias Guijarro's avatar
Matias Guijarro committed
540 541
        v = self.raw_get(key)
        if v is None:
542
            v = DefaultValue(default)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
543
        return v
Matias Guijarro's avatar
Matias Guijarro committed
544

Matias Guijarro's avatar
Matias Guijarro committed
545 546 547
    def _raw_get_all(self):
        cnx = self._cnx()
        return cnx.hgetall(self._name)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
548

Matias Guijarro's avatar
Matias Guijarro committed
549 550
    def get_all(self):
        all_dict = dict(self._default_values)
Vincent Michel's avatar
Vincent Michel committed
551
        for k, raw_v in self._raw_get_all().items():
552
            k = k.decode()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
553 554 555
            v = self._read_type_conversion(raw_v)
            if isinstance(v, InvalidValue):
                raise ValueError(
556 557 558
                    "%s: Invalid value '%s` (cannot deserialize %r)"
                    % (self._name, k, raw_v)
                )
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
559
            all_dict[k] = v
Matias Guijarro's avatar
Matias Guijarro committed
560
        return all_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
561

Matias Guijarro's avatar
Matias Guijarro committed
562
    @read_decorator
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
563
    def pop(self, key, default=Null()):
564
        cnx = self._cnx().pipeline()
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
565 566 567
        cnx.hget(self._name, key)
        cnx.hdel(self._name, key)
        (value, worked) = cnx.execute()
568
        if not worked:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
569
            if isinstance(default, Null):
570 571 572
                raise KeyError(key)
            else:
                value = default
Matias Guijarro's avatar
Matias Guijarro committed
573
        return value
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
574

Matias Guijarro's avatar
Matias Guijarro committed
575
    def remove(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
576
        cnx = self._cnx()
Matias Guijarro's avatar
Matias Guijarro committed
577
        cnx.hdel(self._name, *keys)
Matias Guijarro's avatar
Matias Guijarro committed
578

579
    def clear(self):
Matias Guijarro's avatar
Matias Guijarro committed
580 581 582
        cnx = self._cnx()
        cnx.delete(self._name)

583
    def copy(self):
Matias Guijarro's avatar
Matias Guijarro committed
584 585 586
        return self.get()

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
587
    def set(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
588 589 590
        cnx = self._cnx()
        cnx.delete(self._name)
        if values is not None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
591
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
592 593

    @write_decorator_dict
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
594
    def update(self, values):
Matias Guijarro's avatar
Matias Guijarro committed
595
        cnx = self._cnx()
596
        if values:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
597
            cnx.hmset(self._name, values)
Matias Guijarro's avatar
Matias Guijarro committed
598 599

    @read_decorator
600
    def fromkeys(self, *keys):
Matias Guijarro's avatar
Matias Guijarro committed
601
        cnx = self._cnx()
602
        return cnx.hmget(self._name, *keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
603 604

    def has_key(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
605
        cnx = self._cnx()
Vincent Michel's avatar
Vincent Michel committed
606
        return cnx.hexists(self._name, key) or key in self._default_values
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
607

608
    def keys(self):
Vincent Michel's avatar
Vincent Michel committed
609
        for k, v in self.items():
610 611
            yield k

612
    def values(self):
Vincent Michel's avatar
Vincent Michel committed
613
        for k, v in self.items():
614
            yield v
Matias Guijarro's avatar
Matias Guijarro committed
615

616
    def items(self):
Matias Guijarro's avatar
Matias Guijarro committed
617 618
        cnx = self._cnx()
        next_id = 0
619
        seen_keys = set()
Matias Guijarro's avatar
Matias Guijarro committed
620
        while True:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
621
            next_id, pd = cnx.hscan(self._name, next_id)
Vincent Michel's avatar
Vincent Michel committed
622
            for k, v in pd.items():
623 624
                # Add key conversion
                k = k.decode()
Matias Guijarro's avatar
Matias Guijarro committed
625 626
                if self._read_type_conversion:
                    v = self._read_type_conversion(v)
627
                seen_keys.add(k)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
628
                yield k, v
629
            if not next_id or next_id is "0":
Matias Guijarro's avatar
Matias Guijarro committed
630 631
                break

Vincent Michel's avatar
Vincent Michel committed
632
        for k, v in self._default_values.items():
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
633 634 635
            if k in seen_keys:
                continue
            yield k, v
636

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
637
    def __getitem__(self, key):
Matias Guijarro's avatar
Matias Guijarro committed
638 639
        value = self.get(key)
        if value is None:
Vincent Michel's avatar
Vincent Michel committed
640
            if key not in self._default_values:
641
                raise KeyError(key)
Matias Guijarro's avatar
Matias Guijarro committed
642 643
        return value

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
644
    def __setitem__(self, key, value):
Matias Guijarro's avatar
Matias Guijarro committed
645
        cnx = self._cnx()
646
        if value is None:
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
647
            cnx.hdel(self._name, key)
648
            return
Matias Guijarro's avatar
Matias Guijarro committed
649 650
        if self._write_type_conversion:
            value = self._write_type_conversion(value)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
651
        cnx.hset(self._name, key, value)
Matias Guijarro's avatar
Matias Guijarro committed
652

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
653
    def __contains__(self, key):
654 655 656 657 658 659 660
        try:
            self[key]
            return True
        except KeyError:
            return False


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
661
class HashSettingProp(object):
662 663 664 665
    def __init__(
        self,
        name,
        connection=None,
666 667
        read_type_conversion=auto_coerce_from_redis,
        write_type_conversion=str,
668 669 670
        default_values={},
        use_object_name=True,
    ):
Matias Guijarro's avatar
Matias Guijarro committed
671
        self._name = name
672
        self._cnx = connection or get_redis_connection()
Matias Guijarro's avatar
Matias Guijarro committed
673 674
        self._read_type_conversion = read_type_conversion
        self._write_type_conversion = write_type_conversion
675
        self._default_values = default_values
Matias Guijarro's avatar
Matias Guijarro committed
676 677
        self._use_object_name = use_object_name

678 679 680 681
    @property
    def name(self):
        return self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
682
    def __get__(self, obj, type=None):
Matias Guijarro's avatar
Matias Guijarro committed
683
        if self._use_object_name:
684
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
685 686
        else:
            name = self._name
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
687

688 689 690 691 692 693 694
        return HashSetting(
            name,
            self._cnx,
            self._read_type_conversion,
            self._write_type_conversion,
            self._default_values,
        )
Matias Guijarro's avatar
Matias Guijarro committed
695

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
696
    def __set__(self, obj, values):
Matias Guijarro's avatar
Matias Guijarro committed
697
        if self._use_object_name:
698
            name = obj.name + ":" + self._name
Matias Guijarro's avatar
Matias Guijarro committed
699 700 701
        else:
            name = self._name

Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
702 703
        if isinstance(values, HashSetting):
            return
Matias Guijarro's avatar
Matias Guijarro committed
704

705 706 707 708 709 710 711
        proxy = HashSetting(
            name,
            self._cnx,
            self._read_type_conversion,
            self._write_type_conversion,
            self._default_values,
        )
Matias Guijarro's avatar
Matias Guijarro committed
712
        proxy.set(values)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
713

714
    def get_proxy(self):
715 716 717 718 719 720 721 722 723
        return HashSetting(
            self._name,
            self._cnx,
            self._read_type_conversion,
            self._write_type_conversion,
            self._default_values,
        )


Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
724 725
# helper

Matias Guijarro's avatar
Matias Guijarro committed
726

727
def _change_to_obj_marshalling(keys):
728 729 730 731 732 733 734 735
    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
pep8  
Sebastien Petitdemange committed
736

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
737

738
class HashObjSetting(HashSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
739
    def __init__(self, name, **keys):
740
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
741 742
        HashSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
743

744
class HashObjSettingProp(HashSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
745
    def __init__(self, name, **keys):
746
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
747 748
        HashSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
749

750
class QueueObjSetting(QueueSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
751
    def __init__(self, name, **keys):
752
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
753 754
        QueueSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
755 756

class QueueObjSettingProp(QueueSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
757
    def __init__(self, name, **keys):
758
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
759 760
        QueueSettingProp.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
761

762
class SimpleObjSetting(SimpleSetting):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
763
    def __init__(self, name, **keys):
764
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
765 766
        SimpleSetting.__init__(self, name, **keys)

Sebastien Petitdemange's avatar
Sebastien Petitdemange committed
767 768

class SimpleObjSettingProp(SimpleSettingProp):
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
769
    def __init__(self, name, **keys):
770
        _change_to_obj_marshalling(keys)
Sebastien Petitdemange's avatar
pep8  
Sebastien Petitdemange committed
771 772
        SimpleSettingProp.__init__(self, name, **keys<