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-2021 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
25 /**
26 * @file python_scripting.cpp
27 * @brief methods to add scripting capabilities inside pcbnew
28 */
29
30 #include <python_scripting.h>
31
32 #include <cstdlib>
33 #include <cstring>
34 #include <Python.h>
35 #include <sstream>
36
37 #include <eda_base_frame.h>
38 #include <gal/color4d.h>
39 #include <trace_helpers.h>
40 #include <string_utils.h>
41 #include <macros.h>
42 #include <paths.h>
43 #include <settings/settings_manager.h>
44
45 #include <kiplatform/environment.h>
46
47 #include <wx/app.h>
48
49 #include <config.h>
50
51
52 /**
53 * Run a python method from the pcbnew module.
54 *
55 * @param aMethodName is the name of the method (like "pcbnew.myfunction" ).
56 * @param aNames will contain the returned string.
57 */
pcbnewRunPythonMethodWithReturnedString(const char * aMethodName,wxString & aNames)58 static void pcbnewRunPythonMethodWithReturnedString( const char* aMethodName, wxString& aNames )
59 {
60 aNames.Clear();
61
62 PyLOCK lock;
63 PyErr_Clear();
64
65 PyObject* builtins = PyImport_ImportModule( "pcbnew" );
66 wxASSERT( builtins );
67
68 if( !builtins ) // Something is wrong in pcbnew.py module (incorrect version?)
69 return;
70
71 PyObject* globals = PyDict_New();
72 PyDict_SetItemString( globals, "pcbnew", builtins );
73 Py_DECREF( builtins );
74
75 // Build the python code
76 char cmd[1024];
77 snprintf( cmd, sizeof(cmd), "result = %s()", aMethodName );
78
79 // Execute the python code and get the returned data
80 PyObject* localDict = PyDict_New();
81 PyObject* pobj = PyRun_String( cmd, Py_file_input, globals, localDict);
82 Py_DECREF( globals );
83
84 if( pobj )
85 {
86 PyObject* str = PyDict_GetItemString(localDict, "result" );
87 const char* str_res = nullptr;
88
89 if(str)
90 {
91 PyObject* temp_bytes = PyUnicode_AsEncodedString( str, "UTF-8", "strict" );
92
93 if( temp_bytes != nullptr )
94 {
95 str_res = PyBytes_AS_STRING( temp_bytes );
96 aNames = FROM_UTF8( str_res );
97 Py_DECREF( temp_bytes );
98 }
99 else
100 {
101 wxLogMessage( "cannot encode Unicode python string" );
102 }
103 }
104 else
105 {
106 aNames = wxString();
107 }
108
109 Py_DECREF( pobj );
110 }
111
112 Py_DECREF( localDict );
113
114 if( PyErr_Occurred() )
115 wxLogMessage( PyErrStringWithTraceback() );
116 }
117
118
pcbnewGetUnloadableScriptNames(wxString & aNames)119 void pcbnewGetUnloadableScriptNames( wxString& aNames )
120 {
121 pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetUnLoadableWizards", aNames );
122 }
123
124
pcbnewGetScriptsSearchPaths(wxString & aNames)125 void pcbnewGetScriptsSearchPaths( wxString& aNames )
126 {
127 pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsSearchPaths", aNames );
128 }
129
130
pcbnewGetWizardsBackTrace(wxString & aTrace)131 void pcbnewGetWizardsBackTrace( wxString& aTrace )
132 {
133 pcbnewRunPythonMethodWithReturnedString( "pcbnew.GetWizardsBackTrace", aTrace );
134
135 // Filter message before displaying them
136 // a trace starts by "Traceback" and is followed by 2 useless lines
137 // for our purpose
138 wxArrayString traces;
139 wxStringSplit( aTrace, traces, '\n' );
140
141 // Build the filtered message (remove useless lines)
142 aTrace.Clear();
143
144 for( unsigned ii = 0; ii < traces.Count(); ++ii )
145 {
146 if( traces[ii].Contains( "Traceback" ) )
147 {
148 ii += 2; // Skip this line and next lines which are related to pcbnew.py module
149
150 if( !aTrace.IsEmpty() ) // Add separator for the next trace block
151 aTrace << "\n**********************************\n";
152 }
153 else
154 {
155 aTrace += traces[ii] + "\n";
156 }
157 }
158 }
159