def __init__(self, master_servers, slave_servers=None):
     """
         Initialize MultiNodeRedis object.  The master hosts should contain
         the key "node_name".  This key identifies the node, and will be
         used to maintain a node mapping for sharding data.
         Args:
             master_servers - e.g. [node1|127.0.0.1:6379,
                                    node2|127.0.0.1:6370]
             slave_servers - same as master_hosts
     """
     self.manager = MultiNodeManager(master_servers, slave_servers=slave_servers)
class MultiNodeRedis(object):
    def __init__(self, master_servers, slave_servers=None):
        """
            Initialize MultiNodeRedis object.  The master hosts should contain
            the key "node_name".  This key identifies the node, and will be
            used to maintain a node mapping for sharding data.
            Args:
                master_servers - e.g. [node1|127.0.0.1:6379,
                                       node2|127.0.0.1:6370]
                slave_servers - same as master_hosts
        """
        self.manager = MultiNodeManager(master_servers, slave_servers=slave_servers)

    def __setitem__(self, name, value):
        self.set(name, value)

    def __getitem__(self, name):
        """
        Return the value at key ``name``, raises a KeyError if the key
        doesn't exist.
        """
        value = self.get(name)
        if value:
            return value
        raise KeyError(name)

    @classmethod
    def _list_or_args(cls, keys, args):
        # returns a single list combining keys and args
        try:
            iter(keys)
            # a string or bytes instance can be iterated, but indicates
            # keys wasn't passed as a list
            if isinstance(keys, (basestring, bytes)):
                keys = [keys]
        except TypeError:
            keys = [keys]
        if args:
            keys.extend(args)
        return keys

    def _get_node(self, key, use_slave=False):
        return self.manager._get_node(key, use_slave=use_slave)

    def _get_all_nodes(self):
        return self.manager._get_all_nodes()

    def delete(self, key):
        node = self._get_node(key)
        return node.delete(key)

    def expire(self, key, time):
        node = self._get_node(key)
        return node.expire(key, time)

    def flushall(self):
        for node in self._get_all_nodes():
            node.flushall()

    def get(self, key):
        node = self._get_node(key)
        return node.get(key)

    def hget(self, key, field):
        node = self._get_node(key)
        return node.hget(key, field)

    def hgetall(self, key):
        node = self._get_node(key)
        return node.hgetall(key)

    def hincrby(self, key, field, amount=1):
        node = self._get_node(key)
        return node.hincrby(key, field, amount=amount)

    def hmset(self, key, mapping):
        node = self._get_node(key)
        return node.hmset(key, mapping)

    def hset(self, key, field, value):
        node = self._get_node(key)
        return node.hset(key, field, value)

    def incr(self, key, amount=1):
        node = self._get_node(key)
        return node.incr(key, amount=amount)

    def llen(self, key):
        node = self._get_node(key)
        return node.llen(key)

    def mget(self, keys, *args):
        args = self._list_or_args(keys, args)
        node_map = {}
        for key in args:
            node = self._get_node(key)
            if node not in node_map:
                node_map[node] = []
            node_map[node].append(key)
        mapping = {}
        for node, node_keys in node_map.iteritems():
            node_values = node.mget(node_keys)
            for i, node_key in enumerate(node_keys):
                mapping[node_key] = node_values[i]
        values = []
        for key in args:
            values.append(mapping.get(key, None))
        return values

    def mset(self, *args, **kwargs):
        if args:
            if len(args) != 1 or not isinstance(args[0], dict):
                raise MultiNodeRedisException('MSET requires **kwargs or a ' +
                    'single dict arg')
            kwargs.update(args[0])
        node_map = {}
        for key, value in kwargs.iteritems():
            node = self._get_node(key)
            if node not in node_map:
                node_map[node] = {}
            node_map[node][key] = value
        success = 0
        for node, node_kwargs in node_map.iteritems():
            success = success or node.mset(node_kwargs)
        return success

    def persist(self, key):
        node = self._get_node(key)
        return node.persist(key)

    def pipeline(self, transaction=True, shard_hint=None):
        # TODO(ks) - 5/22/14 - I'm not actually sure if pipeline should return
        # the same pipeline object or a new one each time.
        return MultiNodePipeline(self.manager,
                                 transaction=transaction,
                                 shard_hint=shard_hint)

    def set(self, key, value, ex=None, px=None, nx=False, xx=False):
        node = self._get_node(key)
        return node.set(key, value)
        # TODO(ks) - 5/22/14 - Figure out why ex doesn't work
        # return node.set(key, value, ex=ex, px=px, nx=nx, xx=xx)

    def ttl(self, key):
        node = self._get_node(key)
        return node.ttl(key)

    def zadd(self, key, *args, **kwargs):
        node = self._get_node(key)
        return node.zadd(key, *args, **kwargs)

    def zincrby(self, key, value, amount=1):
        node = self._get_node(key)
        return node.zincrby(key, value, amount=amount)

    def zrange(self, key, start, end, desc=False, withscores=False,
               score_cast_func=float):
        node = self._get_node(key)
        return node.zrange(key, start, end, desc=desc, withscores=withscores,
                           score_cast_func=score_cast_func)