sider.types — Conversion between Python and Redis types

In Redis all data are byte strings — bulks. Lists are lists of byte strings, sets are sets of byte strings, and hashes consist of byte string keys and byte string values.

To store richier objects into Redis we have to encode Python values and decode Redis data. Bulk and its subclasses are for that, it defines two basic methods: encode() and decode(). For example, Integer encodes Python int 3 into Redis bulk "3" and decodes Redis bulk "3" into Python int 3.

Inheritance diagram of sider.types

class sider.types.Boolean

Bases: sider.types.Integer

Stores bool values as '1' or '0'.

>>> boolean = Boolean()
>>> boolean.encode(True)
'1'
>>> boolean.encode(False)
'0'
class sider.types.Bulk

Bases: sider.types.Value

The abstract base class to be subclassed. You have to implement encode() and decode() methods in subclasses.

decode(bulk)

Decodes a Redis bulk to Python object. Every subclass of Bulk must implement this method. By default it raises NotImplementedError.

Parameters:bulk (str) – a Redis bullk to decode into Python object
Returns:a decoded Python object
encode(value)

Encodes a Python value into Redis bulk. Every subclass of Bulk must implement this method. By default it raises NotImplementedError.

Parameters:value – a Python value to encode into Redis bulk
Returns:an encoded Redis bulk
Return type:str
Raises exceptions.TypeError:
 if the type of a given value is not acceptable by this type
class sider.types.ByteString

Bases: sider.types.Bulk

Stores byte strings. It stores the given byte strings as these are. It works simply transparently for str values.

>>> bytestr = ByteString()
>>> bytestr.encode('annyeong')
'annyeong'
>>> bytestr.decode('sayonara')
'sayonara'
bytes_type

alias of str

class sider.types.Date

Bases: sider.types.Bulk

Stores datetime.date values. Dates are internally formatted in RFC 3339 format e.g. 2012-03-28.

>>> import datetime
>>> date = Date()
>>> date.encode(datetime.date(2012, 3, 28))
'2012-03-28'
>>> date.decode(_)
datetime.date(2012, 3, 28)
DATE_FORMAT = '%Y-%m-%d'

(str) The strftime() format string for RFC 3339.

DATE_PATTERN = <_sre.SRE_Pattern object>

The re pattern that matches to RFC 3339 formatted date string e.g. '2012-03-28'.

class sider.types.DateTime

Bases: sider.types.Bulk

Stores naive datetime.datetime values. Values are internally formatted in RFC 3339 format e.g. 2012-03-28T09:21:34.638972.

>>> dt = DateTime()
>>> dt.decode('2012-03-28T09:21:34.638972')
datetime.datetime(2012, 3, 28, 9, 21, 34, 638972)
>>> dt.encode(_)
'2012-03-28T09:21:34.638972'

It doesn’t store tzinfo data.

>>> from sider.datetime import UTC
>>> decoded = dt.decode('2012-03-28T09:21:34.638972Z')
>>> decoded
datetime.datetime(2012, 3, 28, 9, 21, 34, 638972)
>>> dt.encode(decoded.replace(tzinfo=UTC))
'2012-03-28T09:21:34.638972'

Note

If you must be aware of time zone, use TZDateTime instead.

parse_datetime(bulk)

Parses a RFC 3339 formatted string into datetime.datetime.

>>> dt = DateTime()
>>> dt.parse_datetime('2012-03-28T09:21:34.638972')
datetime.datetime(2012, 3, 28, 9, 21, 34, 638972)

Unlike decode() it is aware of tzinfo data if the string contains time zone notation.

>>> a = dt.parse_datetime('2012-03-28T09:21:34.638972Z')
>>> a  
datetime.datetime(2012, 3, 28, 9, 21, 34, 638972,
                  tzinfo=sider.datetime.Utc())
>>> b = dt.parse_datetime('2012-03-28T18:21:34.638972+09:00')
>>> b  
datetime.datetime(2012, 3, 28, 18, 21, 34, 638972,
                  tzinfo=sider.datetime.FixedOffset(540))
>>> a == b
True
Parameters:bulk (basestring) – a RFC 3339 formatted string
Returns:a parsing result
Return type:datetime.datetime

Note

It is for internal use and decode() method actually uses this method.

class sider.types.Hash(key_type=None, value_type=None)

Bases: sider.types.Value

The type object for sider.hash.Hash objects and other collections.Mapping objects.

Parameters:
  • key_type (Bulk, type) – the type of keys the hash will contain. default is String
  • value_type (Bulk, type) – the type of values the hash will contain. default is String
class sider.types.Integer

Bases: sider.types.Bulk

Stores integers as decimal strings. For example:

