def test_dict_ops():
    ed = ExpiringDict()
    ed["one"] = 1
    ed["two"] = 2
    ed["three"] = 3
    d = dict()
    d["one"] = 1
    d["two"] = 2
    d["three"] = 3
    assert [x for x in d] == [x for x in ed]
    assert [k for k in d.keys()] == [k for k in ed.keys()]
    assert [v for v in d.values()] == [v for v in ed.values()]
    assert [i for i in d.items()] == [i for i in ed.items()]
    assert "one" in ed
class Server(object):
    def __init__(self, host=None, port=None, max_clients=None):
        if not host:
            host = os.getenv('HOST', '0.0.0.0')
        if not port:
            port = os.getenv('PORT', 31337)
        if not max_clients:
            max_clients = os.getenv('MAX_CLIENTS', 15)
        self._pool = Pool(int(max_clients))
        self._server = StreamServer((host, int(port)),
                                    self.connection_handler,
                                    spawn=self._pool)

        self._protocol = DataHandler()
        self._kv = ExpiringDict()

        self._commands = self.get_commands()

    def get_commands(self):
        return {
            'GET': self.get,
            'SET': self.set,
            'DEL': self.delete,
            'KEYS': self.keys,
            'FLUSHDB': self.flush,
            'EXPIRE': self.expire,
            'HGET': self.hget,
            'HSET': self.hset,
            'LSET': self.lset,
            'RPUSH': self.rpush,
            'LPUSH': self.lpush,
            'LRANGE': self.lrange,
            'LINDEX': self.lindex
        }

    def connection_handler(self, conn, address):
        logger.info('Connection received: %s:%s' % address)
        socket_file = conn.makefile('rwb')

        while True:
            try:
                data = self._protocol.handle_request(socket_file)
            except Disconnect:
                logger.info('Client went away: %s:%s' % address)
                break
            try:
                resp = self.get_response(data)
            except CommandError as exc:
                logger.exception('Command error')
                resp = Error(exc.args[0])

            self._protocol.write_response(socket_file, resp)

    def run(self):
        self._server.serve_forever()

    def get_response(self, data):
        if not isinstance(data, list):
            try:
                data = data.split()
            except:
                raise CommandError('Request must be list or simple string.')

        if not data:
            raise CommandError('Missing command')

        command = data[0].upper()
        if command not in self._commands:
            raise CommandError('Unrecognized command: %s' % command)
        else:
            logger.debug('Received %s', command)
        try:
            response = self._commands[command](*data[1:])
        except TypeError:
            raise CommandError(
                f'ERR wrong number of arguments for {command.lower()} command')
        return response

    def get(self, key):
        return self._kv.get(key)

    def set(self, key, value):
        self._kv[key] = value
        return '1'

    def delete(self, key):
        if key in self._kv:
            del self._kv[key]
            return 1
        return 0

    def keys(self, pattern):
        r = []
        if pattern == '*':
            pattern = '\w*'
        for k in self._kv.keys():
            if re.match(pattern, k):
                r.append(k)
        return r

    def flush(self):
        kvlen = len(self._kv)
        self._kv.clear()
        return str(kvlen)

    def expire(self, key, ttl):
        value = self._kv.get(key, None)
        if value:
            del self._kv[key]
            self._kv.ttl(key, value, float(ttl))
            return 1
        return 0

    def hset(self, key, field, value):
        self._kv.setdefault(key, {})
        self._kv[key][field] = value
        return 1

    def hget(self, k, field):
        if self._kv.get(k):
            return self._kv[k].get(field)

    def lset(self, key, index, value):
        self._kv.setdefault(key, [])
        self._kv[key][int(index)] = value
        return 'OK'

    def rpush(self, key, *value):
        self._kv.setdefault(key, [])
        for v in value:
            self._kv[key].append(v)
        return len(self._kv[key])

    def lpush(self, key, *value):
        self._kv.setdefault(key, [])
        for v in value:
            self._kv[key].insert(0, v)
        return len(self._kv[key])

    def lrange(self, key, start, end):
        if isinstance(self._kv.get(key), list):
            if end == '-1':
                return self._kv[key][int(start):]
            return self._kv[key][int(start):int(end) + 1]
        return None

    def lindex(self, key, index):
        if isinstance(self._kv.get(key), list):
            try:
                return self._kv[key][int(index)]
            except IndexError:
                return None
        return None