diff --git a/.gitignore b/.gitignore index 63672ee8..4d7fa457 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,3 @@ MANIFEST /env/ # Used in demo apps secrets.cfg - -# cython-generated -tornado/speedups.c diff --git a/MANIFEST.in b/MANIFEST.in index 206ea270..1811a9cb 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,5 +1,5 @@ recursive-include demos *.py *.yaml *.html *.css *.js *.xml *.sql README -include tornado/speedups.pyx +include tornado/speedups.c include tornado/ca-certificates.crt include tornado/test/README include tornado/test/csv_translations/fr_FR.csv diff --git a/setup.py b/setup.py index 5d603227..6fba3988 100644 --- a/setup.py +++ b/setup.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import platform import sys try: @@ -24,10 +25,7 @@ except ImportError: setuptools = None from distutils.core import setup -try: - from Cython.Build import cythonize -except ImportError: - cythonize = None +from distutils.core import Extension kwargs = {} @@ -36,8 +34,13 @@ version = "3.2.dev2" with open('README.rst') as f: kwargs['long_description'] = f.read() -if cythonize is not None: - kwargs['ext_modules'] = cythonize('tornado/speedups.pyx') +if platform.python_implementation() == 'CPython': + # This extension builds and works on pypy as well, although pypy's jit + # produces equivalent performance. + kwargs['ext_modules'] = [ + Extension('tornado.speedups', + sources=['tornado/speedups.c']), + ] if setuptools is not None: # If setuptools is not available, you're on your own for dependencies. diff --git a/tornado/speedups.c b/tornado/speedups.c new file mode 100644 index 00000000..163023cd --- /dev/null +++ b/tornado/speedups.c @@ -0,0 +1,48 @@ +#include + +static PyObject* websocket_mask(PyObject* self, PyObject* args) { + const char* mask; + int mask_len; + const char* data; + int data_len; + + if (!PyArg_ParseTuple(args, "s#s#", &mask, &mask_len, &data, &data_len)) { + return NULL; + } + + PyObject* result = PyBytes_FromStringAndSize(NULL, data_len); + if (!result) { + return NULL; + } + char* buf = PyBytes_AsString(result); + for (int i = 0; i < data_len; i++) { + buf[i] = data[i] ^ mask[i % 4]; + } + + return result; +} + +static PyMethodDef methods[] = { + {"websocket_mask", websocket_mask, METH_VARARGS, ""}, + {NULL, NULL, 0, NULL} +}; + +#if PY_MAJOR_VERSION >= 3 +static struct PyModuleDef speedupsmodule = { + PyModuleDef_HEAD_INIT, + "speedups", + NULL, + -1, + methods +}; + +PyMODINIT_FUNC +PyInit_speedups() { + return PyModule_Create(&speedupsmodule); +} +#else // Python 2.x +PyMODINIT_FUNC +initspeedups() { + Py_InitModule("tornado.speedups", methods); +} +#endif diff --git a/tornado/speedups.pyx b/tornado/speedups.pyx deleted file mode 100644 index 46344453..00000000 --- a/tornado/speedups.pyx +++ /dev/null @@ -1,16 +0,0 @@ -# -*- python -*- -from cpython.mem cimport PyMem_Malloc, PyMem_Free - -def websocket_mask(bytes mask_bytes, bytes data_bytes): - cdef size_t data_len = len(data_bytes) - cdef char* data = data_bytes - cdef char* mask = mask_bytes - cdef size_t i - cdef char* buf = PyMem_Malloc(data_len) - try: - for i in xrange(data_len): - buf[i] = data[i] ^ mask[i % 4] - # Is there a zero-copy equivalent of this? - return (buf[:data_len]) - finally: - PyMem_Free(buf) diff --git a/tornado/websocket.py b/tornado/websocket.py index e3942424..3a83994c 100644 --- a/tornado/websocket.py +++ b/tornado/websocket.py @@ -908,7 +908,12 @@ def _websocket_mask_python(mask, data): else: return unmasked.tostring() -try: - from tornado.speedups import websocket_mask as _websocket_mask -except ImportError: +if os.environ.get('TORNADO_NO_EXTENSION'): + # This environment variable exists to make it easier to do performance comparisons; + # it's not guaranteed to remain supported in the future. _websocket_mask = _websocket_mask_python +else: + try: + from tornado.speedups import websocket_mask as _websocket_mask + except ImportError: + _websocket_mask = _websocket_mask_python diff --git a/tox.ini b/tox.ini index 2d9afaff..77918ef0 100644 --- a/tox.ini +++ b/tox.ini @@ -30,7 +30,6 @@ deps = unittest2 [testenv:py26-full] basepython = python2.6 deps = - Cython futures mock pycurl @@ -40,7 +39,6 @@ deps = [testenv:py27-full] basepython = python2.7 deps = - Cython futures mock pycurl @@ -150,7 +148,6 @@ commands = python -m tornado.test.runtests --locale=zh_TW {posargs:} # there. basepython = pypy deps = - Cython futures mock @@ -171,7 +168,6 @@ setenv = LANG=en_US.utf-8 [testenv:py32-full] basepython = python3.2 deps = - Cython mock [testenv:py33]