KiCad PCB EDA Suite
python_scripting.cpp File Reference

methods to add scripting capabilities inside pcbnew More...

#include <cstdlib>
#include <cstring>
#include <python_scripting.h>
#include <sstream>
#include <fctsys.h>
#include <eda_base_frame.h>
#include <common.h>
#include <gal/color4d.h>
#include <macros.h>
#include <pgm_base.h>

Go to the source code of this file.

Macros

#define EXTRA_PYTHON_MODULES   10
 

Functions

void init_kicad (void)
 
void init_pcbnew (void)
 
bool IsWxPythonLoaded ()
 
static void swigAddModule (const char *name, void(*initfunc)())
 Add a name + initfuction to our SwigImportInittab. More...
 
static void swigAddBuiltin ()
 Add the builtin python modules. More...
 
static void swigAddModules ()
 Add the internal modules to the python scripting so they will be available to the scripts. More...
 
static void swigSwitchPythonBuiltin ()
 Switch the python module table to the Pcbnew built one. More...
 
bool pcbnewInitPythonScripting (const char *aUserScriptingPath)
 Initialize the python environment and publish the Pcbnew interface inside it. More...
 
static void pcbnewRunPythonMethodWithReturnedString (const char *aMethodName, wxString &aNames)
 Run a python method from the pcbnew module. More...
 
void pcbnewGetUnloadableScriptNames (wxString &aNames)
 Collect the list of python scripts which could not be loaded. More...
 
void pcbnewGetScriptsSearchPaths (wxString &aNames)
 Collect the list of paths where python scripts are searched. More...
 
void pcbnewGetWizardsBackTrace (wxString &aNames)
 Return the backtrace of errors (if any) when wizard python scripts are loaded. More...
 
void pcbnewFinishPythonScripting ()
 
wxString PyStringToWx (PyObject *aString)
 
wxArrayString PyArrayStringToWx (PyObject *aArrayString)
 
wxString PyErrStringWithTraceback ()
 
wxString PyScriptingPath ()
 Find the Python scripting path. More...
 
wxString PyPluginsPath ()
 

Variables

struct _inittab * SwigImportInittab
 
static int SwigNumModules = 0
 
static bool wxPythonLoaded = false
 True if the wxPython scripting layer was successfully loaded. More...
 
PyThreadState * g_PythonMainTState
 

Detailed Description

methods to add scripting capabilities inside pcbnew

Definition in file python_scripting.cpp.

Macro Definition Documentation

◆ EXTRA_PYTHON_MODULES

#define EXTRA_PYTHON_MODULES   10

Definition at line 53 of file python_scripting.cpp.

Function Documentation

◆ init_kicad()

void init_kicad ( void  )

◆ init_pcbnew()

void init_pcbnew ( void  )

Referenced by swigAddModules().

◆ IsWxPythonLoaded()

bool IsWxPythonLoaded ( )

Definition at line 69 of file python_scripting.cpp.

70 {
71  return wxPythonLoaded;
72 }
static bool wxPythonLoaded
True if the wxPython scripting layer was successfully loaded.

References wxPythonLoaded.

Referenced by PCB::IFACE::OnKifaceEnd(), PCB_EDIT_FRAME::ReCreateHToolbar(), and PCB_EDIT_FRAME::SyncToolbars().

◆ pcbnewFinishPythonScripting()

void pcbnewFinishPythonScripting ( )

Definition at line 312 of file python_scripting.cpp.

313 {
314 #ifdef KICAD_SCRIPTING_WXPYTHON
315  PyEval_RestoreThread( g_PythonMainTState );
316 #endif
317  Py_Finalize();
318 }
PyThreadState * g_PythonMainTState

References g_PythonMainTState.

Referenced by PCB::IFACE::OnKifaceEnd().

◆ pcbnewGetScriptsSearchPaths()

void pcbnewGetScriptsSearchPaths ( wxString &  aNames)

Collect the list of paths where python scripts are searched.

