Replace cython-based extension module with a hand-written one.

This avoids the complexity of ensuring that cython is installed
at build time.
This commit is contained in:
Ben Darnell 2013-11-05 17:39:03 -05:00
parent 4a5fdb1e83
commit 0ca7fa01d1
7 changed files with 66 additions and 33 deletions

3
.gitignore vendored
View File

@ -14,6 +14,3 @@ MANIFEST
/env/
# Used in demo apps
secrets.cfg
# cython-generated
tornado/speedups.c

View File

@ -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

View File

@ -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.

48
tornado/speedups.c Normal file
View File

@ -0,0 +1,48 @@
#include <Python.h>
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

View File

@ -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 = <char*> 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 <bytes>(buf[:data_len])
finally:
PyMem_Free(buf)

View File

@ -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

View File

@ -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]