pax_global_header00006660000000000000000000000064117433361540014521gustar00rootroot0000000000000052 comment=b3f20838a8e63b345e034f35284405a272f9223f hiredis-py-0.1.1/000077500000000000000000000000001174333615400135755ustar00rootroot00000000000000hiredis-py-0.1.1/.gitignore000066400000000000000000000000341174333615400155620ustar00rootroot00000000000000*.pyc /build /dist MANIFEST hiredis-py-0.1.1/.gitmodules000066400000000000000000000001401174333615400157450ustar00rootroot00000000000000[submodule "vendor/hiredis"] path = vendor/hiredis url = git://github.com/antirez/hiredis.git hiredis-py-0.1.1/COPYING000066400000000000000000000027061174333615400146350ustar00rootroot00000000000000Copyright (c) 2011, Pieter Noordhuis All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Redis nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. hiredis-py-0.1.1/MANIFEST.in000066400000000000000000000002351174333615400153330ustar00rootroot00000000000000include COPYING include src/*.[ch] include vendor/hiredis/COPYING include vendor/hiredis/*.[ch] exclude vendor/hiredis/example* exclude vendor/hiredis/text* hiredis-py-0.1.1/README.md000066400000000000000000000077221174333615400150640ustar00rootroot00000000000000# hiredis-py Python extension that wraps protocol parsing code in [hiredis](http://github.com/antirez/hiredis). It is targeted at speeding up parsing multi bulk replies. ## Install hiredis-py is available on [PyPi](http://pypi.python.org/pypi/hiredis), and can be installed with: easy_install hiredis ## Usage The `hiredis` module contains the `Reader` class. This class is responsible for parsing replies from the stream of data that is read from a Redis connection. It does not contain functionality to handle I/O. ### Reply parser The `Reader` class has two methods that are used when parsing replies from a stream of data. `Reader.feed` takes a string argument that is appended to the internal buffer. `Reader.gets` reads this buffer and returns a reply when the buffer contains a full reply. If a single call to `feed` contains multiple replies, `gets` should be called multiple times to extract all replies. Example: >>> reader = hiredis.Reader() >>> reader.feed("$5\r\nhello\r\n") >>> reader.gets() 'hello' When the buffer does not contain a full reply, `gets` returns `False`. This means extra data is needed and `feed` should be called again before calling `gets` again: >>> reader.feed("*2\r\n$5\r\nhello\r\n") >>> reader.gets() False >>> reader.feed("$5\r\nworld\r\n") >>> reader.gets() ['hello', 'world'] #### Unicode `hiredis.Reader` is able to decode bulk data to any encoding Python supports. To do so, specify the encoding you want to use for decoding replies when initializing it: >>> reader = hiredis.Reader(encoding="utf-8") >>> reader.feed("$3\r\n\xe2\x98\x83\r\n") >>> reader.gets() u'☃' When bulk data in a reply could not be properly decoded using the specified encoding, it will be returned as a plain string. When the encoding cannot be found, a `LookupError` will be raised after calling `gets` for the first reply with bulk data (identical to what Python's `unicode` method would do). #### Error handling When a protocol error occurs (because of multiple threads using the same socket, or some other condition that causes a corrupt stream), the error `hiredis.ProtocolError` is raised. Because the buffer is read in a lazy fashion, it will only be raised when `gets` is called and the first reply in the buffer contains an error. There is no way to recover from a faulty protocol state, so when this happens, the I/O code feeding data to `Reader` should probably reconnect. Redis can reply with error replies (`-ERR ...`). For these replies, the custom error class `hiredis.ReplyError` is returned, **but not raised**. When other error types should be used (so existing code doesn't have to change its `except` clauses), `Reader` can be initialized with the `protocolError` and `replyError` keywords. These keywords should contain a *class* that is a subclass of `Exception`. When not provided, `Reader` will use the default error types. ## Benchmarks The repository contains a benchmarking script in the `benchmark` directory, which uses [gevent](http://gevent.org/) to have non-blocking I/O and redis-py to handle connections. These benchmarks are done with a patched version of redis-py that uses hiredis-py when it is available. All benchmarks are done with 10 concurrent connections. * SET key value + GET key * redis-py: 11.76 Kops * redis-py *with* hiredis-py: 13.40 Kops * improvement: **1.1x** List entries in the following tests are 5 bytes. * LRANGE list 0 **9**: * redis-py: 4.78 Kops * redis-py *with* hiredis-py: 12.94 Kops * improvement: **2.7x** * LRANGE list 0 **99**: * redis-py: 0.73 Kops * redis-py *with* hiredis-py: 11.90 Kops * improvement: **16.3x** * LRANGE list 0 **999**: * redis-py: 0.07 Kops * redis-py *with* hiredis-py: 5.83 Kops * improvement: **83.2x** Throughput improvement for simple SET/GET is minimal, but the larger multi bulk replies get, the larger the performance improvement is. ## License This code is released under the BSD license, after the license of hiredis. hiredis-py-0.1.1/benchmark/000077500000000000000000000000001174333615400155275ustar00rootroot00000000000000hiredis-py-0.1.1/benchmark/benchmark.py000077500000000000000000000023731174333615400200430ustar00rootroot00000000000000#!/usr/bin/env python import sys, optparse, timeit from functools import partial from redis import Redis import gevent from gevent import monkey from gevent.coros import Semaphore monkey.patch_all() parser = optparse.OptionParser() parser.add_option("-n", dest="count", metavar="COUNT", type="int", default=10000) parser.add_option("-c", dest="clients", metavar="CLIENTS", type="int", default=1), (options, args) = parser.parse_args() commands = list() for line in sys.stdin.readlines(): argv = line.strip().split() commands.append((argv[0], argv[1:])) sem = Semaphore(0) count = options.count todo = count def create_client(): global todo redis = Redis(host="localhost", port=6379, db=0) sem.acquire() while todo > 0: todo -= 1 for (cmd, args) in commands: getattr(redis, cmd)(*args) def run(clients): [sem.release() for _ in range(len(clients))] # Time how long it takes for all greenlets to finish join = partial(gevent.joinall, clients) time = timeit.timeit(join, number=1) print "%.2f Kops" % ((len(commands) * count / 1000.0) / time) # Let clients connect, and kickstart benchmark a little later clients = [gevent.spawn(create_client) for _ in range(options.clients)] let = gevent.spawn(run, clients) gevent.joinall([let]) hiredis-py-0.1.1/benchmark/lrange000066400000000000000000000000621174333615400167200ustar00rootroot00000000000000lpush list value ltrim list 0 99 lrange list 0 99 hiredis-py-0.1.1/benchmark/simple000066400000000000000000000000261174333615400167410ustar00rootroot00000000000000set key value get key hiredis-py-0.1.1/hiredis/000077500000000000000000000000001174333615400152245ustar00rootroot00000000000000hiredis-py-0.1.1/hiredis/__init__.py000066400000000000000000000002771174333615400173430ustar00rootroot00000000000000from .hiredis import Reader, HiredisError, ProtocolError, ReplyError from .version import __version__ __all__ = [ "Reader", "HiredisError", "ProtocolError", "ReplyError", "__version__"] hiredis-py-0.1.1/hiredis/version.py000066400000000000000000000000261174333615400172610ustar00rootroot00000000000000__version__ = "0.1.1" hiredis-py-0.1.1/setup.py000077500000000000000000000026231174333615400153150ustar00rootroot00000000000000#!/usr/bin/env python from distutils.core import setup, Extension from distutils.command import install_lib as _install_lib import sys, imp, os, glob def version(): module = imp.load_source("hiredis.version", "hiredis/version.py") return module.__version__ # Patch "install_lib" command to run build_clib before build_ext # to properly work with easy_install. # See: http://bugs.python.org/issue5243 class install_lib(_install_lib.install_lib): def build(self): if not self.skip_build: if self.distribution.has_pure_modules(): self.run_command('build_py') if self.distribution.has_c_libraries(): self.run_command('build_clib') if self.distribution.has_ext_modules(): self.run_command('build_ext') lib = ("hiredis", { "sources": ["vendor/hiredis/%s.c" % src for src in ("hiredis", "net", "sds")], "include_dirs": ["vendor/hiredis"]}) ext = Extension("hiredis.hiredis", sources=glob.glob("src/*.c"), include_dirs=["src", "vendor"], libraries=["hiredis"]) setup( name="hiredis", version=version(), description="Python extension that wraps hiredis", url="https://github.com/pietern/hiredis-py", author="Pieter Noordhuis", author_email="pcnoordhuis@gmail.com", keywords=["Redis"], license="BSD", packages=["hiredis"], libraries=[lib], ext_modules=[ext], # Override "install_lib" command cmdclass={ "install_lib": install_lib }, ) hiredis-py-0.1.1/src/000077500000000000000000000000001174333615400143645ustar00rootroot00000000000000hiredis-py-0.1.1/src/hiredis.c000066400000000000000000000040761174333615400161660ustar00rootroot00000000000000#include "hiredis.h" #include "reader.h" #if IS_PY3K static int hiredis_ModuleTraverse(PyObject *m, visitproc visit, void *arg) { Py_VISIT(GET_STATE(m)->HiErr_Base); Py_VISIT(GET_STATE(m)->HiErr_ProtocolError); Py_VISIT(GET_STATE(m)->HiErr_ReplyError); return 0; } static int hiredis_ModuleClear(PyObject *m) { Py_CLEAR(GET_STATE(m)->HiErr_Base); Py_CLEAR(GET_STATE(m)->HiErr_ProtocolError); Py_CLEAR(GET_STATE(m)->HiErr_ReplyError); return 0; } static struct PyModuleDef hiredis_ModuleDef = { PyModuleDef_HEAD_INIT, MOD_HIREDIS, NULL, sizeof(struct hiredis_ModuleState), /* m_size */ NULL, /* m_methods */ NULL, /* m_reload */ hiredis_ModuleTraverse, /* m_traverse */ hiredis_ModuleClear, /* m_clear */ NULL /* m_free */ }; #else struct hiredis_ModuleState state; #endif /* Keep pointer around for other classes to access the module state. */ PyObject *mod_hiredis; #if IS_PY3K PyMODINIT_FUNC PyInit_hiredis(void) #else PyMODINIT_FUNC inithiredis(void) #endif { if (PyType_Ready(&hiredis_ReaderType) < 0) { #if IS_PY3K return NULL; #else return; #endif } #if IS_PY3K mod_hiredis = PyModule_Create(&hiredis_ModuleDef); #else mod_hiredis = Py_InitModule(MOD_HIREDIS, NULL); #endif /* Setup custom exceptions */ HIREDIS_STATE->HiErr_Base = PyErr_NewException(MOD_HIREDIS ".HiredisError", PyExc_Exception, NULL); HIREDIS_STATE->HiErr_ProtocolError = PyErr_NewException(MOD_HIREDIS ".ProtocolError", HIREDIS_STATE->HiErr_Base, NULL); HIREDIS_STATE->HiErr_ReplyError = PyErr_NewException(MOD_HIREDIS ".ReplyError", HIREDIS_STATE->HiErr_Base, NULL); PyModule_AddObject(mod_hiredis, "HiredisError", HIREDIS_STATE->HiErr_Base); PyModule_AddObject(mod_hiredis, "ProtocolError", HIREDIS_STATE->HiErr_ProtocolError); PyModule_AddObject(mod_hiredis, "ReplyError", HIREDIS_STATE->HiErr_ReplyError); Py_INCREF(&hiredis_ReaderType); PyModule_AddObject(mod_hiredis, "Reader", (PyObject *)&hiredis_ReaderType); #if IS_PY3K return mod_hiredis; #endif } hiredis-py-0.1.1/src/hiredis.h000066400000000000000000000015421174333615400161660ustar00rootroot00000000000000#ifndef __HIREDIS_PY_H #define __HIREDIS_PY_H #include #include #ifndef PyMODINIT_FUNC /* declarations for DLL import/export */ #define PyMODINIT_FUNC void #endif #if PY_MAJOR_VERSION >= 3 #define IS_PY3K 1 #endif #ifndef MOD_HIREDIS #define MOD_HIREDIS "hiredis" #endif struct hiredis_ModuleState { PyObject *HiErr_Base; PyObject *HiErr_ProtocolError; PyObject *HiErr_ReplyError; }; #if IS_PY3K #define GET_STATE(__s) ((struct hiredis_ModuleState*)PyModule_GetState(__s)) #else extern struct hiredis_ModuleState state; #define GET_STATE(__s) (&state) #endif /* Keep pointer around for other classes to access the module state. */ extern PyObject *mod_hiredis; #define HIREDIS_STATE (GET_STATE(mod_hiredis)) #ifdef IS_PY3K PyMODINIT_FUNC PyInit_hiredis(void); #else PyMODINIT_FUNC inithiredis(void); #endif #endif hiredis-py-0.1.1/src/reader.c000066400000000000000000000217131174333615400157760ustar00rootroot00000000000000#include #include "reader.h" static void Reader_dealloc(hiredis_ReaderObject *self); static int Reader_init(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds); static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds); static PyObject *Reader_feed(hiredis_ReaderObject *self, PyObject *args); static PyObject *Reader_gets(hiredis_ReaderObject *self); static PyMethodDef hiredis_ReaderMethods[] = { {"feed", (PyCFunction)Reader_feed, METH_VARARGS, NULL }, {"gets", (PyCFunction)Reader_gets, METH_NOARGS, NULL }, { NULL } /* Sentinel */ }; PyTypeObject hiredis_ReaderType = { PyVarObject_HEAD_INIT(NULL, 0) MOD_HIREDIS ".Reader", /*tp_name*/ sizeof(hiredis_ReaderObject), /*tp_basicsize*/ 0, /*tp_itemsize*/ (destructor)Reader_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash */ 0, /*tp_call*/ 0, /*tp_str*/ 0, /*tp_getattro*/ 0, /*tp_setattro*/ 0, /*tp_as_buffer*/ Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/ "Hiredis protocol reader", /*tp_doc */ 0, /*tp_traverse */ 0, /*tp_clear */ 0, /*tp_richcompare */ 0, /*tp_weaklistoffset */ 0, /*tp_iter */ 0, /*tp_iternext */ hiredis_ReaderMethods, /*tp_methods */ 0, /*tp_members */ 0, /*tp_getset */ 0, /*tp_base */ 0, /*tp_dict */ 0, /*tp_descr_get */ 0, /*tp_descr_set */ 0, /*tp_dictoffset */ (initproc)Reader_init, /*tp_init */ 0, /*tp_alloc */ Reader_new, /*tp_new */ }; static void *tryParentize(const redisReadTask *task, PyObject *obj) { PyObject *parent; if (task && task->parent) { parent = (PyObject*)task->parent->obj; assert(PyList_CheckExact(parent)); PyList_SET_ITEM(parent, task->idx, obj); } return obj; } static PyObject *createDecodedString(hiredis_ReaderObject *self, const char *str, size_t len) { PyObject *obj; if (self->encoding == NULL) { obj = PyBytes_FromStringAndSize(str, len); } else { obj = PyUnicode_Decode(str, len, self->encoding, NULL); if (obj == NULL) { if (PyErr_ExceptionMatches(PyExc_ValueError)) { /* Ignore encoding and simply return plain string. */ obj = PyBytes_FromStringAndSize(str, len); } else { assert(PyErr_ExceptionMatches(PyExc_LookupError)); /* Store error when this is the first. */ if (self->error.ptype == NULL) PyErr_Fetch(&(self->error.ptype), &(self->error.pvalue), &(self->error.ptraceback)); /* Return Py_None as placeholder to let the error bubble up and * be used when a full reply in Reader#gets(). */ obj = Py_None; Py_INCREF(obj); } PyErr_Clear(); } } assert(obj != NULL); return obj; } static void *createStringObject(const redisReadTask *task, char *str, size_t len) { hiredis_ReaderObject *self = (hiredis_ReaderObject*)task->privdata; PyObject *obj; if (task->type == REDIS_REPLY_ERROR) { PyObject *args = Py_BuildValue("(s#)", str, len); assert(args != NULL); /* TODO: properly handle OOM etc */ obj = PyObject_CallObject(self->replyErrorClass, args); assert(obj != NULL); Py_DECREF(args); } else { obj = createDecodedString(self, str, len); } return tryParentize(task, obj); } static void *createArrayObject(const redisReadTask *task, int elements) { PyObject *obj; obj = PyList_New(elements); return tryParentize(task, obj); } static void *createIntegerObject(const redisReadTask *task, long long value) { PyObject *obj; obj = PyLong_FromLongLong(value); return tryParentize(task, obj); } static void *createNilObject(const redisReadTask *task) { PyObject *obj = Py_None; Py_INCREF(obj); return tryParentize(task, obj); } static void freeObject(void *obj) { Py_XDECREF(obj); } redisReplyObjectFunctions hiredis_ObjectFunctions = { createStringObject, // void *(*createString)(const redisReadTask*, char*, size_t); createArrayObject, // void *(*createArray)(const redisReadTask*, int); createIntegerObject, // void *(*createInteger)(const redisReadTask*, long long); createNilObject, // void *(*createNil)(const redisReadTask*); freeObject // void (*freeObject)(void*); }; static void Reader_dealloc(hiredis_ReaderObject *self) { redisReplyReaderFree(self->reader); if (self->encoding) free(self->encoding); ((PyObject *)self)->ob_type->tp_free((PyObject*)self); } static int _Reader_set_exception(PyObject **target, PyObject *value) { int subclass; subclass = PyObject_IsSubclass(value, PyExc_Exception); if (subclass == -1) { return 0; } if (subclass == 0) { PyErr_SetString(PyExc_TypeError, "Expected subclass of Exception"); return 0; } Py_DECREF(*target); *target = value; Py_INCREF(*target); return 1; } static int Reader_init(hiredis_ReaderObject *self, PyObject *args, PyObject *kwds) { static char *kwlist[] = { "protocolError", "replyError", "encoding", NULL }; PyObject *protocolErrorClass = NULL; PyObject *replyErrorClass = NULL; PyObject *encodingObj = NULL; if (!PyArg_ParseTupleAndKeywords(args, kwds, "|OOO", kwlist, &protocolErrorClass, &replyErrorClass, &encodingObj)) return -1; if (protocolErrorClass) if (!_Reader_set_exception(&self->protocolErrorClass, protocolErrorClass)) return -1; if (replyErrorClass) if (!_Reader_set_exception(&self->replyErrorClass, replyErrorClass)) return -1; if (encodingObj) { PyObject *encbytes; char *encstr; int enclen; if (PyUnicode_Check(encodingObj)) encbytes = PyUnicode_AsASCIIString(encodingObj); else encbytes = PyObject_Bytes(encodingObj); if (encbytes == NULL) return -1; enclen = PyBytes_Size(encbytes); encstr = PyBytes_AsString(encbytes); self->encoding = (char*)malloc(enclen+1); memcpy(self->encoding, encstr, enclen); self->encoding[enclen] = '\0'; Py_DECREF(encbytes); } return 0; } static PyObject *Reader_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { hiredis_ReaderObject *self; self = (hiredis_ReaderObject*)type->tp_alloc(type, 0); if (self != NULL) { self->reader = redisReplyReaderCreate(); self->reader->fn = &hiredis_ObjectFunctions; self->reader->privdata = self; self->encoding = NULL; self->protocolErrorClass = HIREDIS_STATE->HiErr_ProtocolError; self->replyErrorClass = HIREDIS_STATE->HiErr_ReplyError; Py_INCREF(self->protocolErrorClass); Py_INCREF(self->replyErrorClass); self->error.ptype = NULL; self->error.pvalue = NULL; self->error.ptraceback = NULL; } return (PyObject*)self; } static PyObject *Reader_feed(hiredis_ReaderObject *self, PyObject *args) { const char *str; int len; if (!PyArg_ParseTuple(args, "s#", &str, &len)) return NULL; redisReplyReaderFeed(self->reader, str, len); Py_RETURN_NONE; } static PyObject *Reader_gets(hiredis_ReaderObject *self) { PyObject *obj; char *err; if (redisReplyReaderGetReply(self->reader, (void**)&obj) == REDIS_ERR) { err = redisReplyReaderGetError(self->reader); PyErr_SetString(self->protocolErrorClass, err); return NULL; } if (obj == NULL) { Py_RETURN_FALSE; } else { /* Restore error when there is one. */ if (self->error.ptype != NULL) { Py_DECREF(obj); PyErr_Restore(self->error.ptype, self->error.pvalue, self->error.ptraceback); self->error.ptype = NULL; self->error.pvalue = NULL; self->error.ptraceback = NULL; return NULL; } return obj; } } hiredis-py-0.1.1/src/reader.h000066400000000000000000000012111174333615400157720ustar00rootroot00000000000000#ifndef __READER_H #define __READER_H #include "hiredis.h" typedef struct { PyObject_HEAD redisReader *reader; char *encoding; PyObject *protocolErrorClass; PyObject *replyErrorClass; /* Stores error object in between incomplete calls to #gets, in order to * only set the error once a full reply has been read. Otherwise, the * reader could get in an inconsistent state. */ struct { PyObject *ptype; PyObject *pvalue; PyObject *ptraceback; } error; } hiredis_ReaderObject; extern PyTypeObject hiredis_ReaderType; extern redisReplyObjectFunctions hiredis_ObjectFunctions; #endif hiredis-py-0.1.1/test.py000077500000000000000000000001531174333615400151300ustar00rootroot00000000000000#!/usr/bin/env python from unittest import TextTestRunner import test TextTestRunner().run(test.tests()) hiredis-py-0.1.1/test/000077500000000000000000000000001174333615400145545ustar00rootroot00000000000000hiredis-py-0.1.1/test/__init__.py000066400000000000000000000005641174333615400166720ustar00rootroot00000000000000import glob, os.path, sys version = sys.version.split(" ")[0] majorminor = version[0:3] # Add path to hiredis.so load path path = glob.glob("build/lib*-%s/hiredis/*.so" % majorminor)[0] sys.path.insert(0, os.path.dirname(path)) from unittest import * from . import reader def tests(): suite = TestSuite() suite.addTest(makeSuite(reader.ReaderTest)) return suite hiredis-py-0.1.1/test/reader.py000066400000000000000000000100041174333615400163630ustar00rootroot00000000000000# coding=utf-8 from unittest import * import hiredis class ReaderTest(TestCase): def setUp(self): self.reader = hiredis.Reader() def reply(self): return self.reader.gets() def test_nothing(self): self.assertEquals(False, self.reply()) def test_error_when_feeding_non_string(self): self.assertRaises(TypeError, self.reader.feed, 1) def test_protocol_error(self): self.reader.feed(b"x") self.assertRaises(hiredis.ProtocolError, self.reply) def test_protocol_error_with_custom_class(self): self.reader = hiredis.Reader(protocolError=RuntimeError) self.reader.feed(b"x") self.assertRaises(RuntimeError, self.reply) def test_fail_with_wrong_protocol_error_class(self): self.assertRaises(TypeError, hiredis.Reader, protocolError="wrong") def test_error_string(self): self.reader.feed(b"-error\r\n") error = self.reply() self.assertEquals(hiredis.ReplyError, type(error)) self.assertEquals(("error",), error.args) def test_error_string_with_custom_class(self): self.reader = hiredis.Reader(replyError=RuntimeError) self.reader.feed(b"-error\r\n") error = self.reply() self.assertEquals(RuntimeError, type(error)) self.assertEquals(("error",), error.args) def test_fail_with_wrong_reply_error_class(self): self.assertRaises(TypeError, hiredis.Reader, replyError="wrong") def test_errors_in_nested_multi_bulk(self): self.reader.feed(b"*2\r\n-err0\r\n-err1\r\n") for r, error in zip(("err0", "err1"), self.reply()): self.assertEquals(hiredis.ReplyError, type(error)) self.assertEquals((r,), error.args) def test_integer(self): value = 2**63-1 # Largest 64-bit signed integer self.reader.feed((":%d\r\n" % value).encode("ascii")) self.assertEquals(value, self.reply()) def test_status_string(self): self.reader.feed(b"+ok\r\n") self.assertEquals(b"ok", self.reply()) def test_empty_bulk_string(self): self.reader.feed(b"$0\r\n\r\n") self.assertEquals(b"", self.reply()) def test_bulk_string(self): self.reader.feed(b"$5\r\nhello\r\n") self.assertEquals(b"hello", self.reply()) def test_bulk_string_without_encoding(self): snowman = b"\xe2\x98\x83" self.reader.feed(b"$3\r\n" + snowman + b"\r\n") self.assertEquals(snowman, self.reply()) def test_bulk_string_with_encoding(self): snowman = b"\xe2\x98\x83" self.reader = hiredis.Reader(encoding="utf-8") self.reader.feed(b"$3\r\n" + snowman + b"\r\n") self.assertEquals(snowman.decode("utf-8"), self.reply()) def test_bulk_string_with_other_encoding(self): snowman = b"\xe2\x98\x83" self.reader = hiredis.Reader(encoding="utf-32") self.reader.feed(b"$3\r\n" + snowman + b"\r\n") self.assertEquals(snowman, self.reply()) def test_bulk_string_with_invalid_encoding(self): self.reader = hiredis.Reader(encoding="unknown") self.reader.feed(b"$5\r\nhello\r\n") self.assertRaises(LookupError, self.reply) def test_null_multi_bulk(self): self.reader.feed(b"*-1\r\n") self.assertEquals(None, self.reply()) def test_empty_multi_bulk(self): self.reader.feed(b"*0\r\n") self.assertEquals([], self.reply()) def test_multi_bulk(self): self.reader.feed(b"*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n") self.assertEquals([b"hello", b"world"], self.reply()) def test_multi_bulk_with_invalid_encoding_and_partial_reply(self): self.reader = hiredis.Reader(encoding="unknown") self.reader.feed(b"*2\r\n$5\r\nhello\r\n") self.assertEquals(False, self.reply()) self.reader.feed(b":1\r\n") self.assertRaises(LookupError, self.reply) def test_nested_multi_bulk(self): self.reader.feed(b"*2\r\n*2\r\n$5\r\nhello\r\n$5\r\nworld\r\n$1\r\n!\r\n") self.assertEquals([[b"hello", b"world"], b"!"], self.reply()) def test_subclassable(self): class TestReader(hiredis.Reader): def __init__(self, *args, **kwargs): super(TestReader, self).__init__(*args, **kwargs) reader = TestReader() reader.feed(b"+ok\r\n") self.assertEquals(b"ok", reader.gets()) hiredis-py-0.1.1/vendor/000077500000000000000000000000001174333615400150725ustar00rootroot00000000000000hiredis-py-0.1.1/vendor/hiredis/000077500000000000000000000000001174333615400165215ustar00rootroot00000000000000