mirror of
https://github.com/clearlinux/uwsgi.git
synced 2026-04-28 19:03:37 +00:00
2003 lines
56 KiB
C
2003 lines
56 KiB
C
#include "uwsgi_python.h"
|
|
|
|
/*
|
|
|
|
This is the official python plugin, the one with the modifier1 mapped to 0
|
|
|
|
Since uWSGI 1.0 it is heavily based on Graham Dumpleton mod_wsgi (for Apache)
|
|
|
|
*/
|
|
|
|
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;
|
|
if (value) {
|
|
up.pyshell = value;
|
|
}
|
|
else {
|
|
up.pyshell = "";
|
|
}
|
|
|
|
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) {
|
|
const char *version = Py_GetVersion();
|
|
const char *space = strchr(version, ' ');
|
|
if (space) {
|
|
fprintf(stdout, "%.*s\n", (int) (space-version), version);
|
|
}
|
|
else {
|
|
fprintf(stdout, "%s\n", version);
|
|
}
|
|
exit(0);
|
|
}
|
|
|
|
int uwsgi_python_init(void);
|
|
void uwsgi_python_preinit_apps(void);
|
|
static void uwsgi_early_python(char *opt, char *foo, void *bar) {
|
|
static int early_initialized = 0;
|
|
if (early_initialized) return;
|
|
early_initialized = 1;
|
|
uwsgi_python_init();
|
|
uwsgi_python_preinit_apps();
|
|
}
|
|
|
|
|
|
static void uwsgi_early_python_import(char *opt, char *module, void *bar) {
|
|
uwsgi_early_python(opt, NULL, NULL);
|
|
if (strchr(module, '/') || uwsgi_endswith(module, ".py")) {
|
|
uwsgi_pyimport_by_filename(uwsgi_pythonize(module), module);
|
|
}
|
|
else {
|
|
if (PyImport_ImportModule(module) == NULL) {
|
|
PyErr_Print();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
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 module 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},
|
|
|
|
|
|
{"pecan", required_argument, 0, "load a pecan config file", uwsgi_opt_set_str, &up.pecan, 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},
|
|
{"paste-name", required_argument, 0, "specify the name of the paste section", uwsgi_opt_set_str, &up.paste_name, 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},
|
|
{"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},
|
|
{"reload-os-env", no_argument, 0, "force reload of os.environ at each request", uwsgi_opt_true, &up.reload_os_env, 0},
|
|
#ifndef __CYGWIN__
|
|
{"no-site", no_argument, 0, "do not import site module", uwsgi_opt_true, &Py_NoSiteFlag, 0},
|
|
#endif
|
|
{"pyshell", optional_argument, 0, "run an interactive python shell in the uWSGI environment", uwsgi_opt_pyshell, NULL, 0},
|
|
{"pyshell-oneshot", optional_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},
|
|
|
|
{"py-tracebacker", required_argument, 0, "enable the uWSGI python tracebacker", uwsgi_opt_set_str, &up.tracebacker, UWSGI_OPT_THREADS|UWSGI_OPT_MASTER},
|
|
|
|
{"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},
|
|
|
|
{"wsgi-strict", no_argument, 0, "try to be fully PEP compliant disabling optimizations", uwsgi_opt_true, &up.wsgi_strict, 0},
|
|
{"wsgi-accept-buffer", no_argument, 0, "accept CPython buffer-compliant objects as WSGI response in addition to string/bytes", uwsgi_opt_true, &up.wsgi_accept_buffer, 0},
|
|
{"wsgi-accept-buffers", no_argument, 0, "accept CPython buffer-compliant objects as WSGI response in addition to string/bytes", uwsgi_opt_true, &up.wsgi_accept_buffer, 0},
|
|
|
|
{"python-version", no_argument, 0, "report python version", uwsgi_opt_pyver, NULL, UWSGI_OPT_IMMEDIATE},
|
|
|
|
{"python-raw", required_argument, 0, "load a python file for managing raw requests", uwsgi_opt_set_str, &up.raw, 0},
|
|
|
|
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
|
{"py-sharedarea", required_argument, 0, "create a sharedarea from a python bytearray object of the specified size", uwsgi_opt_add_string_list, &up.sharedarea, 0},
|
|
#endif
|
|
|
|
{"py-call-osafterfork", no_argument, 0, "enable child processes running cpython to trap OS signals", uwsgi_opt_true, &up.call_osafterfork, 0},
|
|
|
|
{"early-python", no_argument, 0, "load the python VM as soon as possible (useful for the fork server)", uwsgi_early_python, NULL, UWSGI_OPT_IMMEDIATE},
|
|
{"early-pyimport", required_argument, 0, "import a python module in the early phase", uwsgi_early_python_import, NULL, UWSGI_OPT_IMMEDIATE},
|
|
{"early-python-import", required_argument, 0, "import a python module in the early phase", uwsgi_early_python_import, NULL, UWSGI_OPT_IMMEDIATE},
|
|
{"early-pythonpath", required_argument, 0, "add directory (or glob) to pythonpath (immediate version)", uwsgi_opt_pythonpath, NULL, UWSGI_OPT_IMMEDIATE},
|
|
{"early-python-path", required_argument, 0, "add directory (or glob) to pythonpath (immediate version)", uwsgi_opt_pythonpath, 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) {
|
|
if (!up.is_dynamically_loading_an_app)
|
|
pthread_mutex_lock(&up.lock_pyloaders);
|
|
}
|
|
|
|
void uwsgi_python_pthread_parent(void) {
|
|
if (!up.is_dynamically_loading_an_app)
|
|
pthread_mutex_unlock(&up.lock_pyloaders);
|
|
}
|
|
|
|
void uwsgi_python_pthread_child(void) {
|
|
if (!up.is_dynamically_loading_an_app)
|
|
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() {
|
|
|
|
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);
|
|
}
|
|
|
|
if (Py_IsInitialized()) {
|
|
uwsgi_log("--- Python VM already initialized ---\n");
|
|
PyGILState_Ensure();
|
|
goto ready;
|
|
}
|
|
|
|
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
|
|
|
|
|
|
Py_OptimizeFlag = up.optimize;
|
|
|
|
Py_Initialize();
|
|
|
|
ready:
|
|
|
|
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;
|
|
|
|
#if defined(PYTHREE) || defined(Py_TPFLAGS_HAVE_NEWBUFFER)
|
|
struct uwsgi_string_list *usl = NULL;
|
|
uwsgi_foreach(usl, up.sharedarea) {
|
|
uint64_t len = uwsgi_n64(usl->value);
|
|
PyObject *obj = PyByteArray_FromStringAndSize(NULL, len);
|
|
char *storage = PyByteArray_AsString(obj);
|
|
Py_INCREF(obj);
|
|
struct uwsgi_sharedarea *sa = uwsgi_sharedarea_init_ptr(storage, len);
|
|
sa->obj = obj;
|
|
}
|
|
#endif
|
|
|
|
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);
|
|
// 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 == 0) 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_worker_is_busy(uwsgi.mywid))
|
|
return;
|
|
|
|
// managing atexit in async mode is a real pain...skip it for now
|
|
if (uwsgi.async > 0)
|
|
return;
|
|
realstuff:
|
|
|
|
// this time we use this higher level function
|
|
// as this code can be executed in a signal handler
|
|
|
|
#ifdef __GNU_kFreeBSD__
|
|
return;
|
|
#endif
|
|
|
|
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
|
|
}
|
|
|
|
// reset python signal flags so child processes can trap signals
|
|
if (up.call_osafterfork) {
|
|
PyOS_AfterFork();
|
|
}
|
|
|
|
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) {
|
|
uwsgi_python_set_thread_name(0);
|
|
if (up.auto_reload) {
|
|
// spawn the reloader thread
|
|
pthread_t par_tid;
|
|
pthread_create(&par_tid, NULL, uwsgi_python_autoreloader_thread, NULL);
|
|
}
|
|
if (up.tracebacker) {
|
|
// spawn the tracebacker thread
|
|
pthread_t ptb_tid;
|
|
pthread_create(&ptb_tid, NULL, uwsgi_python_tracebacker_thread, NULL);
|
|
}
|
|
}
|
|
|
|
UWSGI_RELEASE_GIL
|
|
|
|
}
|
|
|
|
PyObject *uwsgi_pyimport_by_filename(char *name, char *filename) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
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
|
|
#ifdef HAS_NO_ERRORS_IN_PyFile_FromFd
|
|
PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, 0);
|
|
#else
|
|
PyObject *new_stdprint = PyFile_FromFd(2, NULL, "w", _IOLBF, NULL, NULL, NULL, 0);
|
|
#endif
|
|
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);
|
|
}
|
|
|
|
init_uwsgi_module_advanced(new_uwsgi_module);
|
|
|
|
if (uwsgi.spoolers) {
|
|
init_uwsgi_module_spooler(new_uwsgi_module);
|
|
}
|
|
|
|
if (uwsgi.sharedareas) {
|
|
init_uwsgi_module_sharedarea(new_uwsgi_module);
|
|
}
|
|
|
|
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);
|
|
// lazy ?
|
|
if (uwsgi.mywid > 0) UWSGI_GET_GIL
|
|
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);
|
|
}
|
|
// lazy ?
|
|
if (uwsgi.mywid > 0) UWSGI_RELEASE_GIL
|
|
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() {
|
|
struct uwsgi_string_list *upli = up.shared_import_list;
|
|
|
|
if (up.pre_initialized) goto ready;
|
|
up.pre_initialized = 1;
|
|
|
|
// setup app loaders
|
|
up.loaders[LOADER_DYN] = uwsgi_dyn_loader;
|
|
up.loaders[LOADER_UWSGI] = uwsgi_uwsgi_loader;
|
|
up.loaders[LOADER_FILE] = uwsgi_file_loader;
|
|
up.loaders[LOADER_PECAN] = uwsgi_pecan_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;
|
|
|
|
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();
|
|
|
|
ready:
|
|
|
|
// load shared imports
|
|
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;
|
|
}
|
|
|
|
// prepare for stack suspend/resume
|
|
if (uwsgi.async > 0) {
|
|
up.current_recursion_depth = uwsgi_malloc(sizeof(int)*uwsgi.async);
|
|
up.current_frame = uwsgi_malloc(sizeof(struct _frame)*uwsgi.async);
|
|
}
|
|
|
|
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.raw) {
|
|
up.raw_callable = uwsgi_file_loader(up.raw);
|
|
if (up.raw_callable) {
|
|
Py_INCREF(up.raw_callable);
|
|
}
|
|
}
|
|
|
|
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.pecan != NULL) {
|
|
init_uwsgi_app(LOADER_PECAN, up.pecan, 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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
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_set_thread_name(int core_id) {
|
|
// 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
uwsgi_python_set_thread_name(core_id);
|
|
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;
|
|
if (uwsgi.lazy || uwsgi.lazy_apps) {
|
|
// 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)) {
|
|
if (mod == NULL) continue;
|
|
int found = 0;
|
|
struct uwsgi_string_list *usl = up.auto_reload_ignore;
|
|
while(usl) {
|
|
#ifdef PYTHREE
|
|
PyObject *zero = PyUnicode_AsUTF8String(mod_name);
|
|
char *str_mod_name = PyString_AsString(zero);
|
|
int ret_cmp = strcmp(usl->value, str_mod_name);
|
|
Py_DECREF(zero);
|
|
if (!ret_cmp) {
|
|
#else
|
|
if (!strcmp(usl->value, PyString_AsString(mod_name))) {
|
|
#endif
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
uint64_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 > 0) {
|
|
*buffer = uwsgi_malloc(rl);
|
|
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;
|
|
|
|
PyObject *o_key = PyString_FromStringAndSize(key, keylen);
|
|
PyObject *zero = PyString_FromStringAndSize(val, vallen);
|
|
PyDict_SetItem(pydict, o_key, zero);
|
|
Py_DECREF(o_key);
|
|
Py_DECREF(zero);
|
|
}
|
|
|
|
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;
|
|
|
|
if (!random_seed_reset) {
|
|
uwsgi_python_reset_random_seed();
|
|
random_seed_reset = 1;
|
|
}
|
|
|
|
if (!up.embedded_dict) {
|
|
// ignore
|
|
UWSGI_RELEASE_GIL;
|
|
return 0;
|
|
}
|
|
|
|
PyObject *spool_func = PyDict_GetItemString(up.embedded_dict, "spooler");
|
|
if (!spool_func) {
|
|
// ignore
|
|
UWSGI_RELEASE_GIL;
|
|
return 0;
|
|
}
|
|
|
|
int retval = -1;
|
|
PyObject *spool_dict = PyDict_New();
|
|
PyObject *pyargs = PyTuple_New(1);
|
|
PyObject *ret = NULL;
|
|
|
|
PyObject *value = PyString_FromString(filename);
|
|
PyDict_SetItemString(spool_dict, "spooler_task_name", value);
|
|
Py_DECREF(value);
|
|
|
|
if (uwsgi_hooked_parse(buf, len, uwsgi_python_add_item, spool_dict)) {
|
|
// malformed packet, destroy it
|
|
retval = -2;
|
|
goto clear;
|
|
}
|
|
|
|
if (body && body_len > 0) {
|
|
PyObject *value = PyString_FromStringAndSize(body, body_len);
|
|
PyDict_SetItemString(spool_dict, "body", value);
|
|
Py_DECREF(value);
|
|
}
|
|
// PyTuple_SetItem steals a reference !!!
|
|
Py_INCREF(spool_dict);
|
|
PyTuple_SetItem(pyargs, 0, spool_dict);
|
|
|
|
ret = python_call(spool_func, pyargs, 0, NULL);
|
|
|
|
if (ret) {
|
|
if (!PyInt_Check(ret)) {
|
|
retval = -1; // error, retry
|
|
} else {
|
|
retval = (int) PyInt_AsLong(ret);
|
|
}
|
|
goto clear;
|
|
}
|
|
|
|
if (PyErr_Occurred())
|
|
PyErr_Print();
|
|
|
|
// error, retry
|
|
retval = -1;
|
|
|
|
clear:
|
|
Py_XDECREF(ret);
|
|
Py_XDECREF(pyargs);
|
|
Py_XDECREF(spool_dict);
|
|
UWSGI_RELEASE_GIL;
|
|
return retval;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
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);
|
|
}
|
|
|
|
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;
|
|
int ret = -1;
|
|
if (up.pyshell[0] != 0) {
|
|
ret = PyRun_SimpleString(up.pyshell);
|
|
}
|
|
else {
|
|
PyImport_ImportModule("readline");
|
|
|
|
ret = PyRun_InteractiveLoop(stdin, "uwsgi");
|
|
}
|
|
if (up.pyshell_oneshot) {
|
|
exit(UWSGI_DE_HIJACKED_CODE);
|
|
}
|
|
|
|
if (ret == 0) {
|
|
exit(UWSGI_QUIET_CODE);
|
|
}
|
|
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;
|
|
}
|
|
else if (strchr(opt, ':')) {
|
|
UWSGI_GET_GIL;
|
|
PyObject *result = NULL;
|
|
PyObject *arglist = Py_BuildValue("()");
|
|
PyObject *callable = up.loaders[LOADER_MOUNT](opt);
|
|
if (callable) {
|
|
result = PyEval_CallObject(callable, arglist);
|
|
}
|
|
Py_XDECREF(result);
|
|
Py_XDECREF(arglist);
|
|
Py_XDECREF(callable);
|
|
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;
|
|
}
|
|
|
|
static void uwsgi_python_harakiri(int wid) {
|
|
|
|
if (up.tracebacker) {
|
|
|
|
char buf[8192];
|
|
char *wid_str = uwsgi_num2str(wid);
|
|
char *address = uwsgi_concat2(up.tracebacker, wid_str);
|
|
|
|
int fd = uwsgi_connect(address, -1, 0);
|
|
if (fd < 1)
|
|
goto exit;
|
|
|
|
for(;;) {
|
|
int ret = uwsgi_waitfd(fd, uwsgi.socket_timeout);
|
|
if (ret <= 0) goto cleanup;
|
|
ssize_t len = read(fd, buf, 8192);
|
|
if (len <= 0) goto cleanup;
|
|
uwsgi_log("%.*s", (int) len, buf);
|
|
}
|
|
|
|
cleanup:
|
|
close(fd);
|
|
exit:
|
|
free(wid_str);
|
|
free(address);
|
|
}
|
|
|
|
}
|
|
/*
|
|
# you can use this logger to offload logging to python
|
|
# be sure to configure it to not log to stderr otherwise you will generate a loop
|
|
import logging
|
|
logging.basicConfig(filename='/tmp/pippo.log')
|
|
*/
|
|
static 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;
|
|
}
|
|
|
|
static void uwsgi_python_on_load() {
|
|
uwsgi_register_logger("python", uwsgi_python_logger);
|
|
}
|
|
|
|
struct uwsgi_plugin python_plugin = {
|
|
.name = "python",
|
|
.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,
|
|
|
|
.suspend = uwsgi_python_suspend,
|
|
.resume = uwsgi_python_resume,
|
|
|
|
.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,
|
|
|
|
.exception_class = uwsgi_python_exception_class,
|
|
.exception_msg = uwsgi_python_exception_msg,
|
|
.exception_repr = uwsgi_python_exception_repr,
|
|
.exception_log = uwsgi_python_exception_log,
|
|
.backtrace = uwsgi_python_backtrace,
|
|
|
|
|
|
};
|