Pcbnew: Python scripting support clean up.
Always check the return value of PyRun_SimpleString() for errors when a Python script is run and show an error message rather than fail silently. Enable Python interpreter I/O redirection in debug builds so that script string errors will be shown when PyRun_SimpleString() is called. Do not call PyErr_Print() after a PyRun_SimpleString() call failure. It doesn't do anything useful. Do not call Py_Finalize() after a PyRun_SimpleString() call failure. It seems to cause Pcbnew to crash.
This commit is contained in:
parent
4ae5a006d0
commit
816f6db310
|
@ -303,19 +303,22 @@ static bool scriptingSetup()
|
|||
|
||||
void PythonPluginsReloadBase()
|
||||
{
|
||||
#if defined(KICAD_SCRIPTING)
|
||||
//Reload plugin list: reload Python plugins if they are newer than
|
||||
#if defined( KICAD_SCRIPTING )
|
||||
// Reload plugin list: reload Python plugins if they are newer than
|
||||
// the already loaded, and load new plugins
|
||||
char cmd[1024];
|
||||
|
||||
snprintf( cmd, sizeof(cmd),
|
||||
"pcbnew.LoadPlugins(\"%s\")", TO_UTF8( PyScriptingPath() ) );
|
||||
snprintf( cmd, sizeof( cmd ),
|
||||
"pcbnew.LoadPlugins(\"%s\")", TO_UTF8( PyScriptingPath() ) );
|
||||
|
||||
PyLOCK lock;
|
||||
|
||||
// ReRun the Python method pcbnew.LoadPlugins
|
||||
// (already called when starting Pcbnew)
|
||||
PyRun_SimpleString( cmd );
|
||||
int retv = PyRun_SimpleString( cmd );
|
||||
|
||||
if( retv != 0 )
|
||||
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
|
||||
* Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -62,7 +62,9 @@ extern "C" void init_pcbnew( void );
|
|||
struct _inittab* SwigImportInittab;
|
||||
static int SwigNumModules = 0;
|
||||
|
||||
static bool wxPythonLoaded = false; // true if the wxPython scripting layer was successfully loaded
|
||||
/// True if the wxPython scripting layer was successfully loaded.
|
||||
static bool wxPythonLoaded = false;
|
||||
|
||||
|
||||
bool IsWxPythonLoaded()
|
||||
{
|
||||
|
@ -70,7 +72,9 @@ bool IsWxPythonLoaded()
|
|||
}
|
||||
|
||||
|
||||
/* Add a name + initfuction to our SwigImportInittab */
|
||||
/**
|
||||
* Add a name + initfuction to our SwigImportInittab
|
||||
*/
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
static void swigAddModule( const char* name, PyObject* (* initfunc)() )
|
||||
|
@ -86,8 +90,9 @@ static void swigAddModule( const char* name, void (* initfunc)() )
|
|||
}
|
||||
|
||||
|
||||
/* Add the builtin python modules */
|
||||
|
||||
/**
|
||||
* Add the builtin python modules
|
||||
*/
|
||||
static void swigAddBuiltin()
|
||||
{
|
||||
int i = 0;
|
||||
|
@ -111,12 +116,9 @@ static void swigAddBuiltin()
|
|||
}
|
||||
|
||||
|
||||
/* Function swigAddModules
|
||||
* adds the internal modules we offer to the python scripting, so they will be
|
||||
* available to the scripts we run.
|
||||
*
|
||||
/**
|
||||
* Add the internal modules to the python scripting so they will be available to the scripts.
|
||||
*/
|
||||
|
||||
static void swigAddModules()
|
||||
{
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
@ -132,27 +134,28 @@ static void swigAddModules()
|
|||
}
|
||||
|
||||
|
||||
/* Function swigSwitchPythonBuiltin
|
||||
* switches python module table to our built one .
|
||||
*
|
||||
/**
|
||||
* Switch the python module table to the Pcbnew built one.
|
||||
*/
|
||||
|
||||
static void swigSwitchPythonBuiltin()
|
||||
{
|
||||
PyImport_Inittab = SwigImportInittab;
|
||||
}
|
||||
|
||||
|
||||
/* Function pcbnewInitPythonScripting
|
||||
* Initializes all the python environment and publish our interface inside it
|
||||
* initializes all the wxpython interface, and returns the python thread control structure
|
||||
*
|
||||
*/
|
||||
|
||||
PyThreadState* g_PythonMainTState;
|
||||
|
||||
|
||||
/**
|
||||
* Initialize the python environment and publish the Pcbnew interface inside it.
|
||||
*
|
||||
* This initializes all the wxPython interface and returns the python thread control structure
|
||||
*/
|
||||
bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
||||
{
|
||||
int retv;
|
||||
char cmd[1024];
|
||||
|
||||
swigAddBuiltin(); // add builtin functions
|
||||
swigAddModules(); // add our own modules
|
||||
swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list
|
||||
|
@ -164,20 +167,17 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
PyEval_InitThreads();
|
||||
|
||||
#ifndef KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
|
||||
char cmd[1024];
|
||||
#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
|
||||
// Make sure that that the correct version of wxPython is loaded. In systems where there
|
||||
// are different versions of wxPython installed this can lead to select wrong wxPython
|
||||
// version being selected.
|
||||
snprintf( cmd, sizeof( cmd ), "import wxversion; wxversion.select( '%s' )", WXPYTHON_VERSION );
|
||||
|
||||
int retv = PyRun_SimpleString( cmd );
|
||||
retv = PyRun_SimpleString( cmd );
|
||||
|
||||
if( retv != 0 )
|
||||
{
|
||||
wxLogError( wxT( "Python error %d occurred running string `%s`" ), retv, cmd );
|
||||
PyErr_Print();
|
||||
Py_Finalize();
|
||||
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
|
||||
return false;
|
||||
}
|
||||
#endif // ifndef __WINDOWS__
|
||||
|
@ -187,13 +187,17 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
// internally by the rest of the API functions.
|
||||
if( !wxPyCoreAPI_IMPORT() )
|
||||
{
|
||||
wxLogError( wxT( "***** Error importing the wxPython API! *****" ) );
|
||||
wxLogError( "***** Error importing the wxPython API! *****" );
|
||||
PyErr_Print();
|
||||
Py_Finalize();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined( DEBUG )
|
||||
RedirectStdio();
|
||||
#endif
|
||||
|
||||
wxPythonLoaded = true;
|
||||
|
||||
// Save the current Python thread state and release the
|
||||
|
@ -202,15 +206,18 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
|
||||
#endif // ifdef KICAD_SCRIPTING_WXPYTHON
|
||||
|
||||
// load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
|
||||
// Load pcbnew inside Python and load all the user plugins, TODO: add system wide plugins
|
||||
{
|
||||
char loadCmd[1024];
|
||||
PyLOCK lock;
|
||||
snprintf( loadCmd, sizeof(loadCmd), "import sys, traceback\n"
|
||||
"sys.path.append(\".\")\n"
|
||||
"import pcbnew\n"
|
||||
"pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath );
|
||||
PyRun_SimpleString( loadCmd );
|
||||
|
||||
snprintf( cmd, sizeof( cmd ), "import sys, traceback\n"
|
||||
"sys.path.append(\".\")\n"
|
||||
"import pcbnew\n"
|
||||
"pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath );
|
||||
retv = PyRun_SimpleString( cmd );
|
||||
|
||||
if( retv != 0 )
|
||||
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -218,9 +225,10 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
|
||||
|
||||
/**
|
||||
* this function runs a python method from pcbnew module, which returns a string
|
||||
* Run a python method from the pcbnew module.
|
||||
*
|
||||
* @param aMethodName is the name of the method (like "pcbnew.myfunction" )
|
||||
* @param aNames will contains the returned string
|
||||
* @param aNames will contain the returned string
|
||||
*/
|
||||
static void pcbnewRunPythonMethodWithReturnedString( const char* aMethodName, wxString& aNames )
|
||||
{
|
||||
|
@ -253,9 +261,11 @@ static void pcbnewRunPythonMethodWithReturnedString( const char* aMethodName, wx
|
|||
PyObject* str = PyDict_GetItemString(localDict, "result" );
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
const char* str_res = NULL;
|
||||
|
||||
if(str)
|
||||
{
|
||||
PyObject* temp_bytes = PyUnicode_AsEncodedString( str, "UTF-8", "strict" );
|
||||
|
||||
if( temp_bytes != NULL )
|
||||
{
|
||||
str_res = PyBytes_AS_STRING( temp_bytes );
|
||||
|
@ -277,7 +287,7 @@ static void pcbnewRunPythonMethodWithReturnedString( const char* aMethodName, wx
|
|||
Py_DECREF( localDict );
|
||||
|
||||
if( PyErr_Occurred() )
|
||||
wxLogMessage(PyErrStringWithTraceback());
|
||||
wxLogMessage( PyErrStringWithTraceback() );
|
||||
}
|
||||
|
||||
|
||||
|
@ -292,6 +302,7 @@ void pcbnewGetScriptsSearchPaths( wxString& aNames )
|
|||
pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsSearchPaths", aNames );
|
||||
}
|
||||
|
||||
|
||||
void pcbnewGetWizardsBackTrace( wxString& aNames )
|
||||
{
|
||||
pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsBackTrace", aNames );
|
||||
|
@ -308,7 +319,6 @@ void pcbnewFinishPythonScripting()
|
|||
|
||||
|
||||
#if defined( KICAD_SCRIPTING_WXPYTHON )
|
||||
|
||||
void RedirectStdio()
|
||||
{
|
||||
// This is a helpful little tidbit to help debugging and such. It
|
||||
|
@ -322,7 +332,10 @@ void RedirectStdio()
|
|||
|
||||
PyLOCK lock;
|
||||
|
||||
PyRun_SimpleString( python_redirect );
|
||||
int retv = PyRun_SimpleString( python_redirect );
|
||||
|
||||
if( retv != 0 )
|
||||
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, python_redirect );
|
||||
}
|
||||
|
||||
|
||||
|
@ -370,7 +383,8 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
|
|||
auto app_ptr = wxTheApp;
|
||||
#endif
|
||||
// Execute the code to make the makeWindow function we defined above
|
||||
PyObject* result = PyRun_String( pcbnew_pyshell_one_step.str().c_str(), Py_file_input, globals, globals );
|
||||
PyObject* result = PyRun_String( pcbnew_pyshell_one_step.str().c_str(), Py_file_input,
|
||||
globals, globals );
|
||||
|
||||
#ifdef KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
// This absolutely ugly hack is to work around the pyshell re-writing the global
|
||||
|
@ -395,7 +409,7 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
|
|||
if( !PyInt_Check( result ) )
|
||||
#endif
|
||||
{
|
||||
wxLogError("creation of scripting window didn't return a number");
|
||||
wxLogError( "creation of scripting window didn't return a number" );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -414,16 +428,15 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
|
|||
|
||||
if( !window )
|
||||
{
|
||||
wxLogError("unable to find pyshell window with id %d", windowId);
|
||||
wxLogError( "unable to find pyshell window with id %d", windowId );
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
wxString PyStringToWx( PyObject* aString )
|
||||
{
|
||||
wxString ret;
|
||||
|
@ -434,6 +447,7 @@ wxString PyStringToWx( PyObject* aString )
|
|||
#if PY_MAJOR_VERSION >= 3
|
||||
const char* str_res = NULL;
|
||||
PyObject* temp_bytes = PyUnicode_AsEncodedString( aString, "UTF-8", "strict" );
|
||||
|
||||
if( temp_bytes != NULL )
|
||||
{
|
||||
str_res = PyBytes_AS_STRING( temp_bytes );
|
||||
|
@ -471,6 +485,7 @@ wxArrayString PyArrayStringToWx( PyObject* aArrayString )
|
|||
#if PY_MAJOR_VERSION >= 3
|
||||
const char* str_res = NULL;
|
||||
PyObject* temp_bytes = PyUnicode_AsEncodedString( element, "UTF-8", "strict" );
|
||||
|
||||
if( temp_bytes != NULL )
|
||||
{
|
||||
str_res = PyBytes_AS_STRING( temp_bytes );
|
||||
|
@ -505,6 +520,7 @@ wxString PyErrStringWithTraceback()
|
|||
PyErr_Fetch( &type, &value, &traceback );
|
||||
|
||||
PyErr_NormalizeException( &type, &value, &traceback );
|
||||
|
||||
if( traceback == NULL )
|
||||
{
|
||||
traceback = Py_None;
|
||||
|
@ -545,14 +561,15 @@ wxString PyErrStringWithTraceback()
|
|||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Find the Python scripting path
|
||||
* Find the Python scripting path.
|
||||
*/
|
||||
wxString PyScriptingPath()
|
||||
{
|
||||
wxString path;
|
||||
|
||||
//TODO should this be a user configurable variable eg KISCRIPT ?
|
||||
//@todo This should this be a user configurable variable eg KISCRIPT?
|
||||
#if defined( __WXMAC__ )
|
||||
path = GetOSXKicadDataDir() + wxT( "/scripting" );
|
||||
#else
|
||||
|
@ -571,6 +588,7 @@ wxString PyScriptingPath()
|
|||
return path;
|
||||
}
|
||||
|
||||
|
||||
wxString PyPluginsPath()
|
||||
{
|
||||
// Note we are using unix path separator, because window separator sometimes
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* This program source code file is part of KiCad, a free EDA CAD application.
|
||||
*
|
||||
* Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
* Copyright (C) 1992-2019 KiCad Developers, see AUTHORS.txt for contributors.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -50,31 +50,30 @@
|
|||
#include <wx/string.h>
|
||||
#include <wx/arrstr.h>
|
||||
|
||||
|
||||
/**
|
||||
* Function pcbnewInitPythonScripting
|
||||
* Initializes the Python engine inside pcbnew
|
||||
* Initialize the Python engine inside pcbnew.
|
||||
*/
|
||||
bool pcbnewInitPythonScripting( const char * aUserScriptingPath );
|
||||
void pcbnewFinishPythonScripting();
|
||||
|
||||
/**
|
||||
* Function pcbnewGetUnloadableScriptNames
|
||||
* collects the list of python scripts which could not be loaded because
|
||||
* some error (synthax error) happened
|
||||
* Collect the list of python scripts which could not be loaded.
|
||||
*
|
||||
* @param aNames is a wxString which will contain the filenames (separated by '\n')
|
||||
*/
|
||||
void pcbnewGetUnloadableScriptNames( wxString& aNames );
|
||||
|
||||
/**
|
||||
* Function pcbnewGetScriptsSearchPaths
|
||||
* collects the list of paths where python scripts are searched
|
||||
* Collect the list of paths where python scripts are searched.
|
||||
*
|
||||
* @param aNames is a wxString which will contain the paths (separated by '\n')
|
||||
*/
|
||||
void pcbnewGetScriptsSearchPaths( wxString& aNames );
|
||||
|
||||
/**
|
||||
* Function pcbnewGetWizardsBackTrace
|
||||
* returns the backtrace of errors (if any) when wizard python scripts are loaded
|
||||
* Return the backtrace of errors (if any) when wizard python scripts are loaded.
|
||||
*
|
||||
* @param aNames is a wxString which will contain the trace
|
||||
*/
|
||||
void pcbnewGetWizardsBackTrace( wxString& aNames );
|
||||
|
@ -84,7 +83,6 @@ void RedirectStdio();
|
|||
wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId );
|
||||
#endif
|
||||
|
||||
|
||||
#if 0 && defined (KICAD_SCRIPTING_WXPYTHON)
|
||||
// This definition of PyLOCK crashed Pcbnew under some conditions (JPC),
|
||||
// especially reloading plugins
|
||||
|
@ -98,8 +96,6 @@ public:
|
|||
PyLOCK() { b = wxPyBeginBlockThreads(); }
|
||||
~PyLOCK() { wxPyEndBlockThreads( b ); }
|
||||
};
|
||||
|
||||
|
||||
#else
|
||||
class PyLOCK
|
||||
{
|
||||
|
@ -108,7 +104,6 @@ public:
|
|||
PyLOCK() { gil_state = PyGILState_Ensure(); }
|
||||
~PyLOCK() { PyGILState_Release( gil_state ); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
wxString PyStringToWx( PyObject* str );
|
||||
|
|
Loading…
Reference in New Issue