>>> integer = Integer()
>>> integer.encode(42)
'42'
>>> integer.decode('42')
42

Why it doesn’t store integers as binaries but decimals is that Redis provides INCR, INCRBY, DECR and DECRBY for decimal strings. You can simply add and subtract integers.

class sider.types.List(value_type=None)

Bases: sider.types.Value

The type object for sider.list.List objects and other collections.Sequence objects except strings. (Use ByteString or UnicodeString for strings.)

Parameters:value_type (Bulk, type) – the type of values the list will contain. default is String
class sider.types.Set(value_type=None)

Bases: sider.types.Value

The type object for sider.set.Set objects and other collections.Set objects.

Parameters:value_type (Bulk, type) – the type of values the set will contain. default is String
class sider.types.SortedSet(value_type=None)

Bases: sider.types.Set

The type object for sider.sortedset.SortedSet objects.

Parameters:value_type (Bulk, type) – the type of values the sorted set will contain. default is String
sider.types.String

alias of ByteString

class sider.types.TZDateTime

Bases: sider.types.DateTime

Similar to DateTime except it accepts only tz-aware datetime.datetime values. All values are internally stored in UTC.

>>> from sider.datetime import FixedOffset
>>> dt = datetime.datetime(2012, 3, 28, 18, 21, 34, 638972,
...                        tzinfo=FixedOffset(540))
>>> tzdt = TZDateTime()
>>> tzdt.encode(dt)
'2012-03-28T09:21:34.638972Z'
>>> tzdt.decode(_)  
datetime.datetime(2012, 3, 28, 9, 21, 34, 638972,
                  tzinfo=sider.datetime.Utc())

If any naive datetime.datetime has passed it will raise ValueError.

class sider.types.TZTime

Bases: sider.types.Time

Similar to Time except it accepts only tz-aware datetime.time values.

>>> from sider.datetime import FixedOffset
>>> time = datetime.time(18, 21, 34, 638972,
...                      tzinfo=FixedOffset(540))
>>> tztime = TZTime()
>>> tztime.encode(time)
'18:21:34.638972+09:00'
>>> tztime.decode(_)  
datetime.time(18, 21, 34, 638972,
              tzinfo=sider.datetime.FixedOffset(540))
>>> utctime = datetime.time(9, 21, 34, 638972, tzinfo=UTC)
>>> tztime.encode(utctime)
'09:21:34.638972Z'
>>> tztime.decode(_)
datetime.time(9, 21, 34, 638972, tzinfo=sider.datetime.Utc())

If any naive datetime.time has passed it will raise ValueError.

class sider.types.Time

Bases: sider.types.Bulk

Stores naive datetime.time values.

>>> time = Time()
>>> time.decode('09:21:34.638972')
datetime.time(9, 21, 34, 638972)
>>> time.encode(_)
'09:21:34.638972'

It doesn’t store tzinfo data.

>>> from sider.datetime import UTC
>>> time = Time()
>>> decoded = time.decode('09:21:34.638972Z')
>>> decoded
datetime.time(9, 21, 34, 638972)
>>> time.encode(decoded.replace(tzinfo=UTC))
'09:21:34.638972'

Note

If you must be aware of time zone, use TZTime instead.

parse_time(bulk, drop_tzinfo)

Parses an encoded datetime.time.

Parameters:bulk (basestring) – an encoded time
Returns:a parsed time
Return type:datetime.time

Note

It is for internal use and decode() method actually uses this method.

class sider.types.TimeDelta

Bases: sider.types.Bulk

Stores datetime.timedelta values.

>>> import datetime
>>> td = TimeDelta()
>>> delta = datetime.timedelta(days=3, seconds=53, microseconds=123123)
>>> td.encode(delta)
'3,53,123123'
>>> td.decode(_)
datetime.timedelta(3, 53, 123123)
class sider.types.Tuple(*field_types)

Bases: sider.types.Bulk

Stores tuples of fixed fields. It can be used for compositing multiple fields into one field in ad-hoc way. For example, if you want to store 3D point value without defining new Type:

Tuple(Integer, Integer, Integer)

The above type will store three integers in a field.

>>> int_str_int = Tuple(Integer, ByteString, Integer)
>>> int_str_int.encode((123, 'abc\ndef', 456))
'3,7,3\n123\nabc\ndef\n456'
>>> int_str_int.decode(_)
(123, 'abc\ndef', 456)

Encoded values become a bulk bytes. It consists of a header line and other lines that contain field values. The first header line is a comma-separated integers that represent each byte size of encoded field values.

tuple  ::=  header (newline field)*
header ::=  [size ("," size)*]
size   ::=  digit+
digit  ::=  "0" | "1" | "2" | "3" | "4" |
            "5" | "6" | "7" | "8" | "9"
Parameters:*field_types – the variable number of field types
field_types = None

(tuple) The tuple of field types.

class sider.types.UUID

Bases: sider.types.Bulk

Stores uuid.UUID values. For example:

>>> import uuid
>>> u = UUID()
>>> u.encode(uuid.UUID(int=134626331218489933988508161913704617318))
'65481698-2f85-4bd6-8f7c-ee8aaecf1566'
>>> u.decode('65481698-2f85-4bd6-8f7c-ee8aaecf1566')
UUID('65481698-2f85-4bd6-8f7c-ee8aaecf1566')
class sider.types.UnicodeString

Bases: sider.types.Bulk

Stores Unicode strings (unicode), not byte strings (str). Internally all Unicode strings are encoded into and decoded from UTF-8 byte strings.

>>> unistr = UnicodeString()
>>> unistr.encode(u'\uc720\ub2c8\ucf54\ub4dc')
'\xec\x9c\xa0\xeb\x8b\x88\xec\xbd\x94\xeb\x93\x9c'
>>> unistr.decode(_)
u'\uc720\ub2c8\ucf54\ub4dc'
string_type

alias of unicode

class sider.types.Value

Bases: object

There are two layers behind Sider types: the lower one is this Value and the higher one is Bulk.

Value types can be set to Redis keys, but unlike Bulk it cannot be a value type of other rich Value types e.g. List, Hash.

In most cases you (users) don’t have to subclass Value, and should not. Direct subclasses of Value aren’t about encodings/decodings of Python object but simply Python-side representations of Redis types. It actually doesn’t have methods like encode() and decode(). These methods appear under Bulk or its subtypes.

But it’s about how to save Python objects into Redis keys and how to load values from associated Redis keys. There are several commands to save like SET, MSET, HSET, RPUSH and the rest in Redis and subtypes have to decide which command of those to use.

All subtypes of Value implement save_value() and load_value() methods. The constructor which takes no arguments have to be implemented as well.

classmethod ensure_value_type(value_type, parameter=None)

Raises a TypeError if the given value_type is not an instance of nor a subclass of the class.

>>> Integer.ensure_value_type(Bulk
... )  
Traceback (most recent call last):
  ...
TypeError: expected a subtype of sider.types.Integer,
           but sider.types.Bulk was passed
>>> Integer.ensure_value_type(UnicodeString()
... )  
Traceback (most recent call last):
  ...
TypeError: expected an instance of sider.types.Integer,
           but <sider.types.UnicodeString object at ...>
           was passed
>>> Bulk.ensure_value_type(1)
Traceback (most recent call last):
  ...
TypeError: expected a type, not 1

Otherwise it simply returns an instance of the given value_type.

>>> Bulk.ensure_value_type(Bulk)  
<sider.types.Bulk object at ...>
>>> Bulk.ensure_value_type(ByteString)  
<sider.types.ByteString object at ...>
>>> ByteString.ensure_value_type(ByteString
... )  
<sider.types.ByteString object at ...>
>>> bytestr = ByteString()
>>> ByteString.ensure_value_type(bytestr) 
<sider.types.ByteString object at ...>

If an optional parameter name has present, the error message becomes better.

>>> Integer.ensure_value_type(Bulk,
...   parameter='argname')  
Traceback (most recent call last):
  ...
TypeError: argname must be a subtype of sider.types.Integer,
           but sider.types.Bulk was passed
>>> Integer.ensure_value_type(UnicodeString(),
...   parameter='argname'
... )  
Traceback (most recent call last):
  ...
TypeError: argname must be an instance of sider.types.Integer,
           but <sider.types.UnicodeString object at ...>
           was passed
>>> Bulk.ensure_value_type(1, parameter='argname')
Traceback (most recent call last):
  ...
TypeError: argname must be a type, not 1
Parameters:
  • value_type (Value, type) – a type expected to be a subtype of the class
  • parameter (str) – an optional parameter name. if present the error message becomes better
Raises exceptions.TypeError:
 

if the given subtype is not a subclass of the class

load_value(session, key)

How to load the value from the given Redis key. Subclasses have to implement it. By default it raises NotImplementedError.

Parameters:
  • session (sider.session.Session) – the session object that stores the given key
  • key (str) – the key name to load
Returns:

the Python representation of the loaded value

save_value(session, key, value)

How to save the given value into the given Redis key. Subclasses have to implement it. By default it raises NotImplementedError.

Parameters:
  • session (sider.session.Session) – the session object going to store the given keyvalue pair
  • key (str) – the key name to save the value
  • value – the value to save into the key
Returns:

the Python representation of the saved value. it is equivalent to the given value but may not equal nor the same to