Merge pull request #639 from xtfxme/as-cpyext-intellisurvey

Support building as a proper [C]Python extension, and/or by distutils [intellisurvey]
This commit is contained in:
unbit
2014-05-29 07:55:06 +02:00
3 changed files with 426 additions and 41 deletions
+4
View File
@@ -12,3 +12,7 @@
/t/ring/target
core/dot_h.c
/build/
/dist/
/uWSGI.egg-info/
+291 -41
View File
@@ -1,75 +1,325 @@
#include "../python/uwsgi_python.h"
//FIXME: [upstream:python] needs PyAPI_FUNC(void)
extern void Py_GetArgcArgv(int *, char ***);
extern struct uwsgi_server uwsgi;
extern struct uwsgi_python up;
extern char **environ;
PyObject *u_run(PyObject *self, PyObject *args) {
static int new_argc = -1;
static int orig_argc = -1;
static char **new_argv = NULL;
static char **orig_argv = NULL;
static char *new_argv_buf = NULL;
char **argv;
size_t size = 2;
int i;
if (PyTuple_Size(args) < 1) {
return PyErr_Format(PyExc_ValueError, "you have to specify at least one uWSGI option to run() it");
}
PyObject *
pyuwsgi_setup(PyObject *self, PyObject *args, PyObject *kwds)
{
if (new_argv) {
PyErr_SetString(
PyExc_RuntimeError,
"uWSGI already setup"
);
return NULL;
}
PyObject *the_arg = PyTuple_GetItem(args, 0);
if (uwsgi.mywid) {
PyErr_SetString(
PyExc_RuntimeError,
"uWSGI must be setup by master"
);
return NULL;
}
if (PyList_Check(the_arg)) {
size = PyList_Size(the_arg) + 2;
}
else if (PyTuple_Check(the_arg)) {
size = PyTuple_Size(the_arg) + 2;
}
else if (PyString_Check(the_arg)) {
size = 3;
}
PyObject *iterator;
argv = uwsgi_malloc(sizeof(char *) * size);
memset(argv, 0, sizeof(char *) * size);
if (args == NULL || PyObject_Size(args) == 0) {
PyObject *argv = PySys_GetObject("argv");
if (argv == NULL)
return NULL;
// will be overwritten
argv[0] = "uwsgi";
// during site.py maybe
if (argv == Py_None) {
argv = PyTuple_New(0);
iterator = PyObject_GetIter(argv);
Py_DECREF(argv);
}
else {
iterator = PyObject_GetIter(argv);
if (PyObject_Size(argv) > 0) {
// forward past argv0
PyObject *item = PyIter_Next(iterator);
Py_DECREF(item);
}
}
}
else if (
PyObject_Size(args) == 1
&& !PyString_Check(PyTuple_GetItem(args, 0))
) {
iterator = PyObject_GetIter(PyTuple_GetItem(args, 0));
}
else {
iterator = PyObject_GetIter(args);
}
if (PyList_Check(the_arg)) {
for(i=0;i<PyList_Size(the_arg);i++) {
argv[i+1] = PyString_AsString( PyList_GetItem(the_arg, i) );
}
}
else if (PyTuple_Check(the_arg)) {
for(i=0;i<PyTuple_Size(the_arg);i++) {
argv[i+1] = PyString_AsString( PyTuple_GetItem(the_arg, i) );
}
}
else if (PyString_Check(the_arg)) {
argv[1] = PyString_AsString( the_arg );
}
if (iterator == NULL) {
return NULL;
}
uwsgi_init(size-1, argv, environ);
size_t size = 1;
//FIXME: ARGS prior to and including -c/-m are REQUIRED!
PyObject *item = PyString_FromString(orig_argv[0]);
PyObject *args_li = PyList_New(0);
PyList_Append(args_li, item);
size += strlen(orig_argv[0]) + 1;
Py_DECREF(item);
Py_INCREF(Py_None);
return Py_None;
while ((item = PyIter_Next(iterator))) {
//TODO: call str(...) on everything
PyList_Append(args_li, item);
size += PyObject_Length(item) + 1;
Py_DECREF(item);
}
Py_DECREF(iterator);
new_argc = PyObject_Length(args_li);
new_argv = uwsgi_calloc(sizeof(char *) * (new_argc + 1));
new_argv_buf = uwsgi_calloc(size);
int i = 0;
char *new_argv_ptr = new_argv_buf;
for(i=0; i < new_argc; i++) {
PyObject *arg = PyList_GetItem(args_li, i);
char *arg_str = PyString_AsString(arg);
new_argv[i] = new_argv_ptr;
strcpy(new_argv_ptr, arg_str);
new_argv_ptr += strlen(arg_str) + 1;
}
PyObject *args_tup = PyList_AsTuple(args_li);
PyObject_SetAttrString(self, "NEW_ARGV", args_tup);
Py_DECREF(args_tup);
Py_DECREF(args_li);
// TODO: convention here is a goto methinks?
if (PyErr_Occurred()) {
free(new_argv_buf);
free(new_argv);
new_argv = 0;
new_argc = 0;
return NULL;
}
//TODO: ...???
// actually do the thing!
PyThreadState *_tstate = PyThreadState_Get();
uwsgi_setup(orig_argc, orig_argv, environ);
PyThreadState_Swap(_tstate);
Py_INCREF(self);
return self;
}
PyObject *
pyuwsgi_init(PyObject *self, PyObject *args, PyObject *kwds)
{
if (pyuwsgi_setup(self, args, kwds) == NULL) {
return NULL;
}
int rc = uwsgi_run();
// never(?) here
return Py_BuildValue("i", rc);
}
PyObject *
pyuwsgi_run(PyObject *self, PyObject *args, PyObject *kwds)
{
// backcompat
if (new_argv == NULL &&
pyuwsgi_setup(self, args, kwds) == NULL) {
return NULL;
}
int rc = uwsgi_run();
// never(?) here
return Py_BuildValue("i", rc);
}
PyMethodDef methods[] = {
{"run", u_run, METH_VARARGS, "run the uWSGI server"},
{"run",
(PyCFunction) pyuwsgi_run,
METH_VARARGS | METH_KEYWORDS,
"run(...)"
"\n>>> 0"
"\n"
"\n * Call setup(...) if not configured"
"\n * Begin uWSGI mainloop"
"\n NOTE: will not return"
"\n"
},
{"init",
(PyCFunction) pyuwsgi_init,
METH_VARARGS | METH_KEYWORDS,
"init(...)"
"\n>>> 0"
"\n"
"\n * Call setup(...)"
"\n * Begin uWSGI mainloop"
"\n NOTE: will not return"
"\n"
},
{"setup",
(PyCFunction) pyuwsgi_setup,
METH_VARARGS | METH_KEYWORDS,
"setup('--master', ...)"
"\n>>> <module 'uwsgi' from \"uwsgi.so\">"
"\n"
"\n * Initialize uWSGI core with (...)"
"\n MUST only call once [RuntimeException]"
"\n MUST only call from master [RuntimeException]"
"\n"
},
{NULL, NULL, 0, NULL}
};
static void
pyuwsgi_set_orig_argv(PyObject *self)
{
// ask python for the original argc/argv saved in Py_Main()
Py_GetArgcArgv(&orig_argc, &orig_argv);
// [re?]export to uwsgi.orig_argv
PyObject *m_orig_argv;
m_orig_argv = PyTuple_New(orig_argc);
int i = 0;
int i_cm = -1;
for(i=0; i < orig_argc; i++) {
char *arg = orig_argv[i];
//XXX: _PyOS_optarg != 0 also indicates python quit early...
//FIXME: [upstream:python] orig_argv could be mangled; reset
// rel: http://bugs.python.org/issue8202
orig_argv[i + 1] = arg + strlen(arg) + 1;
// look for -c or -m and record the offset
if (i_cm < 0) {
if (strcmp(arg, "-c") || strcmp(arg, "-m")) {
// python's getopt would've failed had + 1 not exist
i_cm = i + 1;
}
else if (!uwsgi_startswith(arg, "-c", 2) ||
!uwsgi_startswith(arg, "-m", 2)) {
//FIXME: ARGS prior to and including -c/-m are REQUIRED,
// but NOT a part of the uWSGI argv! Needed to make
// exec*() self-referential: exec*(...) -> uwsgi
//
// want: uwsgi.binary_argv[:] + uwsgi.argv[:]!
// binary_argv = [binary_path] + args
i_cm = i;
}
}
PyTuple_SetItem(m_orig_argv, i, PyString_FromString(arg));
}
//TODO: howto properly detect uwsgi already running...
// orig_argv == uwsgi.orig_argv (?)
// ^^^ but if Py_Main not called, python/main.c:orig_argv unset
// howto interact/detect things in general
PyObject *m_new_argv = PyTuple_New(0);
PyObject_SetAttrString(self, "NEW_ARGV", m_new_argv);
PyObject_SetAttrString(self, "ORIG_ARGV", m_orig_argv);
Py_DECREF(m_new_argv);
Py_DECREF(m_orig_argv);
}
static PyObject *
pyuwsgi_init_as(char *mod_name)
{
PyObject *m;
m = PyImport_GetModuleDict();
if (m == NULL) {
return NULL;
}
m = PyDict_GetItemString(m, mod_name);
if (!m) {
m = Py_InitModule(mod_name, NULL);
}
if (orig_argc < 0) {
pyuwsgi_set_orig_argv(m);
}
int i;
for (i=0; methods[i].ml_name != NULL; i++) {
PyObject *fun = PyObject_GetAttrString(m, methods[i].ml_name);
if (fun != NULL) {
// already exists
Py_DECREF(fun);
continue;
}
PyErr_Clear();
// rel: Python/modsupport.c:Py_InitModule4
PyObject* name = PyString_FromString(methods[i].ml_name);
// fun(self, ...)
fun = PyCFunction_NewEx(&methods[i], m, name);
Py_DECREF(name);
// module.fun
PyObject_SetAttrString(m, methods[i].ml_name, fun);
Py_DECREF(fun);
}
return m;
}
PyMODINIT_FUNC
initpyuwsgi()
{
(void) Py_InitModule("pyuwsgi", methods);
(void) pyuwsgi_init_as("pyuwsgi");
}
int pyuwsgi_init() { return 0; }
// allow the module to be called `uwsgi`
PyMODINIT_FUNC
inituwsgi()
{
(void) pyuwsgi_init_as("uwsgi");
}
void pyuwsgi_load()
{
if (new_argc > -1) {
uwsgi.new_argc = new_argc;
uwsgi.new_argv = new_argv;
}
}
struct uwsgi_plugin pyuwsgi_plugin = {
.name = "pyuwsgi",
.init = pyuwsgi_init,
.on_load = pyuwsgi_load,
};
+131
View File
@@ -0,0 +1,131 @@
# encoding: utf-8
"""
This is a hack allowing you installing
uWSGI and uwsgidecorators via pip and easy_install
since 1.9.11 it automatically detects pypy
"""
import os
import sys
import errno
import shlex
import shutil
import uwsgiconfig
from setuptools import setup
from setuptools.dist import Distribution
from setuptools.command.install import install
from setuptools.command.install_lib import install_lib
from setuptools.command.build_ext import build_ext
from distutils.core import Extension
class uWSGIBuildExt(build_ext):
UWSGI_NAME = 'uwsgi'
UWSGI_PLUGIN = 'pyuwsgi'
def build_extensions(self):
self.uwsgi_setup()
#XXX: needs uwsgiconfig fix
self.uwsgi_build()
if 'UWSGI_USE_DISTUTILS' not in os.environ:
#XXX: needs uwsgiconfig fix
#uwsgiconfig.build_uwsgi(self.uwsgi_config)
return
else:
#XXX: needs uwsgiconfig fix
os.unlink(self.uwsgi_config.get('bin_name'))
#FIXME: else build fails :(
for baddie in set(self.compiler.compiler_so) & set((
'-Wstrict-prototypes',
)):
self.compiler.compiler_so.remove(baddie)
build_ext.build_extensions(self)
def uwsgi_setup(self):
default = (
'__pypy__' in sys.builtin_module_names
and 'pypy'
or 'default'
)
profile = (
os.environ.get('UWSGI_PROFILE')
or 'buildconf/%s.ini' % default
)
if not profile.endswith('.ini'):
profile = profile + '.ini'
if not '/' in profile:
profile = 'buildconf/' + profile
#FIXME: update uwsgiconfig to properly set _EVERYTHING_!
config = uwsgiconfig.uConf(profile)
# insert in the beginning so UWSGI_PYTHON_NOLIB is exported
# before the python plugin compiles
ep = config.get('embedded_plugins').split(',')
if self.UWSGI_PLUGIN in ep:
ep.remove(self.UWSGI_PLUGIN)
ep.insert(0, self.UWSGI_PLUGIN)
config.set('embedded_plugins', ','.join(ep))
config.set('as_shared_library', 'true')
config.set('bin_name', self.get_ext_fullpath(self.UWSGI_NAME))
try:
os.makedirs(os.path.dirname(config.get('bin_name')))
except OSError as e:
if e.errno != errno.EEXIST:
raise
self.uwsgi_profile = profile
self.uwsgi_config = config
def uwsgi_build(self):
uwsgiconfig.build_uwsgi(self.uwsgi_config)
#XXX: merge uwsgi_setup (see other comments)
for ext in self.extensions:
if ext.name == self.UWSGI_NAME:
ext.sources = [s + '.c' for s in self.uwsgi_config.gcc_list]
ext.library_dirs = self.uwsgi_config.include_path[:]
ext.libraries = list()
ext.extra_compile_args = list()
for x in uwsgiconfig.uniq_warnings(
self.uwsgi_config.ldflags + self.uwsgi_config.libs,
):
for y in shlex.split(x):
if y.startswith('-l'):
ext.libraries.append(y[2:])
elif y.startswith('-L'):
ext.library_dirs.append(y[2:])
for x in self.uwsgi_config.cflags:
for y in shlex.split(x):
if y:
ext.extra_compile_args.append(y)
setup(
name='uWSGI',
license='GPL2',
version=uwsgiconfig.uwsgi_version,
author='Unbit',
author_email='info@unbit.it',
description='The uWSGI server',
cmdclass={
'build_ext': uWSGIBuildExt,
},
py_modules=[
'uwsgidecorators',
],
ext_modules=[
Extension(uWSGIBuildExt.UWSGI_NAME, sources=[]),
],
entry_points={
'console_scripts': ['uwsgi=%s:run' % uWSGIBuildExt.UWSGI_NAME],
},
)