Files
uwsgi/plugins/python/python_plugin.c
Unbit d4130dbc4b first round of protocol parsers optimizations
preliminary api for language-independent body read

another step

completed body read language independent implementation

ported gevent to the new read/write api

ported websockets to the new read/write api

removed channels subsystem

removed channels subsystem

ported lua to the new read/write api

fixed post-buffering

readline is still broken

improved request body readline

very difficult test for readline()/read() combo passed

other improvements in postbuffering/read/readline

ported --http-socket to the new api

added X-Forwarded-SSL management

removed old api

more refactoring

ported the RACK plugin to the new api

ported psgi plugin to the new api

defintely removed clustering

simpified ifdel hell

simpified ifdef hell

removed useless configuration options
2013-02-03 10:38:13 +01:00

1880 lines
50 KiB
C

#include "uwsgi_python.h"
extern struct uwsgi_server uwsgi;
struct uwsgi_python up;
#include <glob.h>
extern PyTypeObject uwsgi_InputType;
void uwsgi_opt_pythonpath(char *opt, char *value, void *foobar) {
int i;
glob_t g;
if (glob(value, GLOB_MARK, NULL, &g)) {
uwsgi_string_new_list(&up.python_path, value);
}
else {
for (i = 0; i < (int) g.gl_pathc; i++) {
uwsgi_string_new_list(&up.python_path, g.gl_pathv[i]);
}
}
}
void uwsgi_opt_pyshell(char *opt, char *value, void *foobar) {
uwsgi.honour_stdin = 1;
up.pyshell = 1;
if (!strcmp("pyshell-oneshot", opt)) {
up.pyshell_oneshot = 1;
}
}
void uwsgi_opt_pyrun(char *opt, char *value, void *foobar) {
uwsgi.honour_stdin = 1;
uwsgi.command_mode = 1;
up.pyrun = value;
}
void uwsgi_opt_pyver(char *opt, char *foo, void *bar) {
#ifndef UWSGI_PYPY
const char *version = Py_GetVersion();
#else
const char *version = PYPY_VERSION;
#endif
const char *space = strchr(version, ' ');
if (space) {
fprintf(stdout, "%.*s\n", (int) (space-version), version);
}
else {
fprintf(stdout, "%s\n", version);
}
exit(0);
}
#ifdef UWSGI_INI
void uwsgi_opt_ini_paste(char *opt, char *value, void *foobar) {
uwsgi_opt_load_ini(opt, value, NULL);
if (value[0] != '/') {
up.paste = uwsgi_concat4("config:", uwsgi.cwd, "/", value);
}
else {
up.paste = uwsgi_concat2("config:", value);
}
if (!strcmp("ini-paste-logged", opt)) {
up.paste_logger = 1;
}
}
#endif
struct uwsgi_option uwsgi_python_options[] = {
{"wsgi-file", required_argument, 0, "load .wsgi file", uwsgi_opt_set_str, &up.file_config, 0},
{"file", required_argument, 0, "load .wsgi file", uwsgi_opt_set_str, &up.file_config, 0},
{"eval", required_argument, 0, "eval python code", uwsgi_opt_set_str, &up.eval, 0},
{"module", required_argument,'w', "load a WSGI module", uwsgi_opt_set_str, &up.wsgi_config, 0},
{"wsgi", required_argument, 'w', "load a WSGI module", uwsgi_opt_set_str, &up.wsgi_config, 0},
{"callable", required_argument, 0, "set default WSGI callable name", uwsgi_opt_set_str, &up.callable, 0},
{"test", required_argument, 'J', "test a mdule import", uwsgi_opt_set_str, &up.test_module, 0},
{"home", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0},
{"virtualenv", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0},
{"venv", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0},
{"pyhome", required_argument, 'H', "set PYTHONHOME/virtualenv", uwsgi_opt_set_str, &up.home, 0},
{"py-programname", required_argument, 0, "set python program name", uwsgi_opt_set_str, &up.programname, 0},
{"py-program-name", required_argument, 0, "set python program name", uwsgi_opt_set_str, &up.programname, 0},
{"pythonpath", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0},
{"python-path", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0},
{"pp", required_argument, 0, "add directory (or glob) to pythonpath", uwsgi_opt_pythonpath, NULL, 0},
{"pymodule-alias", required_argument, 0, "add a python alias module", uwsgi_opt_add_string_list, &up.pymodule_alias, 0},
{"post-pymodule-alias", required_argument, 0, "add a python module alias after uwsgi module initialization", uwsgi_opt_add_string_list, &up.post_pymodule_alias, 0},
{"import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0},
{"pyimport", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0},
{"py-import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0},
{"python-import", required_argument, 0, "import a python module", uwsgi_opt_add_string_list, &up.import_list, 0},
{"shared-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0},
{"shared-pyimport", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0},
{"shared-py-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0},
{"shared-python-import", required_argument, 0, "import a python module in all of the processes", uwsgi_opt_add_string_list, &up.shared_import_list, 0},
{"spooler-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list},
{"spooler-pyimport", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list},
{"spooler-py-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list},
{"spooler-python-import", required_argument, 0, "import a python module in the spooler", uwsgi_opt_add_string_list, &up.spooler_import_list},
{"pyargv", required_argument, 0, "manually set sys.argv", uwsgi_opt_set_str, &up.argv, 0},
{"optimize", required_argument, 'O', "set python optimization level", uwsgi_opt_set_int, &up.optimize, 0},
{"paste", required_argument, 0, "load a paste.deploy config file", uwsgi_opt_set_str, &up.paste, 0},
{"paste-logger", no_argument, 0, "enable paste fileConfig logger", uwsgi_opt_true, &up.paste_logger, 0},
{"web3", required_argument, 0, "load a web3 app", uwsgi_opt_set_str, &up.web3, 0},
{"pump", required_argument, 0, "load a pump app", uwsgi_opt_set_str, &up.pump, 0},
{"wsgi-lite", required_argument, 0, "load a wsgi-lite app", uwsgi_opt_set_str, &up.wsgi_lite, 0},
#ifdef UWSGI_INI
{"ini-paste", required_argument, 0, "load a paste.deploy config file containing uwsgi section", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE},
{"ini-paste-logged", required_argument, 0, "load a paste.deploy config file containing uwsgi section (load loggers too)", uwsgi_opt_ini_paste, NULL, UWSGI_OPT_IMMEDIATE},
#endif
{"reload-os-env", no_argument, 0, "force reload of os.environ at each request", uwsgi_opt_true, &up.reload_os_env, 0},
#ifndef UWSGI_PYPY
{"no-site", no_argument, 0, "do not import site module", uwsgi_opt_true, &Py_NoSiteFlag, 0},
#endif
{"pyshell", no_argument, 0, "run an interactive python shell in the uWSGI environment", uwsgi_opt_pyshell, NULL, 0},
{"pyshell-oneshot", no_argument, 0, "run an interactive python shell in the uWSGI environment (one-shot variant)", uwsgi_opt_pyshell, NULL, 0},
{"python", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0},
{"py", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0},
{"pyrun", required_argument, 0, "run a python script in the uWSGI environment", uwsgi_opt_pyrun, NULL, 0},
#ifndef UWSGI_PYPY
{"py-tracebacker", required_argument, 0, "enable the uWSGI python tracebacker", uwsgi_opt_set_str, &up.tracebacker, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
#endif
{"py-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
{"py-autoreload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
{"python-auto-reload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
{"python-autoreload", required_argument, 0, "monitor python modules mtime to trigger reload (use only in development)", uwsgi_opt_set_int, &up.auto_reload, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
{"py-auto-reload-ignore", required_argument, 0, "ignore the specified module during auto-reload scan (can be specified multiple times)", uwsgi_opt_add_string_list, &up.auto_reload_ignore, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
{"wsgi-env-behaviour", required_argument, 0, "set the strategy for allocating/deallocating the WSGI env", uwsgi_opt_set_str, &up.wsgi_env_behaviour, 0},
{"wsgi-env-behavior", required_argument, 0, "set the strategy for allocating/deallocating the WSGI env", uwsgi_opt_set_str, &up.wsgi_env_behaviour, 0},
{"start_response-nodelay", no_argument, 0, "send WSGI http headers as soon as possible (PEP violation)", uwsgi_opt_true, &up.start_response_nodelay, 0},
{"python-version", no_argument, 0, "report python version", uwsgi_opt_pyver, NULL, UWSGI_OPT_IMMEDIATE},
{0, 0, 0, 0, 0, 0, 0},
};
/* this routine will be called after each fork to reinitialize the various locks */
void uwsgi_python_pthread_prepare(void) {
pthread_mutex_lock(&up.lock_pyloaders);
}
void uwsgi_python_pthread_parent(void) {
pthread_mutex_unlock(&up.lock_pyloaders);
}
void uwsgi_python_pthread_child(void) {
pthread_mutex_init(&up.lock_pyloaders, NULL);
}
PyMethodDef uwsgi_spit_method[] = { {"uwsgi_spit", py_uwsgi_spit, METH_VARARGS, ""} };
PyMethodDef uwsgi_write_method[] = { {"uwsgi_write", py_uwsgi_write, METH_VARARGS, ""} };
int uwsgi_python_init() {
#ifndef UWSGI_PYPY
char *pyversion = strchr(Py_GetVersion(), '\n');
if (!pyversion) {
uwsgi_log_initial("Python version: %s\n", Py_GetVersion());
}
else {
uwsgi_log_initial("Python version: %.*s %s\n", pyversion-Py_GetVersion(), Py_GetVersion(), Py_GetCompiler()+1);
}
#else
uwsgi_log_initial("PyPy version: %s\n", PYPY_VERSION);
#endif
if (up.home != NULL) {
#ifdef PYTHREE
// check for PEP 405 virtualenv (starting from python 3.3)
char *pep405_env = uwsgi_concat2(up.home, "/pyvenv.cfg");
if (uwsgi_file_exists(pep405_env)) {
uwsgi_log("PEP 405 virtualenv detected: %s\n", up.home);
free(pep405_env);
goto pep405;
}
free(pep405_env);
// build the PYTHONHOME wchar path
wchar_t *wpyhome;
size_t len = strlen(up.home) + 1;
wpyhome = uwsgi_calloc(sizeof(wchar_t) * len );
if (!wpyhome) {
uwsgi_error("malloc()");
exit(1);
}
mbstowcs(wpyhome, up.home, len);
Py_SetPythonHome(wpyhome);
// do not free this memory !!!
//free(wpyhome);
pep405:
#else
Py_SetPythonHome(up.home);
#endif
uwsgi_log("Set PythonHome to %s\n", up.home);
}
char *program_name = up.programname;
if (!program_name) {
program_name = uwsgi.binary_path;
}
#ifdef PYTHREE
if (!up.programname) {
if (up.home) {
program_name = uwsgi_concat2(up.home, "/bin/python");
}
}
wchar_t *pname = uwsgi_calloc(sizeof(wchar_t) * (strlen(program_name)+1));
mbstowcs(pname, program_name, strlen(program_name)+1);
Py_SetProgramName(pname);
#else
Py_SetProgramName(program_name);
#endif
#ifndef UWSGI_PYPY
Py_OptimizeFlag = up.optimize;
#endif
Py_Initialize();
if (!uwsgi.has_threads) {
uwsgi_log_initial("*** Python threads support is disabled. You can enable it with --enable-threads ***\n");
}
up.wsgi_spitout = PyCFunction_New(uwsgi_spit_method, NULL);
up.wsgi_writeout = PyCFunction_New(uwsgi_write_method, NULL);
up.main_thread = PyThreadState_Get();
// by default set a fake GIL (little impact on performance)
up.gil_get = gil_fake_get;
up.gil_release = gil_fake_release;
up.swap_ts = simple_swap_ts;
up.reset_ts = simple_reset_ts;
uwsgi_log_initial("Python main interpreter initialized at %p\n", up.main_thread);
return 1;
}
void uwsgi_python_reset_random_seed() {
PyObject *random_module, *random_dict, *random_seed;
// reinitialize the random seed (thanks Jonas Borgström)
random_module = PyImport_ImportModule("random");
if (random_module) {
random_dict = PyModule_GetDict(random_module);
if (random_dict) {
random_seed = PyDict_GetItemString(random_dict, "seed");
if (random_seed) {
PyObject *random_args = PyTuple_New(1);
#ifdef UWSGI_PYPY
Py_INCREF(Py_None);
#endif
// pass no args
PyTuple_SetItem(random_args, 0, Py_None);
PyEval_CallObject(random_seed, random_args);
if (PyErr_Occurred()) {
PyErr_Print();
}
}
}
}
}
void uwsgi_python_atexit() {
if (uwsgi.mywid == -1) goto realstuff;
// if hijacked do not run atexit hooks
if (uwsgi.workers[uwsgi.mywid].hijacked)
return;
// if busy do not run atexit hooks
if (uwsgi.workers[uwsgi.mywid].busy)
return;
// managing atexit in async mode is a real pain...skip it for now
if (uwsgi.async > 1)
return;
realstuff:
// this time we use this higher level function
// as this code can be executed in a signal handler
if (!Py_IsInitialized()) {
return;
}
// always call it
PyGILState_Ensure();
// no need to worry about freeing memory
PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi");
if (uwsgi_dict) {
PyObject *ae = PyDict_GetItemString(uwsgi_dict, "atexit");
if (ae) {
python_call(ae, PyTuple_New(0), 0, NULL);
}
}
// this part is a 1:1 copy of mod_wsgi 3.x
// it is required to fix some atexit bug with python 3
// and to shutdown useless threads complaints
PyObject *module = PyImport_ImportModule("atexit");
Py_XDECREF(module);
if (uwsgi.has_threads) {
if (!PyImport_AddModule("dummy_threading"))
PyErr_Clear();
}
Py_Finalize();
}
void uwsgi_python_post_fork() {
if (uwsgi.i_am_a_spooler) {
UWSGI_GET_GIL
}
uwsgi_python_reset_random_seed();
// call the post_fork_hook
PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi");
if (uwsgi_dict) {
PyObject *pfh = PyDict_GetItemString(uwsgi_dict, "post_fork_hook");
if (pfh) {
python_call(pfh, PyTuple_New(0), 0, NULL);
}
}
PyErr_Clear();
if (uwsgi.mywid > 0) {
if (up.auto_reload) {
// spawn the reloader thread
pthread_t par_tid;
pthread_create(&par_tid, NULL, uwsgi_python_autoreloader_thread, NULL);
}
#ifndef UWSGI_PYPY
if (up.tracebacker) {
// spawn the tracebacker thread
pthread_t ptb_tid;
pthread_create(&ptb_tid, NULL, uwsgi_python_tracebacker_thread, NULL);
}
#endif
}
UWSGI_RELEASE_GIL
}
PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) {
#ifdef UWSGI_PYPY
uwsgi_log("import by filename is currently not supported on PyPy !!!\n");
return NULL;
#else
FILE *pyfile;
struct _node *py_file_node = NULL;
PyObject *py_compiled_node, *py_file_module;
int is_a_package = 0;
struct stat pystat;
char *real_filename = filename;
if (!uwsgi_check_scheme(filename)) {
pyfile = fopen(filename, "r");
if (!pyfile) {
uwsgi_log("failed to open python file %s\n", filename);
return NULL;
}
if (fstat(fileno(pyfile), &pystat)) {
fclose(pyfile);
uwsgi_error("fstat()");
return NULL;
}
if (S_ISDIR(pystat.st_mode)) {
is_a_package = 1;
fclose(pyfile);
real_filename = uwsgi_concat2(filename, "/__init__.py");
pyfile = fopen(real_filename, "r");
if (!pyfile) {
uwsgi_error_open(real_filename);
free(real_filename);
return NULL;
}
}
py_file_node = PyParser_SimpleParseFile(pyfile, real_filename, Py_file_input);
if (!py_file_node) {
PyErr_Print();
uwsgi_log("failed to parse file %s\n", real_filename);
if (is_a_package)
free(real_filename);
fclose(pyfile);
return NULL;
}
fclose(pyfile);
}
else {
size_t pycontent_size = 0;
char *pycontent = uwsgi_open_and_read(filename, &pycontent_size, 1, NULL);
if (pycontent) {
py_file_node = PyParser_SimpleParseString(pycontent, Py_file_input);
if (!py_file_node) {
PyErr_Print();
uwsgi_log("failed to parse url %s\n", real_filename);
return NULL;
}
}
}
py_compiled_node = (PyObject *) PyNode_Compile(py_file_node, real_filename);
if (!py_compiled_node) {
PyErr_Print();
uwsgi_log("failed to compile python file %s\n", real_filename);
return NULL;
}
if (is_a_package) {
py_file_module = PyImport_AddModule(name);
if (py_file_module) {
PyModule_AddObject(py_file_module, "__path__", Py_BuildValue("[O]", PyString_FromString(filename)));
}
free(real_filename);
}
py_file_module = PyImport_ExecCodeModule(name, py_compiled_node);
if (!py_file_module) {
PyErr_Print();
return NULL;
}
Py_DECREF(py_compiled_node);
return py_file_module;
#endif
}
void init_uwsgi_vars() {
PyObject *pysys, *pysys_dict, *pypath;
PyObject *modules = PyImport_GetModuleDict();
PyObject *tmp_module;
/* add cwd to pythonpath */
pysys = PyImport_ImportModule("sys");
if (!pysys) {
PyErr_Print();
exit(1);
}
pysys_dict = PyModule_GetDict(pysys);
#ifdef PYTHREE
// fix stdout and stderr
PyObject *new_stdprint = PyFile_NewStdPrinter(2);
PyDict_SetItemString(pysys_dict, "stdout", new_stdprint);
PyDict_SetItemString(pysys_dict, "__stdout__", new_stdprint);
PyDict_SetItemString(pysys_dict, "stderr", new_stdprint);
PyDict_SetItemString(pysys_dict, "__stderr__", new_stdprint);
#endif
pypath = PyDict_GetItemString(pysys_dict, "path");
if (!pypath) {
PyErr_Print();
exit(1);
}
if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(".")) != 0) {
PyErr_Print();
}
struct uwsgi_string_list *uppp = up.python_path;
while(uppp) {
if (PyList_Insert(pypath, 0, UWSGI_PYFROMSTRING(uppp->value)) != 0) {
PyErr_Print();
}
else {
uwsgi_log("added %s to pythonpath.\n", uppp->value);
}
uppp = uppp->next;
}
struct uwsgi_string_list *uppma = up.pymodule_alias;
while(uppma) {
// split key=value
char *value = strchr(uppma->value, '=');
if (!value) {
uwsgi_log("invalid pymodule-alias syntax\n");
goto next;
}
value[0] = 0;
if (!strchr(value + 1, '/')) {
// this is a standard pymodule
tmp_module = PyImport_ImportModule(value + 1);
if (!tmp_module) {
PyErr_Print();
exit(1);
}
PyDict_SetItemString(modules, uppma->value, tmp_module);
}
else {
// this is a filepath that need to be mapped
tmp_module = uwsgi_pyimport_by_filename(uppma->value, value + 1);
if (!tmp_module) {
PyErr_Print();
exit(1);
}
}
uwsgi_log("mapped virtual pymodule \"%s\" to real pymodule \"%s\"\n", uppma->value, value + 1);
// reset original value
value[0] = '=';
next:
uppma = uppma->next;
}
}
PyDoc_STRVAR(uwsgi_py_doc, "uWSGI api module.");
#ifdef PYTHREE
static PyModuleDef uwsgi_module3 = {
PyModuleDef_HEAD_INIT,
"uwsgi",
uwsgi_py_doc,
-1,
NULL,
};
PyObject *init_uwsgi3(void) {
return PyModule_Create(&uwsgi_module3);
}
#endif
void init_uwsgi_embedded_module() {
PyObject *new_uwsgi_module, *zero;
int i;
if (PyType_Ready(&uwsgi_InputType) < 0) {
PyErr_Print();
uwsgi_log("could not initialize the uwsgi python module\n");
exit(1);
}
/* initialize for stats */
up.workers_tuple = PyTuple_New(uwsgi.numproc);
for (i = 0; i < uwsgi.numproc; i++) {
zero = PyDict_New();
Py_INCREF(zero);
PyTuple_SetItem(up.workers_tuple, i, zero);
}
#ifdef PYTHREE
PyImport_AppendInittab("uwsgi", init_uwsgi3);
new_uwsgi_module = PyImport_AddModule("uwsgi");
#else
new_uwsgi_module = Py_InitModule3("uwsgi", NULL, uwsgi_py_doc);
#endif
if (new_uwsgi_module == NULL) {
uwsgi_log("could not initialize the uwsgi python module\n");
exit(1);
}
Py_INCREF((PyObject *) &uwsgi_InputType);
up.embedded_dict = PyModule_GetDict(new_uwsgi_module);
if (!up.embedded_dict) {
uwsgi_log("could not get uwsgi module __dict__\n");
exit(1);
}
// just for safety
Py_INCREF(up.embedded_dict);
if (PyDict_SetItemString(up.embedded_dict, "version", PyString_FromString(UWSGI_VERSION))) {
PyErr_Print();
exit(1);
}
PyObject *uwsgi_py_version_info = PyTuple_New(5);
PyTuple_SetItem(uwsgi_py_version_info, 0, PyInt_FromLong(UWSGI_VERSION_BASE));
PyTuple_SetItem(uwsgi_py_version_info, 1, PyInt_FromLong(UWSGI_VERSION_MAJOR));
PyTuple_SetItem(uwsgi_py_version_info, 2, PyInt_FromLong(UWSGI_VERSION_MINOR));
PyTuple_SetItem(uwsgi_py_version_info, 3, PyInt_FromLong(UWSGI_VERSION_REVISION));
PyTuple_SetItem(uwsgi_py_version_info, 4, PyString_FromString(UWSGI_VERSION_CUSTOM));
if (PyDict_SetItemString(up.embedded_dict, "version_info", uwsgi_py_version_info)) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "hostname", PyString_FromStringAndSize(uwsgi.hostname, uwsgi.hostname_len))) {
PyErr_Print();
exit(1);
}
if (uwsgi.mode) {
if (PyDict_SetItemString(up.embedded_dict, "mode", PyString_FromString(uwsgi.mode))) {
PyErr_Print();
exit(1);
}
}
if (uwsgi.pidfile) {
if (PyDict_SetItemString(up.embedded_dict, "pidfile", PyString_FromString(uwsgi.pidfile))) {
PyErr_Print();
exit(1);
}
}
if (uwsgi.spoolers) {
int sc = 0;
struct uwsgi_spooler *uspool = uwsgi.spoolers;
while(uspool) { sc++; uspool = uspool->next;}
PyObject *py_spooler_tuple = PyTuple_New(sc);
uspool = uwsgi.spoolers;
sc = 0;
while(uspool) {
PyTuple_SetItem(py_spooler_tuple, sc, PyString_FromString(uspool->dir));
sc++;
uspool = uspool->next;
}
if (PyDict_SetItemString(up.embedded_dict, "spoolers", py_spooler_tuple)) {
PyErr_Print();
exit(1);
}
}
if (PyDict_SetItemString(up.embedded_dict, "SPOOL_RETRY", PyInt_FromLong(-1))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "SPOOL_OK", PyInt_FromLong(-2))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "SPOOL_IGNORE", PyInt_FromLong(0))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "numproc", PyInt_FromLong(uwsgi.numproc))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "has_threads", PyInt_FromLong(uwsgi.has_threads))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "cores", PyInt_FromLong(uwsgi.cores))) {
PyErr_Print();
exit(1);
}
if (uwsgi.loop) {
if (PyDict_SetItemString(up.embedded_dict, "loop", PyString_FromString(uwsgi.loop))) {
PyErr_Print();
exit(1);
}
}
else {
PyDict_SetItemString(up.embedded_dict, "loop", Py_None);
}
PyObject *py_opt_dict = PyDict_New();
for (i = 0; i < uwsgi.exported_opts_cnt; i++) {
if (PyDict_Contains(py_opt_dict, PyString_FromString(uwsgi.exported_opts[i]->key))) {
PyObject *py_opt_item = PyDict_GetItemString(py_opt_dict, uwsgi.exported_opts[i]->key);
if (PyList_Check(py_opt_item)) {
if (uwsgi.exported_opts[i]->value == NULL) {
PyList_Append(py_opt_item, Py_True);
}
else {
PyList_Append(py_opt_item, PyString_FromString(uwsgi.exported_opts[i]->value));
}
}
else {
PyObject *py_opt_list = PyList_New(0);
PyList_Append(py_opt_list, py_opt_item);
if (uwsgi.exported_opts[i]->value == NULL) {
PyList_Append(py_opt_list, Py_True);
}
else {
PyList_Append(py_opt_list, PyString_FromString(uwsgi.exported_opts[i]->value));
}
PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, py_opt_list);
}
}
else {
if (uwsgi.exported_opts[i]->value == NULL) {
PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, Py_True);
}
else {
PyDict_SetItemString(py_opt_dict, uwsgi.exported_opts[i]->key, PyString_FromString(uwsgi.exported_opts[i]->value));
}
}
}
if (PyDict_SetItemString(up.embedded_dict, "opt", py_opt_dict)) {
PyErr_Print();
exit(1);
}
PyObject *py_magic_table = PyDict_New();
uint8_t mtk;
for (i = 0; i <= 0xff; i++) {
// a bit of magic :P
mtk = i;
if (uwsgi.magic_table[i]) {
if (uwsgi.magic_table[i][0] != 0) {
PyDict_SetItem(py_magic_table, PyString_FromStringAndSize((char *) &mtk, 1), PyString_FromString(uwsgi.magic_table[i]));
}
}
}
if (PyDict_SetItemString(up.embedded_dict, "magic_table", py_magic_table)) {
PyErr_Print();
exit(1);
}
#ifdef UNBIT
if (PyDict_SetItemString(up.embedded_dict, "unbit", Py_True)) {
#else
if (PyDict_SetItemString(up.embedded_dict, "unbit", Py_None)) {
#endif
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "buffer_size", PyInt_FromLong(uwsgi.buffer_size))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "started_on", PyInt_FromLong(uwsgi.start_tv.tv_sec))) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "start_response", up.wsgi_spitout)) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "applications", Py_None)) {
PyErr_Print();
exit(1);
}
if (uwsgi.is_a_reload) {
if (PyDict_SetItemString(up.embedded_dict, "is_a_reload", Py_True)) {
PyErr_Print();
exit(1);
}
}
else {
if (PyDict_SetItemString(up.embedded_dict, "is_a_reload", Py_False)) {
PyErr_Print();
exit(1);
}
}
up.embedded_args = PyTuple_New(2);
if (!up.embedded_args) {
PyErr_Print();
exit(1);
}
if (PyDict_SetItemString(up.embedded_dict, "message_manager_marshal", Py_None)) {
PyErr_Print();
exit(1);
}
init_uwsgi_module_advanced(new_uwsgi_module);
if (uwsgi.spoolers) {
init_uwsgi_module_spooler(new_uwsgi_module);
}
if (uwsgi.sharedareasize > 0 && uwsgi.sharedarea) {
init_uwsgi_module_sharedarea(new_uwsgi_module);
}
if (uwsgi.cache_max_items > 0) {
init_uwsgi_module_cache(new_uwsgi_module);
}
if (uwsgi.queue_size > 0) {
init_uwsgi_module_queue(new_uwsgi_module);
}
if (uwsgi.snmp) {
init_uwsgi_module_snmp(new_uwsgi_module);
}
if (up.extension) {
up.extension();
}
}
int uwsgi_python_magic(char *mountpoint, char *lazy) {
char *qc = strchr(lazy, ':');
if (qc) {
qc[0] = 0;
up.callable = qc + 1;
}
if (!strcmp(lazy + strlen(lazy) - 3, ".py")) {
up.file_config = lazy;
return 1;
}
else if (!strcmp(lazy + strlen(lazy) - 5, ".wsgi")) {
up.file_config = lazy;
return 1;
}
else if (qc && strchr(lazy, '.')) {
up.wsgi_config = lazy;
return 1;
}
// reset lazy
if (qc) {
qc[0] = ':';
}
return 0;
}
int uwsgi_python_mount_app(char *mountpoint, char *app) {
int id;
if (strchr(app, ':') || uwsgi_endswith(app, ".py") || uwsgi_endswith(app, ".wsgi")) {
uwsgi.wsgi_req->appid = mountpoint;
uwsgi.wsgi_req->appid_len = strlen(mountpoint);
if (uwsgi.single_interpreter) {
id = init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI);
}
else {
id = init_uwsgi_app(LOADER_MOUNT, app, uwsgi.wsgi_req, NULL, PYTHON_APP_TYPE_WSGI);
}
return id;
}
return -1;
}
char *uwsgi_pythonize(char *orig) {
char *name = uwsgi_concat2(orig, "");
size_t i;
size_t len = 0;
if (!strncmp(name, "sym://", 6)) {
name+=6;
}
else if (!strncmp(name, "http://", 7)) {
name+=7;
}
else if (!strncmp(name, "data://", 7)) {
name+=7;
}
len = strlen(name);
for(i=0;i<len;i++) {
if (name[i] == '.') {
name[i] = '_';
}
else if (name[i] == '/') {
name[i] = '_';
}
}
if ((name[len-3] == '.' || name[len-3] == '_') && name[len-2] == 'p' && name[len-1] == 'y') {
name[len-3] = 0;
}
return name;
}
void uwsgi_python_spooler_init(void) {
struct uwsgi_string_list *upli = up.spooler_import_list;
UWSGI_GET_GIL
while(upli) {
if (strchr(upli->value, '/') || uwsgi_endswith(upli->value, ".py")) {
uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value);
}
else {
if (PyImport_ImportModule(upli->value) == NULL) {
PyErr_Print();
}
}
upli = upli->next;
}
UWSGI_RELEASE_GIL
}
// this is the default (fake) allocator for WSGI's env
// the dictionary is created on app loading (one for each async core/thread) and reused (clearing it after each request, constantly)
//
// from a python-programmer point of view it is a hack/cheat but it does not violate the WSGI standard
// and it is a bit faster than the "holy" allocator
void *uwsgi_python_create_env_cheat(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) {
wsgi_req->async_args = wi->args[wsgi_req->async_id];
Py_INCREF((PyObject *)wi->environ[wsgi_req->async_id]);
return wi->environ[wsgi_req->async_id];
}
void uwsgi_python_destroy_env_cheat(struct wsgi_request *wsgi_req) {
PyDict_Clear((PyObject *)wsgi_req->async_environ);
}
// this is the "holy" allocator for WSGI's env
// Armin Ronacher told me this is what most of python programmers expect
// I cannot speak for that as i am a perl guy, and i expect only black-magic things :P
//
// this should be the default one, but changing default behaviours (even if they are wrong)
// always make my customers going berserk...
//
// it is only slightly (better: irrelevant) slower, so no fear in enabling it...
void *uwsgi_python_create_env_holy(struct wsgi_request *wsgi_req, struct uwsgi_app *wi) {
wsgi_req->async_args = PyTuple_New(2);
// set start_response()
Py_INCREF(up.wsgi_spitout);
PyTuple_SetItem((PyObject *)wsgi_req->async_args, 1, up.wsgi_spitout);
PyObject *env = PyDict_New();
Py_INCREF(env);
return env;
}
void uwsgi_python_destroy_env_holy(struct wsgi_request *wsgi_req) {
Py_DECREF((PyObject *)wsgi_req->async_environ);
Py_DECREF((PyObject *) wsgi_req->async_args);
// in non-multithread modes, we set uwsgi.env incrementing the refcount of the environ
if (uwsgi.threads < 2) {
Py_DECREF((PyObject *)wsgi_req->async_environ);
}
}
// this hook will be executed by master (or worker1 when master is not requested, so COW is in place)
void uwsgi_python_preinit_apps() {
init_pyargv();
init_uwsgi_embedded_module();
#ifdef __linux__
uwsgi_init_symbol_import();
#endif
if (up.test_module != NULL) {
if (PyImport_ImportModule(up.test_module)) {
exit(0);
}
exit(1);
}
if (!up.wsgi_env_behaviour) {
up.wsgi_env_create = uwsgi_python_create_env_cheat;
up.wsgi_env_destroy = uwsgi_python_destroy_env_cheat;
}
else if (!strcmp(up.wsgi_env_behaviour, "holy")) {
up.wsgi_env_create = uwsgi_python_create_env_holy;
up.wsgi_env_destroy = uwsgi_python_destroy_env_holy;
}
else if (!strcmp(up.wsgi_env_behaviour, "cheat")) {
up.wsgi_env_create = uwsgi_python_create_env_cheat;
up.wsgi_env_destroy = uwsgi_python_destroy_env_cheat;
}
init_uwsgi_vars();
// load shared imports
struct uwsgi_string_list *upli = up.shared_import_list;
while(upli) {
if (strchr(upli->value, '/') || uwsgi_endswith(upli->value, ".py")) {
uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value);
}
else {
if (PyImport_ImportModule(upli->value) == NULL) {
PyErr_Print();
}
}
upli = upli->next;
}
}
void uwsgi_python_init_apps() {
// lazy ?
if (uwsgi.mywid > 0) {
UWSGI_GET_GIL;
}
#ifndef UWSGI_PYPY
// prepare for stack suspend/resume
if (uwsgi.async > 1) {
up.current_recursion_depth = uwsgi_malloc(sizeof(int)*uwsgi.async);
up.current_frame = uwsgi_malloc(sizeof(struct _frame)*uwsgi.async);
}
#endif
// setup app loaders
#ifdef UWSGI_MINTERPRETERS
up.loaders[LOADER_DYN] = uwsgi_dyn_loader;
#endif
up.loaders[LOADER_UWSGI] = uwsgi_uwsgi_loader;
up.loaders[LOADER_FILE] = uwsgi_file_loader;
up.loaders[LOADER_PASTE] = uwsgi_paste_loader;
up.loaders[LOADER_EVAL] = uwsgi_eval_loader;
up.loaders[LOADER_MOUNT] = uwsgi_mount_loader;
up.loaders[LOADER_CALLABLE] = uwsgi_callable_loader;
up.loaders[LOADER_STRING_CALLABLE] = uwsgi_string_callable_loader;
struct uwsgi_string_list *upli = up.import_list;
while(upli) {
if (strchr(upli->value, '/') || uwsgi_endswith(upli->value, ".py")) {
uwsgi_pyimport_by_filename(uwsgi_pythonize(upli->value), upli->value);
}
else {
if (PyImport_ImportModule(upli->value) == NULL) {
PyErr_Print();
}
}
upli = upli->next;
}
struct uwsgi_string_list *uppa = up.post_pymodule_alias;
PyObject *modules = PyImport_GetModuleDict();
PyObject *tmp_module;
while(uppa) {
// split key=value
char *value = strchr(uppa->value, '=');
if (!value) {
uwsgi_log("invalid pymodule-alias syntax\n");
goto next;
}
value[0] = 0;
if (!strchr(value + 1, '/')) {
// this is a standard pymodule
tmp_module = PyImport_ImportModule(value + 1);
if (!tmp_module) {
PyErr_Print();
exit(1);
}
PyDict_SetItemString(modules, uppa->value, tmp_module);
}
else {
// this is a filepath that need to be mapped
tmp_module = uwsgi_pyimport_by_filename(uppa->value, value + 1);
if (!tmp_module) {
PyErr_Print();
exit(1);
}
}
uwsgi_log("mapped virtual pymodule \"%s\" to real pymodule \"%s\"\n", uppa->value, value + 1);
// reset original value
value[0] = '=';
next:
uppa = uppa->next;
}
if (up.wsgi_config != NULL) {
init_uwsgi_app(LOADER_UWSGI, up.wsgi_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI);
}
if (up.file_config != NULL) {
init_uwsgi_app(LOADER_FILE, up.file_config, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI);
}
if (up.paste != NULL) {
init_uwsgi_app(LOADER_PASTE, up.paste, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI);
}
if (up.eval != NULL) {
init_uwsgi_app(LOADER_EVAL, up.eval, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI);
}
if (up.web3 != NULL) {
init_uwsgi_app(LOADER_UWSGI, up.web3, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WEB3);
}
if (up.pump != NULL) {
init_uwsgi_app(LOADER_UWSGI, up.pump, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_PUMP);
}
if (up.wsgi_lite != NULL) {
init_uwsgi_app(LOADER_UWSGI, up.wsgi_lite, uwsgi.wsgi_req, up.main_thread, PYTHON_APP_TYPE_WSGI_LITE);
}
#ifndef UWSGI_PYPY
if (uwsgi.profiler) {
if (!strcmp(uwsgi.profiler, "pycall")) {
PyEval_SetProfile(uwsgi_python_profiler_call, NULL);
}
else if (!strcmp(uwsgi.profiler, "pyline")) {
PyEval_SetTrace(uwsgi_python_tracer, NULL);
}
}
#endif
PyObject *uwsgi_dict = get_uwsgi_pydict("uwsgi");
if (uwsgi_dict) {
up.after_req_hook = PyDict_GetItemString(uwsgi_dict, "after_req_hook");
if (up.after_req_hook) {
Py_INCREF(up.after_req_hook);
up.after_req_hook_args = PyTuple_New(0);
Py_INCREF(up.after_req_hook_args);
}
}
// lazy ?
if (uwsgi.mywid > 0) {
UWSGI_RELEASE_GIL;
}
}
void uwsgi_python_master_fixup(int step) {
static int master_fixed = 0;
static int worker_fixed = 0;
if (!uwsgi.master_process) return;
if (uwsgi.has_threads) {
if (step == 0) {
if (!master_fixed) {
UWSGI_RELEASE_GIL;
master_fixed = 1;
}
}
else {
if (!worker_fixed) {
UWSGI_GET_GIL;
worker_fixed = 1;
}
}
}
}
void uwsgi_python_enable_threads() {
PyEval_InitThreads();
if (pthread_key_create(&up.upt_save_key, NULL)) {
uwsgi_error("pthread_key_create()");
exit(1);
}
if (pthread_key_create(&up.upt_gil_key, NULL)) {
uwsgi_error("pthread_key_create()");
exit(1);
}
pthread_setspecific(up.upt_save_key, (void *) PyThreadState_Get());
pthread_setspecific(up.upt_gil_key, (void *) PyThreadState_Get());
pthread_mutex_init(&up.lock_pyloaders, NULL);
pthread_atfork(uwsgi_python_pthread_prepare, uwsgi_python_pthread_parent, uwsgi_python_pthread_child);
up.gil_get = gil_real_get;
up.gil_release = gil_real_release;
up.swap_ts = simple_threaded_swap_ts;
up.reset_ts = simple_threaded_reset_ts;
if (uwsgi.threads > 1) {
up.swap_ts = threaded_swap_ts;
up.reset_ts = threaded_reset_ts;
}
uwsgi_log("python threads support enabled\n");
}
void uwsgi_python_init_thread(int core_id) {
// set a new ThreadState for each thread
PyThreadState *pts;
pts = PyThreadState_New(up.main_thread->interp);
pthread_setspecific(up.upt_save_key, (void *) pts);
pthread_setspecific(up.upt_gil_key, (void *) pts);
#ifdef UWSGI_DEBUG
uwsgi_log("python ThreadState %d = %p\n", core_id, pts);
#endif
UWSGI_GET_GIL;
// call threading.currentThread (taken from mod_wsgi, but removes DECREFs as thread in uWSGI are fixed)
PyObject *threading_module = PyImport_ImportModule("threading");
if (threading_module) {
PyObject *threading_module_dict = PyModule_GetDict(threading_module);
if (threading_module_dict) {
#ifdef PYTHREE
PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread");
#else
PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread");
#endif
if (threading_current) {
PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL);
if (!current_thread) {
// ignore the error
PyErr_Clear();
}
else {
PyObject_SetAttrString(current_thread, "name", PyString_FromFormat("uWSGIWorker%dCore%d", uwsgi.mywid, core_id));
Py_INCREF(current_thread);
}
}
}
}
UWSGI_RELEASE_GIL;
}
int uwsgi_check_python_mtime(PyObject *times_dict, char *filename) {
struct stat st;
PyObject *py_mtime = PyDict_GetItemString(times_dict, filename);
if (!py_mtime) {
if (stat(filename, &st)) {
return 0;
}
PyDict_SetItemString(times_dict, filename, PyLong_FromLong(st.st_mtime));
}
// the record is already tracked;
else {
long mtime = PyLong_AsLong(py_mtime);
if (stat(filename, &st)) {
return 0;
}
if ((long) st.st_mtime != mtime) {
uwsgi_log("[uwsgi-python-reloader] module/file %s has been modified\n", filename);
kill(uwsgi.workers[0].pid, SIGHUP);
return 1;
}
}
return 0;
}
PyObject *uwsgi_python_setup_thread(char *name) {
// block signals on this thread
sigset_t smask;
sigfillset(&smask);
#ifndef UWSGI_DEBUG
sigdelset(&smask, SIGSEGV);
#endif
pthread_sigmask(SIG_BLOCK, &smask, NULL);
PyThreadState *pts = PyThreadState_New(up.main_thread->interp);
pthread_setspecific(up.upt_save_key, (void *) pts);
pthread_setspecific(up.upt_gil_key, (void *) pts);
UWSGI_GET_GIL;
PyObject *threading_module = PyImport_ImportModule("threading");
if (threading_module) {
PyObject *threading_module_dict = PyModule_GetDict(threading_module);
if (threading_module_dict) {
#ifdef PYTHREE
PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "current_thread");
#else
PyObject *threading_current = PyDict_GetItemString(threading_module_dict, "currentThread");
#endif
if (threading_current) {
PyObject *current_thread = PyEval_CallObject(threading_current, (PyObject *)NULL);
if (!current_thread) {
// ignore the error
PyErr_Clear();
}
else {
PyObject_SetAttrString(current_thread, "name", PyString_FromString(name));
Py_INCREF(current_thread);
return current_thread;
}
}
}
}
return NULL;
}
void *uwsgi_python_autoreloader_thread(void *foobar) {
PyObject *new_thread = uwsgi_python_setup_thread("uWSGIAutoReloader");
if (!new_thread) return NULL;
PyObject *modules = PyImport_GetModuleDict();
if (uwsgi.mywid == 1) {
uwsgi_log("Python auto-reloader enabled\n");
}
PyObject *times_dict = PyDict_New();
char *filename;
for(;;) {
UWSGI_RELEASE_GIL;
sleep(up.auto_reload);
UWSGI_GET_GIL;
// do not start monitoring til the first app is loaded (required for lazy mode)
if (uwsgi_apps_cnt == 0) continue;
#ifdef UWSGI_PYTHON_OLD
int pos = 0;
#else
Py_ssize_t pos = 0;
#endif
PyObject *mod_name, *mod;
while (PyDict_Next(modules, &pos, &mod_name, &mod)) {
int found = 0;
struct uwsgi_string_list *usl = up.auto_reload_ignore;
while(usl) {
if (!strcmp(usl->value, PyString_AsString(mod_name))) {
found = 1;
break;
}
usl = usl->next;
}
if (found) continue;
if (!PyObject_HasAttrString(mod, "__file__")) continue;
PyObject *mod_file = PyObject_GetAttrString(mod, "__file__");
if (!mod_file) continue;
#ifdef PYTHREE
PyObject *zero = PyUnicode_AsUTF8String(mod_file);
char *mod_filename = PyString_AsString(zero);
#else
char *mod_filename = PyString_AsString(mod_file);
#endif
if (!mod_filename) {
#ifdef PYTHREE
Py_DECREF(zero);
#endif
continue;
}
char *ext = strrchr(mod_filename, '.');
if (ext && (!strcmp(ext+1, "pyc") || !strcmp(ext+1, "pyd") || !strcmp(ext+1, "pyo"))) {
filename = uwsgi_concat2n(mod_filename, strlen(mod_filename)-1, "", 0);
}
else {
filename = uwsgi_concat2(mod_filename, "");
}
if (uwsgi_check_python_mtime(times_dict, filename)) {
UWSGI_RELEASE_GIL;
return NULL;
}
free(filename);
#ifdef PYTHREE
Py_DECREF(zero);
#endif
}
}
return NULL;
}
#ifndef UWSGI_PYPY
void uwsgi_python_suspend(struct wsgi_request *wsgi_req) {
PyGILState_STATE pgst = PyGILState_Ensure();
PyThreadState *tstate = PyThreadState_GET();
PyGILState_Release(pgst);
if (wsgi_req) {
up.current_recursion_depth[wsgi_req->async_id] = tstate->recursion_depth;
up.current_frame[wsgi_req->async_id] = tstate->frame;
}
else {
up.current_main_recursion_depth = tstate->recursion_depth;
up.current_main_frame = tstate->frame;
}
}
#endif
char *uwsgi_python_code_string(char *id, char *code, char *function, char *key, uint16_t keylen) {
PyObject *cs_module = NULL;
PyObject *cs_dict = NULL;
UWSGI_GET_GIL;
cs_module = PyImport_ImportModule(id);
if (!cs_module) {
PyErr_Clear();
cs_module = uwsgi_pyimport_by_filename(id, code);
}
if (!cs_module) {
UWSGI_RELEASE_GIL;
return NULL;
}
cs_dict = PyModule_GetDict(cs_module);
if (!cs_dict) {
PyErr_Print();
UWSGI_RELEASE_GIL;
return NULL;
}
PyObject *func = PyDict_GetItemString(cs_dict, function);
if (!func) {
uwsgi_log("function %s not available in %s\n", function, code);
PyErr_Print();
UWSGI_RELEASE_GIL;
return NULL;
}
PyObject *args = PyTuple_New(1);
PyTuple_SetItem(args, 0, PyString_FromStringAndSize(key, keylen));
PyObject *ret = python_call(func, args, 0, NULL);
Py_DECREF(args);
if (ret && PyString_Check(ret)) {
char *val = PyString_AsString(ret);
UWSGI_RELEASE_GIL;
return val;
}
UWSGI_RELEASE_GIL;
return NULL;
}
int uwsgi_python_signal_handler(uint8_t sig, void *handler) {
UWSGI_GET_GIL;
PyObject *args = PyTuple_New(1);
PyObject *ret;
if (!args)
goto clear;
if (!handler) goto clear;
PyTuple_SetItem(args, 0, PyInt_FromLong(sig));
ret = python_call(handler, args, 0, NULL);
Py_DECREF(args);
if (ret) {
Py_DECREF(ret);
UWSGI_RELEASE_GIL;
return 0;
}
clear:
UWSGI_RELEASE_GIL;
return -1;
}
uint16_t uwsgi_python_rpc(void *func, uint8_t argc, char **argv, uint16_t argvs[], char *buffer) {
UWSGI_GET_GIL;
uint8_t i;
char *rv;
size_t rl;
PyObject *pyargs = PyTuple_New(argc);
PyObject *ret;
if (!pyargs)
return 0;
for (i = 0; i < argc; i++) {
PyTuple_SetItem(pyargs, i, PyString_FromStringAndSize(argv[i], argvs[i]));
}
ret = python_call((PyObject *) func, pyargs, 0, NULL);
Py_DECREF(pyargs);
if (ret) {
if (PyString_Check(ret)) {
rv = PyString_AsString(ret);
rl = PyString_Size(ret);
if (rl <= 65536) {
memcpy(buffer, rv, rl);
Py_DECREF(ret);
UWSGI_RELEASE_GIL;
return rl;
}
}
Py_DECREF(ret);
}
if (PyErr_Occurred())
PyErr_Print();
UWSGI_RELEASE_GIL;
return 0;
}
void uwsgi_python_add_item(char *key, uint16_t keylen, char *val, uint16_t vallen, void *data) {
PyObject *pydict = (PyObject *) data;
PyDict_SetItem(pydict, PyString_FromStringAndSize(key, keylen), PyString_FromStringAndSize(val, vallen));
}
int uwsgi_python_spooler(char *filename, char *buf, uint16_t len, char *body, size_t body_len) {
static int random_seed_reset = 0;
UWSGI_GET_GIL;
PyObject *spool_dict = PyDict_New();
PyObject *spool_func, *pyargs, *ret;
if (!random_seed_reset) {
uwsgi_python_reset_random_seed();
random_seed_reset = 1;
}
if (!up.embedded_dict) {
// ignore
UWSGI_RELEASE_GIL;
return 0;
}
spool_func = PyDict_GetItemString(up.embedded_dict, "spooler");
if (!spool_func) {
// ignore
UWSGI_RELEASE_GIL;
return 0;
}
if (uwsgi_hooked_parse(buf, len, uwsgi_python_add_item, spool_dict)) {
// malformed packet, destroy it
UWSGI_RELEASE_GIL;
return -2;
}
pyargs = PyTuple_New(1);
PyDict_SetItemString(spool_dict, "spooler_task_name", PyString_FromString(filename));
if (body && body_len > 0) {
PyDict_SetItemString(spool_dict, "body", PyString_FromStringAndSize(body, body_len));
}
PyTuple_SetItem(pyargs, 0, spool_dict);
ret = python_call(spool_func, pyargs, 0, NULL);
if (ret) {
if (!PyInt_Check(ret)) {
// error, retry
UWSGI_RELEASE_GIL;
return -1;
}
int retval = (int) PyInt_AsLong(ret);
UWSGI_RELEASE_GIL;
return retval;
}
if (PyErr_Occurred())
PyErr_Print();
// error, retry
UWSGI_RELEASE_GIL;
return -1;
}
#ifndef UWSGI_PYPY
void uwsgi_python_resume(struct wsgi_request *wsgi_req) {
PyGILState_STATE pgst = PyGILState_Ensure();
PyThreadState *tstate = PyThreadState_GET();
PyGILState_Release(pgst);
if (wsgi_req) {
tstate->recursion_depth = up.current_recursion_depth[wsgi_req->async_id];
tstate->frame = up.current_frame[wsgi_req->async_id];
}
else {
tstate->recursion_depth = up.current_main_recursion_depth;
tstate->frame = up.current_main_frame;
}
}
#endif
void uwsgi_python_fixup() {
// set hacky modifier 30
uwsgi.p[30] = uwsgi_malloc( sizeof(struct uwsgi_plugin) );
memcpy(uwsgi.p[30], uwsgi.p[0], sizeof(struct uwsgi_plugin) );
uwsgi.p[30]->init_thread = NULL;
uwsgi.p[30]->atexit = NULL;
}
void uwsgi_python_hijack(void) {
// the pyshell will be execute only in the first worker
#ifndef UWSGI_PYPY
FILE *pyfile;
if (up.pyrun) {
uwsgi.workers[uwsgi.mywid].hijacked = 1;
UWSGI_GET_GIL;
pyfile = fopen(up.pyrun, "r");
if (!pyfile) {
uwsgi_error_open(up.pyrun);
exit(1);
}
PyRun_SimpleFile(pyfile, up.pyrun);
// could be never executed
exit(0);
}
#endif
if (up.pyshell_oneshot && uwsgi.workers[uwsgi.mywid].hijacked_count > 0) {
uwsgi.workers[uwsgi.mywid].hijacked = 0;
return;
}
if (up.pyshell && uwsgi.mywid == 1) {
uwsgi.workers[uwsgi.mywid].hijacked = 1;
uwsgi.workers[uwsgi.mywid].hijacked_count++;
// re-map stdin to stdout and stderr if we are logging to a file
if (uwsgi.logfile) {
if (dup2(0, 1) < 0) {
uwsgi_error("dup2()");
}
if (dup2(0, 2) < 0) {
uwsgi_error("dup2()");
}
}
UWSGI_GET_GIL;
PyImport_ImportModule("readline");
#ifndef UWSGI_PYPY
int ret = PyRun_InteractiveLoop(stdin, "uwsgi");
if (up.pyshell_oneshot) {
exit(UWSGI_DE_HIJACKED_CODE);
}
if (ret == 0) {
exit(UWSGI_QUIET_CODE);
}
#endif
exit(0);
}
}
int uwsgi_python_mule(char *opt) {
if (uwsgi_endswith(opt, ".py")) {
UWSGI_GET_GIL;
uwsgi_pyimport_by_filename("__main__", opt);
UWSGI_RELEASE_GIL;
return 1;
}
return 0;
}
int uwsgi_python_mule_msg(char *message, size_t len) {
UWSGI_GET_GIL;
PyObject *mule_msg_hook = PyDict_GetItemString(up.embedded_dict, "mule_msg_hook");
if (!mule_msg_hook) {
// ignore
UWSGI_RELEASE_GIL;
return 0;
}
PyObject *pyargs = PyTuple_New(1);
PyTuple_SetItem(pyargs, 0, PyString_FromStringAndSize(message, len));
PyObject *ret = python_call(mule_msg_hook, pyargs, 0, NULL);
Py_DECREF(pyargs);
if (ret) {
Py_DECREF(ret);
}
if (PyErr_Occurred())
PyErr_Print();
UWSGI_RELEASE_GIL;
return 1;
}
void uwsgi_python_harakiri(int wid) {
if (up.tracebacker) {
char buf[8192];
char *address = uwsgi_concat2(up.tracebacker, uwsgi_num2str(wid));
int fd = uwsgi_connect(address, -1, 0);
while (fd >= 0) {
int ret = uwsgi_waitfd(fd, uwsgi.shared->options[UWSGI_OPTION_SOCKET_TIMEOUT]);
if (ret <= 0) {
break;
}
ssize_t len = read(fd, buf, 8192);
if (len <= 0) {
break;
}
uwsgi_log("%.*s", (int) len, buf);
}
free(address);
}
}
ssize_t uwsgi_python_logger(struct uwsgi_logger *ul, char *message, size_t len) {
if (!Py_IsInitialized()) return -1;
UWSGI_GET_GIL
if (!ul->configured) {
PyObject *py_logging = PyImport_ImportModule("logging");
if (!py_logging) goto clear;
PyObject *py_logging_dict = PyModule_GetDict(py_logging);
if (!py_logging_dict) goto clear;
PyObject *py_getLogger = PyDict_GetItemString(py_logging_dict, "getLogger");
if (!py_getLogger) goto clear;
PyObject *py_getLogger_args = NULL;
if (ul->arg) {
py_getLogger_args = PyTuple_New(1);
PyTuple_SetItem(py_getLogger_args, 0, PyString_FromString(ul->arg));
}
ul->data = (void *) PyEval_CallObject(py_getLogger, py_getLogger_args);
if (PyErr_Occurred()) {
PyErr_Clear();
}
Py_XDECREF(py_getLogger_args);
if (!ul->data) goto clear;
ul->configured = 1;
}
PyObject_CallMethod((PyObject *) ul->data, "error", "(s#)", message, len);
if (PyErr_Occurred()) {
PyErr_Clear();
}
UWSGI_RELEASE_GIL
return len;
clear:
UWSGI_RELEASE_GIL
return -1;
}
void uwsgi_python_on_load() {
uwsgi_register_logger("python", uwsgi_python_logger);
}
#ifndef UWSGI_PYPY
struct uwsgi_plugin python_plugin = {
.name = "python",
#else
struct uwsgi_plugin pypy_plugin = {
.name = "pypy",
#endif
.alias = "python",
.modifier1 = 0,
.init = uwsgi_python_init,
.post_fork = uwsgi_python_post_fork,
.options = uwsgi_python_options,
.request = uwsgi_request_wsgi,
.after_request = uwsgi_after_request_wsgi,
.preinit_apps = uwsgi_python_preinit_apps,
.init_apps = uwsgi_python_init_apps,
.fixup = uwsgi_python_fixup,
.master_fixup = uwsgi_python_master_fixup,
.mount_app = uwsgi_python_mount_app,
.enable_threads = uwsgi_python_enable_threads,
.init_thread = uwsgi_python_init_thread,
.magic = uwsgi_python_magic,
#ifndef UWSGI_PYPY
.suspend = uwsgi_python_suspend,
.resume = uwsgi_python_resume,
#endif
.harakiri = uwsgi_python_harakiri,
.hijack_worker = uwsgi_python_hijack,
.spooler_init = uwsgi_python_spooler_init,
.signal_handler = uwsgi_python_signal_handler,
.rpc = uwsgi_python_rpc,
.mule = uwsgi_python_mule,
.mule_msg = uwsgi_python_mule_msg,
.on_load = uwsgi_python_on_load,
.spooler = uwsgi_python_spooler,
.atexit = uwsgi_python_atexit,
.code_string = uwsgi_python_code_string,
};