Parameters
aNamesis a wxString which will contain the paths (separated by '
')

Definition at line 300 of file python_scripting.cpp.

301 {
302  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsSearchPaths", aNames );
303 }
static void pcbnewRunPythonMethodWithReturnedString(const char *aMethodName, wxString &aNames)
Run a python method from the pcbnew module.

References pcbnewRunPythonMethodWithReturnedString().

◆ pcbnewGetUnloadableScriptNames()

void pcbnewGetUnloadableScriptNames ( wxString &  aNames)

Collect the list of python scripts which could not be loaded.

Parameters
aNamesis a wxString which will contain the filenames (separated by '
')

Definition at line 294 of file python_scripting.cpp.

295 {
296  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetUnLoadableWizards", aNames );
297 }
static void pcbnewRunPythonMethodWithReturnedString(const char *aMethodName, wxString &aNames)
Run a python method from the pcbnew module.

References pcbnewRunPythonMethodWithReturnedString().

◆ pcbnewGetWizardsBackTrace()

void pcbnewGetWizardsBackTrace ( wxString &  aNames)

Return the backtrace of errors (if any) when wizard python scripts are loaded.

Parameters
aNamesis a wxString which will contain the trace

Definition at line 306 of file python_scripting.cpp.

307 {
308  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsBackTrace", aNames );
309 }
static void pcbnewRunPythonMethodWithReturnedString(const char *aMethodName, wxString &aNames)
Run a python method from the pcbnew module.

References pcbnewRunPythonMethodWithReturnedString().

◆ pcbnewInitPythonScripting()

bool pcbnewInitPythonScripting ( const char *  aUserScriptingPath)

Initialize the python environment and publish the Pcbnew interface inside it.

Initialize the Python engine inside pcbnew.

This initializes all the wxPython interface and returns the python thread control structure

Definition at line 154 of file python_scripting.cpp.

155 {
156  int retv;
157  char cmd[1024];
158 
159  swigAddBuiltin(); // add builtin functions
160  swigAddModules(); // add our own modules
161  swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list
162 
163  Py_Initialize();
164  PySys_SetArgv( Pgm().App().argc, Pgm().App().argv );
165 
166 #ifdef KICAD_SCRIPTING_WXPYTHON
167  PyEval_InitThreads();
168 
169 #ifndef KICAD_SCRIPTING_WXPYTHON_PHOENIX
170 #ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
171  // Make sure that that the correct version of wxPython is loaded. In systems where there
172  // are different versions of wxPython installed this can lead to select wrong wxPython
173  // version being selected.
174  snprintf( cmd, sizeof( cmd ), "import wxversion; wxversion.select( '%s' )", WXPYTHON_VERSION );
175 
176  retv = PyRun_SimpleString( cmd );
177 
178  if( retv != 0 )
179  {
180  wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
181  return false;
182  }
183 #endif // ifndef __WINDOWS__
184 
185  // Load the wxPython core API. Imports the wx._core_ module and sets a
186  // local pointer to a function table located there. The pointer is used
187  // internally by the rest of the API functions.
188  if( !wxPyCoreAPI_IMPORT() )
189  {
190  wxLogError( "***** Error importing the wxPython API! *****" );
191  PyErr_Print();
192  Py_Finalize();
193  return false;
194  }
195 #endif
196 
197 #if defined( DEBUG )
198  RedirectStdio();
199 #endif
200 
201  wxPythonLoaded = true;
202 
203  // Save the current Python thread state and release the
204  // Global Interpreter Lock.
205  g_PythonMainTState = PyEval_SaveThread();
206 
207 #endif // ifdef KICAD_SCRIPTING_WXPYTHON
208 
209  // Load pcbnew inside Python and load all the user plugins, TODO: add system wide plugins
210  {
211  PyLOCK lock;
212 
213  snprintf( cmd, sizeof( cmd ), "import sys, traceback\n"
214  "sys.path.append(\".\")\n"
215  "import pcbnew\n"
216  "pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath );
217  retv = PyRun_SimpleString( cmd );
218 
219  if( retv != 0 )
220  wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
221  }
222 
223  return true;
224 }
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104
static void swigAddBuiltin()
Add the builtin python modules.
static void swigAddModules()
Add the internal modules to the python scripting so they will be available to the scripts.
static void swigSwitchPythonBuiltin()
Switch the python module table to the Pcbnew built one.
static bool wxPythonLoaded
True if the wxPython scripting layer was successfully loaded.
PyThreadState * g_PythonMainTState

References g_PythonMainTState, Pgm(), swigAddBuiltin(), swigAddModules(), swigSwitchPythonBuiltin(), and wxPythonLoaded.

◆ pcbnewRunPythonMethodWithReturnedString()

static void pcbnewRunPythonMethodWithReturnedString ( const char *  aMethodName,
wxString &  aNames 
)
static

Run a python method from the pcbnew module.

Parameters
aMethodNameis the name of the method (like "pcbnew.myfunction" )
aNameswill contain the returned string

Definition at line 233 of file python_scripting.cpp.

234 {
235  aNames.Clear();
236 
237  PyLOCK lock;
238  PyErr_Clear();
239 
240  PyObject* builtins = PyImport_ImportModule( "pcbnew" );
241  wxASSERT( builtins );
242 
243  if( !builtins ) // Something is wrong in pcbnew.py module (incorrect version?)
244  return;
245 
246  PyObject* globals = PyDict_New();
247  PyDict_SetItemString( globals, "pcbnew", builtins );
248  Py_DECREF( builtins );
249 
250  // Build the python code
251  char cmd[1024];
252  snprintf( cmd, sizeof(cmd), "result = %s()", aMethodName );
253 
254  // Execute the python code and get the returned data
255  PyObject* localDict = PyDict_New();
256  PyObject* pobj = PyRun_String( cmd, Py_file_input, globals, localDict);
257  Py_DECREF( globals );
258 
259  if( pobj )
260  {
261  PyObject* str = PyDict_GetItemString(localDict, "result" );
262 #if PY_MAJOR_VERSION >= 3
263  const char* str_res = NULL;
264 
265  if(str)
266  {
267  PyObject* temp_bytes = PyUnicode_AsEncodedString( str, "UTF-8", "strict" );
268 
269  if( temp_bytes != NULL )
270  {
271  str_res = PyBytes_AS_STRING( temp_bytes );
272  str_res = strdup( str_res );
273  Py_DECREF( temp_bytes );
274  }
275  else
276  {
277  wxLogMessage( "cannot encode unicode python string" );
278  }
279  }
280 #else
281  const char* str_res = str ? PyString_AsString( str ) : 0;
282 #endif
283  aNames = FROM_UTF8( str_res );
284  Py_DECREF( pobj );
285  }
286 
287  Py_DECREF( localDict );
288 
289  if( PyErr_Occurred() )
290  wxLogMessage( PyErrStringWithTraceback() );
291 }
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
#define NULL
wxString PyErrStringWithTraceback()

References FROM_UTF8(), NULL, and PyErrStringWithTraceback().

Referenced by pcbnewGetScriptsSearchPaths(), pcbnewGetUnloadableScriptNames(), and pcbnewGetWizardsBackTrace().

◆ PyArrayStringToWx()

wxArrayString PyArrayStringToWx ( PyObject *  aArrayString)

Definition at line 470 of file python_scripting.cpp.

471 {
472  wxArrayString ret;
473 
474  if( !aArrayString )
475  return ret;
476 
477  int list_size = PyList_Size( aArrayString );
478 
479  for( int n = 0; n < list_size; n++ )
480  {
481  PyObject* element = PyList_GetItem( aArrayString, n );
482 
483  if( element )
484  {
485 #if PY_MAJOR_VERSION >= 3
486  const char* str_res = NULL;
487  PyObject* temp_bytes = PyUnicode_AsEncodedString( element, "UTF-8", "strict" );
488 
489  if( temp_bytes != NULL )
490  {
491  str_res = PyBytes_AS_STRING( temp_bytes );
492  ret.Add( FROM_UTF8( str_res ), 1 );
493  Py_DECREF( temp_bytes );
494  }
495  else
496  {
497  wxLogMessage( "cannot encode unicode python string" );
498  }
499 #else
500  ret.Add( FROM_UTF8( PyString_AsString( element ) ), 1 );
501 #endif
502  }
503  }
504 
505  return ret;
506 }
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
#define NULL

References FROM_UTF8(), and NULL.

Referenced by PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod(), and PyErrStringWithTraceback().

◆ PyErrStringWithTraceback()

wxString PyErrStringWithTraceback ( )

Definition at line 509 of file python_scripting.cpp.

510 {
511  wxString err;
512 
513  if( !PyErr_Occurred() )
514  return err;
515 
516  PyObject* type;
517  PyObject* value;
518  PyObject* traceback;
519 
520  PyErr_Fetch( &type, &value, &traceback );
521 
522  PyErr_NormalizeException( &type, &value, &traceback );
523 
524  if( traceback == NULL )
525  {
526  traceback = Py_None;
527  Py_INCREF( traceback );
528  }
529 
530 #if PY_MAJOR_VERSION >= 3
531  PyException_SetTraceback( value, traceback );
532 
533  PyObject* tracebackModuleString = PyUnicode_FromString( "traceback" );
534 #else
535  PyObject* tracebackModuleString = PyString_FromString( "traceback" );
536 #endif
537  PyObject* tracebackModule = PyImport_Import( tracebackModuleString );
538  Py_DECREF( tracebackModuleString );
539 
540  PyObject* formatException = PyObject_GetAttrString( tracebackModule,
541  "format_exception" );
542  Py_DECREF( tracebackModule );
543 
544  PyObject* args = Py_BuildValue( "(O,O,O)", type, value, traceback );
545  PyObject* result = PyObject_CallObject( formatException, args );
546  Py_XDECREF( formatException );
547  Py_XDECREF( args );
548  Py_XDECREF( type );
549  Py_XDECREF( value );
550  Py_XDECREF( traceback );
551 
552  wxArrayString res = PyArrayStringToWx( result );
553 
554  for( unsigned i = 0; i<res.Count(); i++ )
555  {
556  err += res[i] + wxT( "\n" );
557  }
558 
559  PyErr_Clear();
560 
561  return err;
562 }
#define NULL
wxArrayString PyArrayStringToWx(PyObject *aArrayString)

References NULL, and PyArrayStringToWx().

Referenced by PYTHON_ACTION_PLUGIN::CallMethod(), PYTHON_FOOTPRINT_WIZARD::CallMethod(), and pcbnewRunPythonMethodWithReturnedString().

◆ PyPluginsPath()

wxString PyPluginsPath ( )

Definition at line 592 of file python_scripting.cpp.

593 {
594  // Note we are using unix path separator, because window separator sometimes
595  // creates issues when passing a command string to a python method by PyRun_SimpleString
596  return PyScriptingPath() + '/' + "plugins";
597 }
wxString PyScriptingPath()
Find the Python scripting path.

References PyScriptingPath().

◆ PyScriptingPath()

wxString PyScriptingPath ( )

Find the Python scripting path.

Definition at line 568 of file python_scripting.cpp.

569 {
570  wxString path;
571 
572  //@todo This should this be a user configurable variable eg KISCRIPT?
573 #if defined( __WXMAC__ )
574  path = GetOSXKicadDataDir() + wxT( "/scripting" );
575 #else
576  path = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
577 #endif
578 
579  wxFileName scriptPath( path );
580  scriptPath.MakeAbsolute();
581 
582  // Convert '\' to '/' in path, because later python script read \n or \r
583  // as escaped sequence, and create issues, when calling it by PyRun_SimpleString() method.
584  // It can happen on Windows.
585  path = scriptPath.GetFullPath();
586  path.Replace( '\\', '/' );
587 
588  return path;
589 }
KIWAY Kiway & Pgm(), KFCTL_STANDALONE
The global Program "get" accessor.
Definition: single_top.cpp:104

References Pgm().

Referenced by PyPluginsPath(), and PythonPluginsReloadBase().

◆ PyStringToWx()

wxString PyStringToWx ( PyObject *  aString)

Definition at line 440 of file python_scripting.cpp.

441 {
442  wxString ret;
443 
444  if( !aString )
445  return ret;
446 
447 #if PY_MAJOR_VERSION >= 3
448  const char* str_res = NULL;
449  PyObject* temp_bytes = PyUnicode_AsEncodedString( aString, "UTF-8", "strict" );
450 
451  if( temp_bytes != NULL )
452  {
453  str_res = PyBytes_AS_STRING( temp_bytes );
454  ret = FROM_UTF8( str_res );
455  Py_DECREF( temp_bytes );
456  }
457  else
458  {
459  wxLogMessage( "cannot encode unicode python string" );
460  }
461 #else
462  const char* str_res = PyString_AsString( aString );
463  ret = FROM_UTF8( str_res );
464 #endif
465 
466  return ret;
467 }
static wxString FROM_UTF8(const char *cstring)
function FROM_UTF8 converts a UTF8 encoded C string to a wxString for all wxWidgets build modes.
Definition: macros.h:114
#define NULL

References FROM_UTF8(), and NULL.

Referenced by PYTHON_ACTION_PLUGIN::CallRetStrMethod(), PYTHON_FOOTPRINT_WIZARD::CallRetStrMethod(), and PYTHON_FOOTPRINT_WIZARD::GetParameterPageName().

◆ swigAddBuiltin()

static void swigAddBuiltin ( )
static

Add the builtin python modules.

Definition at line 96 of file python_scripting.cpp.

97 {
98  int i = 0;
99 
100  /* discover the length of the pyimport inittab */
101  while( PyImport_Inittab[i].name )
102  i++;
103 
104  /* allocate memory for the python module table */
105  SwigImportInittab = (struct _inittab*) malloc(
106  sizeof( struct _inittab ) * ( i + EXTRA_PYTHON_MODULES ) );
107 
108  /* copy all pre-existing python modules into our newly created table */
109  i = 0;
110 
111  while( PyImport_Inittab[i].name )
112  {
113  swigAddModule( PyImport_Inittab[i].name, PyImport_Inittab[i].initfunc );
114  i++;
115  }
116 }
#define EXTRA_PYTHON_MODULES
static void swigAddModule(const char *name, void(*initfunc)())
Add a name + initfuction to our SwigImportInittab.
const char * name
Definition: DXF_plotter.cpp:60
struct _inittab * SwigImportInittab

References EXTRA_PYTHON_MODULES, name, swigAddModule(), and SwigImportInittab.

Referenced by pcbnewInitPythonScripting().

◆ swigAddModule()

static void swigAddModule ( const char *  name,
void(*)()  initfunc 
)
static

Add a name + initfuction to our SwigImportInittab.

Definition at line 82 of file python_scripting.cpp.

84 {
85  SwigImportInittab[SwigNumModules].name = (char*) name;
86  SwigImportInittab[SwigNumModules].initfunc = initfunc;
88  SwigImportInittab[SwigNumModules].name = (char*) 0;
89  SwigImportInittab[SwigNumModules].initfunc = 0;
90 }
const char * name
Definition: DXF_plotter.cpp:60
struct _inittab * SwigImportInittab
static int SwigNumModules

References name, SwigImportInittab, and SwigNumModules.

Referenced by swigAddBuiltin(), and swigAddModules().

◆ swigAddModules()

static void swigAddModules ( )
static

Add the internal modules to the python scripting so they will be available to the scripts.

Definition at line 122 of file python_scripting.cpp.

123 {
124 #if PY_MAJOR_VERSION >= 3
125  swigAddModule( "_pcbnew", PyInit__pcbnew );
126 #else
127  swigAddModule( "_pcbnew", init_pcbnew );
128 #endif
129 
130  // finally it seems better to include all in just one module
131  // but in case we needed to include any other modules,
132  // it must be done like this:
133  // swigAddModule( "_kicad", init_kicad );
134 }
static void swigAddModule(const char *name, void(*initfunc)())
Add a name + initfuction to our SwigImportInittab.
void init_pcbnew(void)

References init_pcbnew(), and swigAddModule().

Referenced by pcbnewInitPythonScripting().

◆ swigSwitchPythonBuiltin()

static void swigSwitchPythonBuiltin ( )
static

Switch the python module table to the Pcbnew built one.

Definition at line 140 of file python_scripting.cpp.

141 {
142  PyImport_Inittab = SwigImportInittab;
143 }
struct _inittab * SwigImportInittab

References SwigImportInittab.

Referenced by pcbnewInitPythonScripting().

Variable Documentation

◆ g_PythonMainTState

PyThreadState* g_PythonMainTState

Definition at line 146 of file python_scripting.cpp.

Referenced by pcbnewFinishPythonScripting(), and pcbnewInitPythonScripting().

◆ SwigImportInittab

struct _inittab* SwigImportInittab

Definition at line 62 of file python_scripting.cpp.

Referenced by swigAddBuiltin(), swigAddModule(), and swigSwitchPythonBuiltin().

◆ SwigNumModules

int SwigNumModules = 0
static

Definition at line 63 of file python_scripting.cpp.

Referenced by swigAddModule().

◆ wxPythonLoaded

bool wxPythonLoaded = false
static

True if the wxPython scripting layer was successfully loaded.

Definition at line 66 of file python_scripting.cpp.

Referenced by IsWxPythonLoaded(), and pcbnewInitPythonScripting().