KiCad PCB EDA Suite
python_scripting.cpp
Go to the documentation of this file.
1 /*
2  * This program source code file is part of KiCad, a free EDA CAD application.
3  *
4  * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@nbee.es>
5  * Copyright (C) 1992-2016 KiCad Developers, see AUTHORS.txt for contributors.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, you may find one here:
19  * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
20  * or you may search the http://www.gnu.org website for the version 2 license,
21  * or you may write to the Free Software Foundation, Inc.,
22  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23  */
24 
30 #include <python_scripting.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include <fctsys.h>
35 #include <wxstruct.h>
36 #include <common.h>
37 #include <gal/color4d.h>
38 #include <macros.h>
39 
40 #include <pgm_base.h>
41 
42 /* init functions defined by swig */
43 
44 extern "C" void init_kicad( void );
45 
46 extern "C" void init_pcbnew( void );
47 
48 #define EXTRA_PYTHON_MODULES 10 // this is the number of python
49  // modules that we want to add into the list
50 
51 
52 /* python inittab that links module names to module init functions
53  * we will rebuild it to include the original python modules plus
54  * our own ones
55  */
56 
57 struct _inittab* SwigImportInittab;
58 static int SwigNumModules = 0;
59 
60 static bool wxPythonLoaded = false; // true if the wxPython scripting layer was successfully loaded
61 
63 {
64  return wxPythonLoaded;
65 }
66 
67 
68 /* Add a name + initfuction to our SwigImportInittab */
69 
70 static void swigAddModule( const char* name, void (* initfunc)() )
71 {
72  SwigImportInittab[SwigNumModules].name = (char*) name;
73  SwigImportInittab[SwigNumModules].initfunc = initfunc;
75  SwigImportInittab[SwigNumModules].name = (char*) 0;
76  SwigImportInittab[SwigNumModules].initfunc = 0;
77 }
78 
79 
80 /* Add the builtin python modules */
81 
82 static void swigAddBuiltin()
83 {
84  int i = 0;
85 
86  /* discover the length of the pyimport inittab */
87  while( PyImport_Inittab[i].name )
88  i++;
89 
90  /* allocate memory for the python module table */
91  SwigImportInittab = (struct _inittab*) malloc(
92  sizeof(struct _inittab) * (i + EXTRA_PYTHON_MODULES) );
93 
94  /* copy all pre-existing python modules into our newly created table */
95  i = 0;
96 
97  while( PyImport_Inittab[i].name )
98  {
99  swigAddModule( PyImport_Inittab[i].name, PyImport_Inittab[i].initfunc );
100  i++;
101  }
102 }
103 
104 
105 /* Function swigAddModules
106  * adds the internal modules we offer to the python scripting, so they will be
107  * available to the scripts we run.
108  *
109  */
110 
111 static void swigAddModules()
112 {
113  swigAddModule( "_pcbnew", init_pcbnew );
114 
115  // finally it seems better to include all in just one module
116  // but in case we needed to include any other modules,
117  // it must be done like this:
118  // swigAddModule( "_kicad", init_kicad );
119 }
120 
121 
122 /* Function swigSwitchPythonBuiltin
123  * switches python module table to our built one .
124  *
125  */
126 
128 {
129  PyImport_Inittab = SwigImportInittab;
130 }
131 
132 
133 /* Function pcbnewInitPythonScripting
134  * Initializes all the python environment and publish our interface inside it
135  * initializes all the wxpython interface, and returns the python thread control structure
136  *
137  */
138 
139 PyThreadState* g_PythonMainTState;
140 
141 bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
142 {
143  swigAddBuiltin(); // add builtin functions
144  swigAddModules(); // add our own modules
145  swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list
146 
147  Py_Initialize();
148 
149 #ifdef KICAD_SCRIPTING_WXPYTHON
150  PyEval_InitThreads();
151 
152 #ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
153  char cmd[1024];
154  // Make sure that that the correct version of wxPython is loaded. In systems where there
155  // are different versions of wxPython installed this can lead to select wrong wxPython
156  // version being selected.
157  snprintf( cmd, sizeof(cmd), "import wxversion; wxversion.select('%s')", WXPYTHON_VERSION );
158 
159  int retv = PyRun_SimpleString( cmd );
160 
161  if( retv != 0 )
162  {
163  wxLogError( wxT( "Python error %d occurred running string `%s`" ), retv, cmd );
164  PyErr_Print();
165  Py_Finalize();
166  return false;
167  }
168 #endif // ifndef __WINDOWS__
169 
170  // Load the wxPython core API. Imports the wx._core_ module and sets a
171  // local pointer to a function table located there. The pointer is used
172  // internally by the rest of the API functions.
173  if( !wxPyCoreAPI_IMPORT() )
174  {
175  wxLogError( wxT( "***** Error importing the wxPython API! *****" ) );
176  PyErr_Print();
177  Py_Finalize();
178  return false;
179  }
180 
181  wxPythonLoaded = true;
182 
183  // Save the current Python thread state and release the
184  // Global Interpreter Lock.
185 
186  g_PythonMainTState = wxPyBeginAllowThreads();
187 #endif // ifdef KICAD_SCRIPTING_WXPYTHON
188 
189  // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
190  {
191  char loadCmd[1024];
192  PyLOCK lock;
193  snprintf( loadCmd, sizeof(loadCmd), "import sys, traceback\n"
194  "sys.path.append(\".\")\n"
195  "import pcbnew\n"
196  "pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath );
197  PyRun_SimpleString( loadCmd );
198  }
199 
200  return true;
201 }
202 
203 
209 static void pcbnewRunPythonMethodWithReturnedString( const char* aMethodName, wxString& aNames )
210 {
211  aNames.Clear();
212 
213  PyLOCK lock;
214  PyErr_Clear();
215 
216  PyObject* builtins = PyImport_ImportModule( "pcbnew" );
217  wxASSERT( builtins );
218 
219  if( !builtins ) // Something is wrong in pcbnew.py module (incorrect version?)
220  return;
221 
222  PyObject* globals = PyDict_New();
223  PyDict_SetItemString( globals, "pcbnew", builtins );
224  Py_DECREF( builtins );
225 
226  // Build the python code
227  char cmd[1024];
228  snprintf( cmd, sizeof(cmd), "result = %s()", aMethodName );
229 
230  // Execute the python code and get the returned data
231  PyObject* localDict = PyDict_New();
232  PyObject* pobj = PyRun_String( cmd, Py_file_input, globals, localDict);
233  Py_DECREF( globals );
234 
235  if( pobj )
236  {
237  PyObject* str = PyDict_GetItemString(localDict, "result" );
238  const char* str_res = str ? PyString_AsString( str ) : 0;
239  aNames = FROM_UTF8( str_res );
240  Py_DECREF( pobj );
241  }
242 
243  Py_DECREF( localDict );
244 
245  if( PyErr_Occurred() )
246  wxLogMessage(PyErrStringWithTraceback());
247 }
248 
249 
250 void pcbnewGetUnloadableScriptNames( wxString& aNames )
251 {
252  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetUnLoadableWizards", aNames );
253 }
254 
255 
256 void pcbnewGetScriptsSearchPaths( wxString& aNames )
257 {
258  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsSearchPaths", aNames );
259 }
260 
261 void pcbnewGetWizardsBackTrace( wxString& aNames )
262 {
263  pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsBackTrace", aNames );
264 }
265 
266 
268 {
269 #ifdef KICAD_SCRIPTING_WXPYTHON
270  wxPyEndAllowThreads( g_PythonMainTState );
271 #endif
272  Py_Finalize();
273 }
274 
275 
276 #if defined( KICAD_SCRIPTING_WXPYTHON )
277 
278 void RedirectStdio()
279 {
280  // This is a helpful little tidbit to help debugging and such. It
281  // redirects Python's stdout and stderr to a window that will popup
282  // only on demand when something is printed, like a traceback.
283  const char* python_redirect =
284  "import sys\n"
285  "import wx\n"
286  "output = wx.PyOnDemandOutputWindow()\n"
287  "sys.stderr = output\n";
288 
289  PyLOCK lock;
290 
291  PyRun_SimpleString( python_redirect );
292 }
293 
294 
295 wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId )
296 {
297  const char* pcbnew_pyshell =
298  "import kicad_pyshell\n"
299  "\n"
300  "def makeWindow(parent):\n"
301  " return kicad_pyshell.makePcbnewShellWindow(parent)\n"
302  "\n";
303 
304  wxWindow* window = NULL;
305  PyObject* result;
306 
307  // As always, first grab the GIL
308  PyLOCK lock;
309 
310  // Now make a dictionary to serve as the global namespace when the code is
311  // executed. Put a reference to the builtins module in it.
312 
313  PyObject* globals = PyDict_New();
314  PyObject* builtins = PyImport_ImportModule( "__builtin__" );
315 
316  PyDict_SetItemString( globals, "__builtins__", builtins );
317  Py_DECREF( builtins );
318 
319  // Execute the code to make the makeWindow function we defined above
320  result = PyRun_String( pcbnew_pyshell, Py_file_input, globals, globals );
321 
322  // Was there an exception?
323  if( !result )
324  {
325  PyErr_Print();
326  return NULL;
327  }
328 
329  Py_DECREF( result );
330 
331  // Now there should be an object named 'makeWindow' in the dictionary that
332  // we can grab a pointer to:
333  PyObject* func = PyDict_GetItemString( globals, "makeWindow" );
334  wxASSERT( PyCallable_Check( func ) );
335 
336  // Now build an argument tuple and call the Python function. Notice the
337  // use of another wxPython API to take a wxWindows object and build a
338  // wxPython object that wraps it.
339 
340  PyObject* arg = wxPyMake_wxObject( parent, false );
341  wxASSERT( arg != NULL );
342 
343  PyObject* tuple = PyTuple_New( 1 );
344  PyTuple_SET_ITEM( tuple, 0, arg );
345 
346  result = PyEval_CallObject( func, tuple );
347 
348  // Was there an exception?
349  if( !result )
350  PyErr_Print();
351  else
352  {
353  // Otherwise, get the returned window out of Python-land and
354  // into C++-ville...
355  bool success = wxPyConvertSwigPtr( result, (void**) &window, "wxWindow" );
356  (void) success;
357 
358  wxASSERT_MSG( success, "Returned object was not a wxWindow!" );
359  Py_DECREF( result );
360 
361  window->SetName( aFramenameId );
362  }
363 
364  // Release the python objects we still have
365  Py_DECREF( globals );
366  Py_DECREF( tuple );
367 
368  return window;
369 }
370 
371 
372 #endif
373 
374 wxArrayString PyArrayStringToWx( PyObject* aArrayString )
375 {
376  wxArrayString ret;
377 
378  if( !aArrayString )
379  return ret;
380 
381  int list_size = PyList_Size( aArrayString );
382 
383  for( int n = 0; n < list_size; n++ )
384  {
385  PyObject* element = PyList_GetItem( aArrayString, n );
386 
387  if( element )
388  ret.Add( FROM_UTF8( PyString_AsString( element ) ), 1 );
389  }
390 
391  return ret;
392 }
393 
394 
396 {
397  wxString err;
398 
399  if( !PyErr_Occurred() )
400  return err;
401 
402  PyObject* type;
403  PyObject* value;
404  PyObject* traceback;
405 
406  PyErr_Fetch( &type, &value, &traceback );
407 
408  PyObject* tracebackModuleString = PyString_FromString( "traceback" );
409  PyObject* tracebackModule = PyImport_Import( tracebackModuleString );
410  Py_DECREF( tracebackModuleString );
411 
412  PyObject* formatException = PyObject_GetAttrString( tracebackModule,
413  "format_exception" );
414  Py_DECREF( tracebackModule );
415 
416  PyObject* args = Py_BuildValue( "(O,O,O)", type, value, traceback );
417  PyObject* result = PyObject_CallObject( formatException, args );
418  Py_XDECREF( formatException );
419  Py_XDECREF( args );
420  Py_XDECREF( type );
421  Py_XDECREF( value );
422  Py_XDECREF( traceback );
423 
424  wxArrayString res = PyArrayStringToWx( result );
425 
426  for( unsigned i = 0; i<res.Count(); i++ )
427  {
428  err += res[i] + wxT( "\n" );
429  }
430 
431  PyErr_Clear();
432 
433  return err;
434 }
435 
439 wxString PyScriptingPath()
440 {
441  wxString path;
442 
443  //TODO should this be a user configurable variable eg KISCRIPT ?
444 #if defined( __WXMAC__ )
445  path = GetOSXKicadDataDir() + wxT( "/scripting" );
446 #else
447  path = Pgm().GetExecutablePath() + wxT( "../share/kicad/scripting" );
448 #endif
449 
450  wxFileName scriptPath( path );
451  scriptPath.MakeAbsolute();
452 
453  return scriptPath.GetFullPath();
454 }
455 
456 wxString PyPluginsPath()
457 {
458  return PyScriptingPath() + wxFileName::GetPathSeparator() + "plugins";
459 }
static void pcbnewRunPythonMethodWithReturnedString(const char *aMethodName, wxString &aNames)
this function runs a python method from pcbnew module, which returns a string
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:53
wxString PyPluginsPath()
bool pcbnewInitPythonScripting(const char *aUserScriptingPath)
Function pcbnewInitPythonScripting Initializes the Python engine inside pcbnew.
VTBL_ENTRY const wxString & GetExecutablePath() const
Definition: pgm_base.h:173
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: kicad.cpp:65
static void swigAddBuiltin()
void pcbnewFinishPythonScripting()
#define EXTRA_PYTHON_MODULES
This file contains miscellaneous commonly used macros and functions.
void pcbnewGetWizardsBackTrace(wxString &aNames)
Function pcbnewGetWizardsBackTrace returns the backtrace of errors (if any) when wizard python script...
wxString PyScriptingPath()
Find the Python scripting path.
static void swigAddModules()
Base window classes and related definitions.
static void swigAddModule(const char *name, void(*initfunc)())
wxArrayString PyArrayStringToWx(PyObject *aArrayString)
static void swigSwitchPythonBuiltin()
static bool wxPythonLoaded
void init_pcbnew(void)
void pcbnewGetUnloadableScriptNames(wxString &aNames)
Function pcbnewGetUnloadableScriptNames collects the list of python scripts which could not be loaded...
see class PGM_BASE
void init_kicad(void)
wxString PyErrStringWithTraceback()
The common library.
struct _inittab * SwigImportInittab
bool IsWxPythonLoaded()
void pcbnewGetScriptsSearchPaths(wxString &aNames)
Function pcbnewGetScriptsSearchPaths collects the list of paths where python scripts are searched...
static int SwigNumModules
PyThreadState * g_PythonMainTState