From 65d25a0fc224f0694ebfd1434524eed1efb45ae8 Mon Sep 17 00:00:00 2001 From: C Anthony Risinger Date: Wed, 28 May 2014 12:51:58 -0500 Subject: [PATCH 1/3] add build products to .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index d021ce9b..df2b62e4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,7 @@ /t/ring/target core/dot_h.c + +/build/ +/dist/ +/uWSGI.egg-info/ From c5e1ead0658334388a41b5ce685333334d8ec55b Mon Sep 17 00:00:00 2001 From: C Anthony Risinger Date: Wed, 28 May 2014 12:52:41 -0500 Subject: [PATCH 2/3] add alternate setup.py impl --- setup.cpyext.py | 131 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 setup.cpyext.py diff --git a/setup.cpyext.py b/setup.cpyext.py new file mode 100644 index 00000000..ee89dbc5 --- /dev/null +++ b/setup.cpyext.py @@ -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], + }, + ) From 5a17557954fd622038a49340a5353ee1da791f18 Mon Sep 17 00:00:00 2001 From: C Anthony Risinger Date: Tue, 27 May 2014 05:51:13 -0500 Subject: [PATCH 3/3] Support alternate build as proper CPython extension, and/or by distutils uWSGI will instruct setuptools to generate a console_scripts entry_point as bin/uwsgi, and will in general behave more like a normal extension. Also exports uwsgi.ORIG_ARGV and uwsgi.NEW_ARGV... ex. usage: # python -i -c \ 'import uwsgi as u; u.setup().worker_id()==1 or u.run()' \ --master --workers=1 --enable-threads --honour-stdin \ --auto-procname --http :8000 Relies on new_argc/new_argv support in 2.1 --- plugins/pyuwsgi/pyuwsgi.c | 332 +++++++++++++++++++++++++++++++++----- 1 file changed, 291 insertions(+), 41 deletions(-) diff --git a/plugins/pyuwsgi/pyuwsgi.c b/plugins/pyuwsgi/pyuwsgi.c index 02a03329..2f248f4a 100644 --- a/plugins/pyuwsgi/pyuwsgi.c +++ b/plugins/pyuwsgi/pyuwsgi.c @@ -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>> 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>>> " + "\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, };