зеркало из https://github.com/mozilla/pjs.git
Move all Python initialization code into a new global
PyXPCOM_EnsurePythonEnvironment. At the same time remove the old, dead code behind PYXPCOM_USE_PYGILSTATE and LOADER_LINKS_WITH_PYTHON. Not part of the default build.
This commit is contained in:
Родитель
e7c870d4eb
Коммит
c1b2e6b995
|
@ -142,8 +142,6 @@ PyG_Base::PyG_Base(PyObject *instance, const nsIID &iid)
|
|||
#endif // DEBUG_LIFETIMES
|
||||
Py_XINCREF(instance); // instance should never be NULL - but what's an X between friends!
|
||||
|
||||
PyXPCOM_DLLAddRef();
|
||||
|
||||
#ifdef DEBUG_FULL
|
||||
LogF("PyGatewayBase: created %s", m_pPyObject ? m_pPyObject->ob_type->tp_name : "<NULL>");
|
||||
#endif
|
||||
|
@ -169,7 +167,6 @@ PyG_Base::~PyG_Base()
|
|||
p->m_pBase = nsnull;
|
||||
m_pWeakRef = nsnull;
|
||||
}
|
||||
PyXPCOM_DLLRelease();
|
||||
}
|
||||
|
||||
// Get the correct interface pointer for this object given the IID.
|
||||
|
|
|
@ -73,7 +73,6 @@ Py_nsISupports::Py_nsISupports(nsISupports *punk, const nsIID &iid, PyTypeObject
|
|||
m_iid = iid;
|
||||
// refcnt of object managed by caller.
|
||||
PR_AtomicIncrement(&cInterfaces);
|
||||
PyXPCOM_DLLAddRef();
|
||||
_Py_NewReference(this);
|
||||
}
|
||||
|
||||
|
@ -81,7 +80,6 @@ Py_nsISupports::~Py_nsISupports()
|
|||
{
|
||||
SafeRelease(this);
|
||||
PR_AtomicDecrement(&cInterfaces);
|
||||
PyXPCOM_DLLRelease();
|
||||
}
|
||||
|
||||
/*static*/ nsISupports *
|
||||
|
|
|
@ -121,8 +121,9 @@ PYXPCOM_EXPORT PRBool PyXPCOM_FormatGivenException(nsCString &streamout,
|
|||
PyObject *exc_typ, PyObject *exc_val,
|
||||
PyObject *exc_tb);
|
||||
|
||||
// A couple of logging/error functions. These probably end up
|
||||
// being written to the console service.
|
||||
// A couple of logging/error functions. These now end up being written
|
||||
// via the logging module, and via handlers added by the xpcom package,
|
||||
// then are written to the JSConsole and the ConsoleService.
|
||||
|
||||
// Log a warning for the user - something at runtime
|
||||
// they may care about, but nothing that prevents us actually
|
||||
|
@ -621,6 +622,10 @@ public:
|
|||
~CEnterLeaveXPCOMFramework() {PyXPCOM_ReleaseGlobalLock();}
|
||||
};
|
||||
|
||||
// Initialize Python and do anything else necessary to get a function
|
||||
// Python environment going...
|
||||
PYXPCOM_EXPORT void PyXPCOM_EnsurePythonEnvironment(void);
|
||||
|
||||
// Python thread-lock stuff. Free-threading patches use different semantics, but
|
||||
// these are abstracted away here...
|
||||
//#include <threadstate.h>
|
||||
|
|
|
@ -50,6 +50,12 @@
|
|||
#include "nsIThread.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsTraceRefcntImpl.h"
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsILocalFile.h"
|
||||
#include "nsITimelineService.h"
|
||||
|
||||
#include <nsIConsoleService.h>
|
||||
#include "nspr.h" // PR_fprintf
|
||||
|
||||
#ifdef XP_WIN
|
||||
#ifndef WIN32_LEAN_AND_MEAN
|
||||
|
@ -58,7 +64,11 @@
|
|||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
static PRInt32 g_cLockCount = 0;
|
||||
#ifdef XP_UNIX
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#endif
|
||||
|
||||
static PRLock *g_lockMain = nsnull;
|
||||
|
||||
PYXPCOM_EXPORT PyObject *PyXPCOM_Error = NULL;
|
||||
|
@ -72,109 +82,6 @@ PyXPCOM_INTERFACE_DEFINE(Py_nsIInputStream, nsIInputStream, PyMethods_IInputStre
|
|||
PyXPCOM_INTERFACE_DEFINE(Py_nsIClassInfo, nsIClassInfo, PyMethods_IClassInfo)
|
||||
PyXPCOM_INTERFACE_DEFINE(Py_nsIVariant, nsIVariant, PyMethods_IVariant)
|
||||
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Thread-state helpers/global functions.
|
||||
// Only used if there is no Python PyGILState_* API
|
||||
//
|
||||
static PyThreadState *ptsGlobal = nsnull;
|
||||
PyInterpreterState *PyXPCOM_InterpreterState = nsnull;
|
||||
PRUintn tlsIndex = 0;
|
||||
|
||||
|
||||
// This function must be called at some time when the interpreter lock and state is valid.
|
||||
// Called by init{module} functions and also COM factory entry point.
|
||||
void PyXPCOM_InterpreterState_Ensure()
|
||||
{
|
||||
if (PyXPCOM_InterpreterState==NULL) {
|
||||
PyThreadState *threadStateSave = PyThreadState_Swap(NULL);
|
||||
if (threadStateSave==NULL)
|
||||
Py_FatalError("Can not setup interpreter state, as current state is invalid");
|
||||
|
||||
PyXPCOM_InterpreterState = threadStateSave->interp;
|
||||
PyThreadState_Swap(threadStateSave);
|
||||
}
|
||||
}
|
||||
|
||||
void PyXPCOM_InterpreterState_Free()
|
||||
{
|
||||
PyXPCOM_ThreadState_Free();
|
||||
PyXPCOM_InterpreterState = NULL; // Eek - should I be freeing something?
|
||||
}
|
||||
|
||||
// This structure is stored in the TLS slot. At this stage only a Python thread state
|
||||
// is kept, but this may change in the future...
|
||||
struct ThreadData{
|
||||
PyThreadState *ts;
|
||||
};
|
||||
|
||||
// Ensure that we have a Python thread state available to use.
|
||||
// If this is called for the first time on a thread, it will allocate
|
||||
// the thread state. This does NOT change the state of the Python lock.
|
||||
// Returns TRUE if a new thread state was created, or FALSE if a
|
||||
// thread state already existed.
|
||||
PRBool PyXPCOM_ThreadState_Ensure()
|
||||
{
|
||||
ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex);
|
||||
if (pData==NULL) { /* First request on this thread */
|
||||
/* Check we have an interpreter state */
|
||||
if (PyXPCOM_InterpreterState==NULL) {
|
||||
Py_FatalError("Can not setup thread state, as have no interpreter state");
|
||||
}
|
||||
pData = (ThreadData *)nsMemory::Alloc(sizeof(ThreadData));
|
||||
if (!pData)
|
||||
Py_FatalError("Out of memory allocating thread state.");
|
||||
memset(pData, 0, sizeof(*pData));
|
||||
if (NS_FAILED( PR_SetThreadPrivate( tlsIndex, pData ) ) ) {
|
||||
NS_ABORT_IF_FALSE(0, "Could not create thread data for this thread!");
|
||||
Py_FatalError("Could not thread private thread data!");
|
||||
}
|
||||
pData->ts = PyThreadState_New(PyXPCOM_InterpreterState);
|
||||
return PR_TRUE; // Did create a thread state state
|
||||
}
|
||||
return PR_FALSE; // Thread state was previously created
|
||||
}
|
||||
|
||||
// Asuming we have a valid thread state, acquire the Python lock.
|
||||
void PyXPCOM_InterpreterLock_Acquire()
|
||||
{
|
||||
ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex);
|
||||
NS_ABORT_IF_FALSE(pData, "Have no thread data for this thread!");
|
||||
PyThreadState *thisThreadState = pData->ts;
|
||||
PyEval_AcquireThread(thisThreadState);
|
||||
}
|
||||
|
||||
// Asuming we have a valid thread state, release the Python lock.
|
||||
void PyXPCOM_InterpreterLock_Release()
|
||||
{
|
||||
ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex);
|
||||
NS_ABORT_IF_FALSE(pData, "Have no thread data for this thread!");
|
||||
PyThreadState *thisThreadState = pData->ts;
|
||||
PyEval_ReleaseThread(thisThreadState);
|
||||
}
|
||||
|
||||
// Free the thread state for the current thread
|
||||
// (Presumably previously create with a call to
|
||||
// PyXPCOM_ThreadState_Ensure)
|
||||
void PyXPCOM_ThreadState_Free()
|
||||
{
|
||||
ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex);
|
||||
if (!pData) return;
|
||||
PyThreadState *thisThreadState = pData->ts;
|
||||
PyThreadState_Delete(thisThreadState);
|
||||
PR_SetThreadPrivate(tlsIndex, NULL);
|
||||
nsMemory::Free(pData);
|
||||
}
|
||||
|
||||
void PyXPCOM_ThreadState_Clear()
|
||||
{
|
||||
ThreadData *pData = (ThreadData *)PR_GetThreadPrivate(tlsIndex);
|
||||
PyThreadState *thisThreadState = pData->ts;
|
||||
PyThreadState_Clear(thisThreadState);
|
||||
}
|
||||
#endif // PYXPCOM_USE_PYGILSTATE
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// Lock/exclusion global functions.
|
||||
//
|
||||
|
@ -192,65 +99,166 @@ PyXPCOM_ReleaseGlobalLock(void)
|
|||
PR_Unlock(g_lockMain);
|
||||
}
|
||||
|
||||
void PyXPCOM_DLLAddRef(void)
|
||||
// Note we can't use the PyXPCOM_Log* functions as we are still booting
|
||||
// up the xpcom support, which is what sets up the logger etc. So we
|
||||
// just write directly to the console service and to stderr.
|
||||
static void DoLogStartupMessage(const char *prefix, const char *fmt, va_list argptr)
|
||||
{
|
||||
// Must be thread-safe, although can't have the Python lock!
|
||||
CEnterLeaveXPCOMFramework _celf;
|
||||
PRInt32 cnt = PR_AtomicIncrement(&g_cLockCount);
|
||||
if (cnt==1) { // First call
|
||||
if (!Py_IsInitialized()) {
|
||||
Py_Initialize();
|
||||
// Make sure our Windows framework is all setup.
|
||||
PyXPCOM_Globals_Ensure();
|
||||
// Make sure we have _something_ as sys.argv.
|
||||
if (PySys_GetObject("argv")==NULL) {
|
||||
PyObject *path = PyList_New(0);
|
||||
PyObject *str = PyString_FromString("");
|
||||
PyList_Append(path, str);
|
||||
PySys_SetObject("argv", path);
|
||||
Py_XDECREF(path);
|
||||
Py_XDECREF(str);
|
||||
}
|
||||
char buff[2048];
|
||||
PR_vsnprintf(buff, sizeof(buff), fmt, argptr);
|
||||
|
||||
// Must force Python to start using thread locks, as
|
||||
// we are free-threaded (maybe, I think, sometimes :-)
|
||||
PyEval_InitThreads();
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
// Release Python lock, as first thing we do is re-get it.
|
||||
ptsGlobal = PyEval_SaveThread();
|
||||
nsCOMPtr<nsIConsoleService> consoleService = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
|
||||
if (consoleService)
|
||||
consoleService->LogStringMessage(NS_ConvertASCIItoUTF16(buff).get());
|
||||
PR_fprintf(PR_STDERR,"%s\n", buff);
|
||||
}
|
||||
|
||||
static void LogStartupError(const char *fmt, ...)
|
||||
{
|
||||
va_list marker;
|
||||
va_start(marker, fmt);
|
||||
DoLogStartupMessage("PyXPCOM Startup Error:", fmt, marker);
|
||||
}
|
||||
|
||||
static void LogStartupDebug(const char *fmt, ...)
|
||||
{
|
||||
#ifdef NS_DEBUG
|
||||
va_list marker;
|
||||
va_start(marker, fmt);
|
||||
DoLogStartupMessage("", fmt, marker);
|
||||
#endif
|
||||
// NOTE: We never finalize Python!!
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure that any paths guaranteed by this package exist on sys.path
|
||||
// Only called once as we are first loaded into the process.
|
||||
void AddStandardPaths()
|
||||
{
|
||||
// Put {bin}\Python on the path if it exists.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> aFile;
|
||||
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, getter_AddRefs(aFile));
|
||||
if (NS_FAILED(rv)) {
|
||||
LogStartupError("The Python XPCOM loader could not locate the 'bin' directory");
|
||||
return;
|
||||
}
|
||||
aFile->Append(NS_LITERAL_STRING("python"));
|
||||
nsAutoString pathBuf;
|
||||
aFile->GetPath(pathBuf);
|
||||
PyObject *obPath = PySys_GetObject("path");
|
||||
if (!obPath) {
|
||||
LogStartupError("The Python XPCOM loader could not get the Python sys.path variable");
|
||||
return;
|
||||
}
|
||||
// XXX - this should use the file-system encoding...
|
||||
NS_LossyConvertUTF16toASCII pathCBuf(pathBuf);
|
||||
// This is too early for effective LogDebug
|
||||
LogStartupDebug("The Python XPCOM loader is adding '%s' to sys.path",
|
||||
pathCBuf.get());
|
||||
PyObject *newStr = PyString_FromString(pathCBuf.get());
|
||||
PyList_Insert(obPath, 0, newStr);
|
||||
Py_XDECREF(newStr);
|
||||
// And now try and get Python to process this directory as a "site dir"
|
||||
// - ie, look for .pth files, etc
|
||||
nsCAutoString cmdBuf(NS_LITERAL_CSTRING("import site;site.addsitedir(r'") + pathCBuf + NS_LITERAL_CSTRING("')\n"));
|
||||
if (0 != PyRun_SimpleString((char *)cmdBuf.get())) {
|
||||
LogStartupError("The directory '%s' could not be added as a site directory", pathCBuf.get());
|
||||
PyErr_Clear();
|
||||
}
|
||||
// and somewhat like Python itself (site, citecustomize), we attempt
|
||||
// to import "sitepyxpcom" ignoring ImportError
|
||||
if (NULL==PyImport_ImportModule("sitepyxpcom")) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_ImportError))
|
||||
LogStartupError("Failed to import 'sitepyxpcom'");
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
void PyXPCOM_DLLRelease(void)
|
||||
|
||||
PYXPCOM_EXPORT void
|
||||
PyXPCOM_EnsurePythonEnvironment(void)
|
||||
{
|
||||
PR_AtomicDecrement(&g_cLockCount);
|
||||
static PRBool bIsInitialized = PR_FALSE;
|
||||
// Must be thread-safe
|
||||
CEnterLeaveXPCOMFramework _celf;
|
||||
if (bIsInitialized)
|
||||
return;
|
||||
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
/* *sob* - seems necessary to open the .so as RTLD_GLOBAL. Without
|
||||
this we see:
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1, in ?
|
||||
File "/usr/lib/python2.4/logging/__init__.py", line 29, in ?
|
||||
import sys, os, types, time, string, cStringIO, traceback
|
||||
ImportError: /usr/lib/python2.4/lib-dynload/time.so: undefined
|
||||
symbol: PyExc_IOError
|
||||
|
||||
On osx, ShaneC writes that is it unnecessary (and fails anyway since
|
||||
PYTHON_SO is wrong.) More clues about this welcome!
|
||||
*/
|
||||
|
||||
dlopen(PYTHON_SO,RTLD_NOW | RTLD_GLOBAL);
|
||||
#endif
|
||||
|
||||
PRBool bDidInitPython = !Py_IsInitialized(); // well, I will next line, anyway :-)
|
||||
if (bDidInitPython) {
|
||||
NS_TIMELINE_START_TIMER("PyXPCOM: Python initializing");
|
||||
Py_Initialize(); // NOTE: We never finalize Python!!
|
||||
#ifndef NS_DEBUG
|
||||
Py_OptimizeFlag = 1;
|
||||
#endif // NS_DEBUG
|
||||
// Must force Python to start using thread locks, as
|
||||
// this is certainly a threaded environment we are playing in
|
||||
PyEval_InitThreads();
|
||||
|
||||
NS_TIMELINE_STOP_TIMER("PyXPCOM: Python initializing");
|
||||
NS_TIMELINE_MARK_TIMER("PyXPCOM: Python initializing");
|
||||
}
|
||||
// Get the Python interpreter state
|
||||
NS_TIMELINE_START_TIMER("PyXPCOM: Python threadstate setup");
|
||||
PyGILState_STATE state = PyGILState_Ensure();
|
||||
#ifdef MOZ_TIMELINE
|
||||
// If the timeline service is installed, see if we can install our hooks.
|
||||
if (NULL==PyImport_ImportModule("timeline_hook")) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_ImportError))
|
||||
PyXPCOM_LogError("Failed to import 'timeline_hook'");
|
||||
PyErr_Clear(); // but don't care if we can't.
|
||||
}
|
||||
#endif
|
||||
|
||||
// Make sure we have _something_ as sys.argv.
|
||||
if (PySys_GetObject("argv")==NULL) {
|
||||
PyObject *path = PyList_New(0);
|
||||
PyObject *str = PyString_FromString("");
|
||||
PyList_Append(path, str);
|
||||
PySys_SetObject("argv", path);
|
||||
Py_XDECREF(path);
|
||||
Py_XDECREF(str);
|
||||
}
|
||||
|
||||
// Add the standard extra paths we assume
|
||||
AddStandardPaths();
|
||||
|
||||
// If we initialized Python, then we will also have acquired the thread
|
||||
// lock. In that case, we want to leave it unlocked, so other threads
|
||||
// are free to run, even if they aren't running Python code.
|
||||
PyGILState_Release(bDidInitPython ? PyGILState_UNLOCKED : state);
|
||||
|
||||
NS_TIMELINE_STOP_TIMER("PyXPCOM: Python threadstate setup");
|
||||
NS_TIMELINE_MARK_TIMER("PyXPCOM: Python threadstate setup");
|
||||
bIsInitialized = PR_TRUE;
|
||||
}
|
||||
|
||||
void pyxpcom_construct(void)
|
||||
{
|
||||
// Create the lock we will use to ensure startup thread
|
||||
// safetly, but don't actually initialize Python yet.
|
||||
g_lockMain = PR_NewLock();
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
PRStatus status;
|
||||
status = PR_NewThreadPrivateIndex( &tlsIndex, NULL );
|
||||
NS_ASSERTION(status==0, "Could not allocate TLS storage");
|
||||
if (NS_FAILED(status)) {
|
||||
PR_DestroyLock(g_lockMain);
|
||||
return; // PR_FALSE;
|
||||
}
|
||||
#endif // PYXPCOM_USE_PYGILSTATE
|
||||
return; // PR_TRUE;
|
||||
}
|
||||
|
||||
void pyxpcom_destruct(void)
|
||||
{
|
||||
PR_DestroyLock(g_lockMain);
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
// I can't locate a way to kill this -
|
||||
// should I pass a dtor to PR_NewThreadPrivateIndex??
|
||||
// TlsFree(tlsIndex);
|
||||
#endif // PYXPCOM_USE_PYGILSTATE
|
||||
}
|
||||
|
||||
// Yet another attempt at cross-platform library initialization and finalization.
|
||||
|
@ -271,10 +279,6 @@ PyXPCOM_Globals_Ensure()
|
|||
{
|
||||
PRBool rc = PR_TRUE;
|
||||
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
PyXPCOM_InterpreterState_Ensure();
|
||||
#endif
|
||||
|
||||
// The exception object - we load it from .py code!
|
||||
if (PyXPCOM_Error == NULL) {
|
||||
rc = PR_FALSE;
|
||||
|
|
|
@ -44,79 +44,14 @@
|
|||
// pyxpcom core library and transfers control to that.
|
||||
|
||||
#include <PyXPCOM.h>
|
||||
|
||||
#include "nsDirectoryServiceDefs.h"
|
||||
#include "nsILocalFile.h"
|
||||
|
||||
#include "nspr.h" // PR_fprintf
|
||||
|
||||
#if (PY_VERSION_HEX >= 0x02030000)
|
||||
#define PYXPCOM_USE_PYGILSTATE
|
||||
#endif
|
||||
|
||||
static char *PyTraceback_AsString(PyObject *exc_tb);
|
||||
|
||||
#ifdef XP_WIN
|
||||
#include "windows.h"
|
||||
#endif
|
||||
|
||||
#ifdef XP_UNIX
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#endif
|
||||
|
||||
#include "nsITimelineService.h"
|
||||
#include "nsILocalFile.h"
|
||||
|
||||
typedef nsresult (*pfnPyXPCOM_NSGetModule)(nsIComponentManager *servMgr,
|
||||
nsIFile* location,
|
||||
nsIModule** result);
|
||||
|
||||
|
||||
static void LogError(const char *fmt, ...);
|
||||
static void LogDebug(const char *fmt, ...);
|
||||
|
||||
// Ensure that any paths guaranteed by this package exist on sys.path
|
||||
// Only called once as we are first loaded into the process.
|
||||
void AddStandardPaths()
|
||||
{
|
||||
// Put {bin}\Python on the path if it exists.
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIFile> aFile;
|
||||
rv = NS_GetSpecialDirectory(NS_XPCOM_CURRENT_PROCESS_DIR, getter_AddRefs(aFile));
|
||||
if (NS_FAILED(rv)) {
|
||||
LogError("The Python XPCOM loader could not locate the 'bin' directory\n");
|
||||
return;
|
||||
}
|
||||
aFile->Append(NS_LITERAL_STRING("python"));
|
||||
nsAutoString pathBuf;
|
||||
aFile->GetPath(pathBuf);
|
||||
PyObject *obPath = PySys_GetObject("path");
|
||||
if (!obPath) {
|
||||
LogError("The Python XPCOM loader could not get the Python sys.path variable\n");
|
||||
return;
|
||||
}
|
||||
NS_LossyConvertUTF16toASCII pathCBuf(pathBuf);
|
||||
LogDebug("The Python XPCOM loader is adding '%s' to sys.path\n", pathCBuf.get());
|
||||
PyObject *newStr = PyString_FromString(pathCBuf.get());
|
||||
PyList_Insert(obPath, 0, newStr);
|
||||
Py_XDECREF(newStr);
|
||||
// And now try and get Python to process this directory as a "site dir"
|
||||
// - ie, look for .pth files, etc
|
||||
nsCAutoString cmdBuf(NS_LITERAL_CSTRING("import site;site.addsitedir(r'") + pathCBuf + NS_LITERAL_CSTRING("')\n"));
|
||||
if (0 != PyRun_SimpleString((char *)cmdBuf.get())) {
|
||||
LogError("The directory '%s' could not be added as a site directory", pathCBuf.get());
|
||||
PyErr_Clear();
|
||||
}
|
||||
// and somewhat like Python itself (site, citecustomize), we attempt
|
||||
// to import "sitepyxpcom" ignoring ImportError
|
||||
if (NULL==PyImport_ImportModule("sitepyxpcom")) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_ImportError))
|
||||
LogError("Failed to import 'sitepyxpcom'");
|
||||
PyErr_Clear();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
// This is the main entry point that delegates into Python
|
||||
nsresult PyXPCOM_NSGetModule(nsIComponentManager *servMgr,
|
||||
|
@ -126,20 +61,6 @@ nsresult PyXPCOM_NSGetModule(nsIComponentManager *servMgr,
|
|||
NS_PRECONDITION(result!=NULL, "null result pointer in PyXPCOM_NSGetModule!");
|
||||
NS_PRECONDITION(location!=NULL, "null nsIFile pointer in PyXPCOM_NSGetModule!");
|
||||
NS_PRECONDITION(servMgr!=NULL, "null servMgr pointer in PyXPCOM_NSGetModule!");
|
||||
#ifndef LOADER_LINKS_WITH_PYTHON
|
||||
if (!Py_IsInitialized()) {
|
||||
Py_Initialize();
|
||||
if (!Py_IsInitialized()) {
|
||||
PyXPCOM_LogError("Python initialization failed!\n");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
PyEval_InitThreads();
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
PyXPCOM_InterpreterState_Ensure();
|
||||
#endif
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
#endif // LOADER_LINKS_WITH_PYTHON
|
||||
CEnterLeavePython _celp;
|
||||
PyObject *func = NULL;
|
||||
PyObject *obServMgr = NULL;
|
||||
|
@ -178,268 +99,10 @@ extern "C" NS_EXPORT nsresult NSGetModule(nsIComponentManager *servMgr,
|
|||
nsIFile* location,
|
||||
nsIModule** result)
|
||||
{
|
||||
#if defined(XP_UNIX) && !defined(XP_MACOSX)
|
||||
/* *sob* - seems necessary to open the .so as RTLD_GLOBAL. Without
|
||||
this we see:
|
||||
Traceback (most recent call last):
|
||||
File "<string>", line 1, in ?
|
||||
File "/usr/lib/python2.4/logging/__init__.py", line 29, in ?
|
||||
import sys, os, types, time, string, cStringIO, traceback
|
||||
ImportError: /usr/lib/python2.4/lib-dynload/time.so: undefined
|
||||
symbol: PyExc_IOError
|
||||
|
||||
On osx, ShaneC writes that is it unnecessary (and fails anyway since
|
||||
PYTHON_SO is wrong.) More clues about this welcome!
|
||||
*/
|
||||
|
||||
dlopen(PYTHON_SO,RTLD_NOW | RTLD_GLOBAL);
|
||||
#endif
|
||||
PRBool bDidInitPython = !Py_IsInitialized(); // well, I will next line, anyway :-)
|
||||
if (bDidInitPython) {
|
||||
NS_TIMELINE_START_TIMER("PyXPCOM: Python initializing");
|
||||
Py_Initialize();
|
||||
if (!Py_IsInitialized()) {
|
||||
LogError("Python initialization failed!\n");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
#ifndef NS_DEBUG
|
||||
Py_OptimizeFlag = 1;
|
||||
#endif // NS_DEBUG
|
||||
PyEval_InitThreads();
|
||||
NS_TIMELINE_STOP_TIMER("PyXPCOM: Python initializing");
|
||||
NS_TIMELINE_MARK_TIMER("PyXPCOM: Python initializing");
|
||||
}
|
||||
// Get the Python interpreter state
|
||||
NS_TIMELINE_START_TIMER("PyXPCOM: Python threadstate setup");
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
PyThreadState *threadStateCreated = NULL;
|
||||
PyThreadState *threadState = PyThreadState_Swap(NULL);
|
||||
if (threadState==NULL) {
|
||||
// no thread-state - set one up.
|
||||
// *sigh* - what I consider a bug is that Python
|
||||
// will deadlock unless we own the lock before creating
|
||||
// a new interpreter (it appear Py_NewInterpreter has
|
||||
// really only been tested/used with no thread lock
|
||||
PyEval_AcquireLock();
|
||||
threadState = threadStateCreated = Py_NewInterpreter();
|
||||
PyThreadState_Swap(NULL);
|
||||
}
|
||||
PyEval_ReleaseLock();
|
||||
PyEval_AcquireThread(threadState);
|
||||
#else
|
||||
PyGILState_STATE state = PyGILState_Ensure();
|
||||
#endif // PYXPCOM_USE_PYGILSTATE
|
||||
#ifdef MOZ_TIMELINE
|
||||
// If the timeline service is installed, see if we can install our hooks.
|
||||
if (NULL==PyImport_ImportModule("timeline_hook")) {
|
||||
if (!PyErr_ExceptionMatches(PyExc_ImportError))
|
||||
LogError("Failed to import 'timeline_hook'");
|
||||
PyErr_Clear(); // but don't care if we can't.
|
||||
}
|
||||
#endif
|
||||
// Add the standard paths always - we may not have been the first to
|
||||
// init Python.
|
||||
AddStandardPaths();
|
||||
|
||||
#ifndef PYXPCOM_USE_PYGILSTATE
|
||||
// Abandon the thread-lock, as the first thing Python does
|
||||
// is re-establish the lock (the Python thread-state story SUCKS!!!)
|
||||
if (threadStateCreated) {
|
||||
Py_EndInterpreter(threadStateCreated);
|
||||
PyEval_ReleaseLock(); // see Py_NewInterpreter call above
|
||||
} else {
|
||||
PyEval_ReleaseThread(threadState);
|
||||
PyThreadState *threadStateSave = PyThreadState_Swap(NULL);
|
||||
if (threadStateSave)
|
||||
PyThreadState_Delete(threadStateSave);
|
||||
}
|
||||
#else
|
||||
// If we initialized Python, then we will also have acquired the thread
|
||||
// lock. In that case, we want to leave it unlocked, so other threads
|
||||
// are free to run, even if they aren't running Python code.
|
||||
PyGILState_Release(bDidInitPython ? PyGILState_UNLOCKED : state);
|
||||
#endif
|
||||
|
||||
NS_TIMELINE_STOP_TIMER("PyXPCOM: Python threadstate setup");
|
||||
NS_TIMELINE_MARK_TIMER("PyXPCOM: Python threadstate setup");
|
||||
PyXPCOM_EnsurePythonEnvironment();
|
||||
NS_TIMELINE_START_TIMER("PyXPCOM: PyXPCOM NSGetModule entry point");
|
||||
nsresult rc = PyXPCOM_NSGetModule(servMgr, location, result);
|
||||
NS_TIMELINE_STOP_TIMER("PyXPCOM: PyXPCOM NSGetModule entry point");
|
||||
NS_TIMELINE_MARK_TIMER("PyXPCOM: PyXPCOM NSGetModule entry point");
|
||||
return rc;
|
||||
}
|
||||
|
||||
// The internal helper that actually moves the
|
||||
// formatted string to the target!
|
||||
|
||||
void LogMessage(const char *prefix, const char *pszMessageText)
|
||||
{
|
||||
PR_fprintf(PR_STDERR, "%s", pszMessageText);
|
||||
}
|
||||
|
||||
void LogMessage(const char *prefix, nsACString &text)
|
||||
{
|
||||
LogMessage(prefix, nsPromiseFlatCString(text).get());
|
||||
}
|
||||
|
||||
// A helper for the various logging routines.
|
||||
static void VLogF(const char *prefix, const char *fmt, va_list argptr)
|
||||
{
|
||||
char buff[512];
|
||||
|
||||
vsprintf(buff, fmt, argptr);
|
||||
|
||||
LogMessage(prefix, buff);
|
||||
}
|
||||
|
||||
static void LogError(const char *fmt, ...)
|
||||
{
|
||||
va_list marker;
|
||||
va_start(marker, fmt);
|
||||
VLogF("PyXPCOM Loader Error: ", fmt, marker);
|
||||
// If we have a Python exception, also log that:
|
||||
PyObject *exc_typ = NULL, *exc_val = NULL, *exc_tb = NULL;
|
||||
PyErr_Fetch( &exc_typ, &exc_val, &exc_tb);
|
||||
if (exc_typ) {
|
||||
nsCAutoString streamout;
|
||||
|
||||
if (exc_tb) {
|
||||
const char *szTraceback = PyTraceback_AsString(exc_tb);
|
||||
if (szTraceback == NULL)
|
||||
streamout += "Can't get the traceback info!";
|
||||
else {
|
||||
streamout += "Traceback (most recent call last):\n";
|
||||
streamout += szTraceback;
|
||||
PyMem_Free((void *)szTraceback);
|
||||
}
|
||||
}
|
||||
PyObject *temp = PyObject_Str(exc_typ);
|
||||
if (temp) {
|
||||
streamout += PyString_AsString(temp);
|
||||
Py_DECREF(temp);
|
||||
} else
|
||||
streamout += "Can convert exception to a string!";
|
||||
streamout += ": ";
|
||||
if (exc_val != NULL) {
|
||||
temp = PyObject_Str(exc_val);
|
||||
if (temp) {
|
||||
streamout += PyString_AsString(temp);
|
||||
Py_DECREF(temp);
|
||||
} else
|
||||
streamout += "Can convert exception value to a string!";
|
||||
}
|
||||
streamout += "\n";
|
||||
LogMessage("PyXPCOM Exception:", streamout);
|
||||
}
|
||||
PyErr_Restore(exc_typ, exc_val, exc_tb);
|
||||
}
|
||||
/*** - not currently used - silence compiler warning.
|
||||
static void LogWarning(const char *fmt, ...)
|
||||
{
|
||||
va_list marker;
|
||||
va_start(marker, fmt);
|
||||
VLogF("PyXPCOM Loader Warning: ", fmt, marker);
|
||||
}
|
||||
***/
|
||||
#ifdef DEBUG
|
||||
static void LogDebug(const char *fmt, ...)
|
||||
{
|
||||
va_list marker;
|
||||
va_start(marker, fmt);
|
||||
VLogF("PyXPCOM Loader Debug: ", fmt, marker);
|
||||
}
|
||||
#else
|
||||
static void LogDebug(const char *fmt, ...)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Obtains a string from a Python traceback.
|
||||
This is the exact same string as "traceback.print_exc" would return.
|
||||
|
||||
Pass in a Python traceback object (probably obtained from PyErr_Fetch())
|
||||
Result is a string which must be free'd using PyMem_Free()
|
||||
*/
|
||||
#define TRACEBACK_FETCH_ERROR(what) {errMsg = what; goto done;}
|
||||
|
||||
char *PyTraceback_AsString(PyObject *exc_tb)
|
||||
{
|
||||
char *errMsg = NULL; /* a static that hold a local error message */
|
||||
char *result = NULL; /* a valid, allocated result. */
|
||||
PyObject *modStringIO = NULL;
|
||||
PyObject *modTB = NULL;
|
||||
PyObject *obFuncStringIO = NULL;
|
||||
PyObject *obStringIO = NULL;
|
||||
PyObject *obFuncTB = NULL;
|
||||
PyObject *argsTB = NULL;
|
||||
PyObject *obResult = NULL;
|
||||
|
||||
/* Import the modules we need - cStringIO and traceback */
|
||||
modStringIO = PyImport_ImportModule("cStringIO");
|
||||
if (modStringIO==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant import cStringIO\n");
|
||||
|
||||
modTB = PyImport_ImportModule("traceback");
|
||||
if (modTB==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant import traceback\n");
|
||||
/* Construct a cStringIO object */
|
||||
obFuncStringIO = PyObject_GetAttrString(modStringIO, "StringIO");
|
||||
if (obFuncStringIO==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant find cStringIO.StringIO\n");
|
||||
obStringIO = PyObject_CallObject(obFuncStringIO, NULL);
|
||||
if (obStringIO==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cStringIO.StringIO() failed\n");
|
||||
/* Get the traceback.print_exception function, and call it. */
|
||||
obFuncTB = PyObject_GetAttrString(modTB, "print_tb");
|
||||
if (obFuncTB==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant find traceback.print_tb\n");
|
||||
|
||||
argsTB = Py_BuildValue("OOO",
|
||||
exc_tb ? exc_tb : Py_None,
|
||||
Py_None,
|
||||
obStringIO);
|
||||
if (argsTB==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant make print_tb arguments\n");
|
||||
|
||||
obResult = PyObject_CallObject(obFuncTB, argsTB);
|
||||
if (obResult==NULL)
|
||||
TRACEBACK_FETCH_ERROR("traceback.print_tb() failed\n");
|
||||
/* Now call the getvalue() method in the StringIO instance */
|
||||
Py_DECREF(obFuncStringIO);
|
||||
obFuncStringIO = PyObject_GetAttrString(obStringIO, "getvalue");
|
||||
if (obFuncStringIO==NULL)
|
||||
TRACEBACK_FETCH_ERROR("cant find getvalue function\n");
|
||||
Py_DECREF(obResult);
|
||||
obResult = PyObject_CallObject(obFuncStringIO, NULL);
|
||||
if (obResult==NULL)
|
||||
TRACEBACK_FETCH_ERROR("getvalue() failed.\n");
|
||||
|
||||
/* And it should be a string all ready to go - duplicate it. */
|
||||
if (!PyString_Check(obResult))
|
||||
TRACEBACK_FETCH_ERROR("getvalue() did not return a string\n");
|
||||
|
||||
{ // a temp scope so I can use temp locals.
|
||||
char *tempResult = PyString_AsString(obResult);
|
||||
result = (char *)PyMem_Malloc(strlen(tempResult)+1);
|
||||
if (result==NULL)
|
||||
TRACEBACK_FETCH_ERROR("memory error duplicating the traceback string");
|
||||
|
||||
strcpy(result, tempResult);
|
||||
} // end of temp scope.
|
||||
done:
|
||||
/* All finished - first see if we encountered an error */
|
||||
if (result==NULL && errMsg != NULL) {
|
||||
result = (char *)PyMem_Malloc(strlen(errMsg)+1);
|
||||
if (result != NULL)
|
||||
/* if it does, not much we can do! */
|
||||
strcpy(result, errMsg);
|
||||
}
|
||||
Py_XDECREF(modStringIO);
|
||||
Py_XDECREF(modTB);
|
||||
Py_XDECREF(obFuncStringIO);
|
||||
Py_XDECREF(obStringIO);
|
||||
Py_XDECREF(obFuncTB);
|
||||
Py_XDECREF(argsTB);
|
||||
Py_XDECREF(obResult);
|
||||
return result;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче