OpenWalnut  1.4.0
WScriptInterpreterPython.cpp
1 //---------------------------------------------------------------------------
2 //
3 // Project: OpenWalnut ( http://www.openwalnut.org )
4 //
5 // Copyright 2009 OpenWalnut Community, BSV@Uni-Leipzig and CNCF@MPI-CBS
6 // For more information see http://www.openwalnut.org/copying
7 //
8 // This file is part of OpenWalnut.
9 //
10 // OpenWalnut is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // OpenWalnut is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with OpenWalnut. If not, see <http://www.gnu.org/licenses/>.
22 //
23 //---------------------------------------------------------------------------
24 
25 #include <fstream>
26 #include <string>
27 #include <vector>
28 
29 #include "core/kernel/WKernel.h"
30 
31 #include "../wrappers/WLoggerWrapper.h"
32 #include "../wrappers/WModuleWrapper.h"
33 #include "../wrappers/WPropertyGroupWrapper.h"
34 #include "../wrappers/WPropertyWrapper.h"
35 
36 #include "WScriptInterpreterPython.h"
37 
38 #ifdef PYTHON_FOUND
39 
40 WScriptInterpreterPython::WScriptInterpreterPython( boost::shared_ptr< WModuleContainer > const& rootContainer )
42  m_rootContainer( rootContainer ),
43  m_argc( 0 ),
44  m_argv( 0 ),
45  m_scriptThread( *this )
46 {
47  try
48  {
49  Py_Initialize();
50  m_pyModule = pb::import( "__main__" );
51  m_pyMainNamespace = m_pyModule.attr( "__dict__" );
52  }
53  catch( pb::error_already_set const& )
54  {
55  PyErr_Print();
56  }
57  // Make ctrl+c key available for killing interpeter
58  execute( "import signal" );
59  execute( "signal.signal( signal.SIGINT, signal.SIG_DFL )" );
60 
61  m_scriptThread.run();
62 }
63 
64 WScriptInterpreterPython::~WScriptInterpreterPython()
65 {
66  m_scriptThread.requestStop();
67  m_scriptThread.wait();
68 
69  Py_Finalize();
70 
71  if( m_argv )
72  {
73  for( int k = 0; k < m_argc; ++k )
74  {
75  delete[] m_argv[ k ];
76  }
77  delete[] m_argv;
78  }
79 }
80 
81 void WScriptInterpreterPython::initBindings()
82 {
83  boost::unique_lock< boost::mutex > lock( m_mutex );
84 
85  // bind WPropertyWrapper class to "WProperty" in the python namespace
86  // no constructor in python for now
87  m_pyMainNamespace[ "WProperty" ] = pb::class_< WPropertyWrapper >( "WProperty", pb::no_init )
88  .def( "getBool", &WPropertyWrapper::getBool )
89  .def( "getInt", &WPropertyWrapper::getInt )
90  .def( "getString", &WPropertyWrapper::getString )
91  .def( "getDouble", &WPropertyWrapper::getDouble )
92  .def( "getFilename", &WPropertyWrapper::getFilename )
93  .def( "getSelection", &WPropertyWrapper::getSelection )
94  .def( "setBool", &WPropertyWrapper::setBool )
95  .def( "setInt", &WPropertyWrapper::setInt )
96  .def( "setString", &WPropertyWrapper::setString )
97  .def( "setDouble", &WPropertyWrapper::setDouble )
98  .def( "setFilename", &WPropertyWrapper::setFilename )
99  .def( "setSelection", &WPropertyWrapper::setSelection )
100  .def( "click", &WPropertyWrapper::click )
101  .def( "getName", &WPropertyWrapper::getName )
102  .def( "getDescription", &WPropertyWrapper::getDescription )
103  .def( "waitForUpdate", &WPropertyWrapper::waitForUpdate );
104 
105  m_pyMainNamespace[ "WPropertyGroup" ] = pb::class_< WPropertyGroupWrapper >( "WPropertyGroup", pb::no_init )
106  .def( "getProperty", &WPropertyGroupWrapper::getProperty )
107  .def( "getGroup", &WPropertyGroupWrapper::getGroup )
108  .def( "getName", &WPropertyGroupWrapper::getName )
109  .def( "getDescription", &WPropertyGroupWrapper::getDescription );
110 
111  m_pyMainNamespace[ "WModuleContainer" ] = pb::class_< WModuleContainerWrapper >( "WModuleContainer", pb::no_init )
112  .def( "create", &WModuleContainerWrapper::create )
113  .def( "remove", &WModuleContainerWrapper::remove )
114  .def( "createDataModule", &WModuleContainerWrapper::createDataModule );
115 
116  m_pyMainNamespace[ "WOutputConnector" ] = pb::class_< WOutputConnectorWrapper >( "WOutputConnectorWrapper", pb::no_init )
117  .def( "disconnect", &WOutputConnectorWrapper::disconnect );
118 
119  m_pyMainNamespace[ "WInputConnector" ] = pb::class_< WInputConnectorWrapper >( "WInputConnectorWrapper", pb::no_init )
120  .def( "connect", &WInputConnectorWrapper::connect )
121  .def( "disconnect", &WInputConnectorWrapper::disconnect )
122  .def( "waitForInput", &WInputConnectorWrapper::waitForInput );
123 
124  m_pyMainNamespace[ "WModule" ] = pb::class_< WModuleWrapper >( "WModule", pb::no_init )
125  .def( "getName", &WModuleWrapper::getName )
126  .def( "getDescription", &WModuleWrapper::getDescription )
127  .def( "getProperties", &WModuleWrapper::getProperties )
128  .def( "getInformationProperties", &WModuleWrapper::getInformationProperties )
129  .def( "getInputConnector", &WModuleWrapper::getInputConnector )
130  .def( "getOutputConnector", &WModuleWrapper::getOutputConnector );
131 
132  // bind the kernel's root container to the "rootContainer" variable in the python namespace
133  // this allows access to the modules via this variable
134  m_pyMainNamespace[ "rootContainer" ] = &m_rootContainer;
135 
136  m_pyMainNamespace[ "WLogger" ] = pb::class_< WLoggerWrapper >( "WLogger", pb::no_init )
137  .def( "addFileStream", &WLoggerWrapper::addFileStream )
138  .def( "removeFileStream", &WLoggerWrapper::removeFileStream )
139  .def( "removeAllFileStreams", &WLoggerWrapper::removeAllFileStreams );
140 
141  m_logger = WLoggerWrapper( WLogger::getLogger() );
142  m_pyMainNamespace[ "logger" ] = &m_logger;
143 }
144 
145 void WScriptInterpreterPython::setParameters( std::vector< std::string > const& params )
146 {
147  boost::unique_lock< boost::mutex > lock( m_mutex );
148 
149  if( params.size() == 0 )
150  {
151  return;
152  }
153 
154  m_argc = params.size();
155  m_argv = new char*[ params.size() ];
156 
157  for( std::size_t k = 0; k < params.size(); ++k )
158  {
159  m_argv[ k ] = new char[ params[ k ].length() + 1 ];
160  std::snprintf( m_argv[ k ], params[ k ].length() + 1, "%s", params[ k ].c_str() );
161  m_argv[ k ][ params[ k ].length() ] = '\0';
162  }
163 
164  PySys_SetArgv( m_argc, m_argv );
165 }
166 
167 void WScriptInterpreterPython::execute( std::string const& line )
168 {
169  boost::unique_lock< boost::mutex > lock( m_mutex );
170 
171  try
172  {
173  pb::exec( line.c_str(), m_pyMainNamespace );
174  }
175  catch( pb::error_already_set const& )
176  {
177  PyErr_Print();
178  }
179 }
180 
181 void WScriptInterpreterPython::executeAsync( std::string const& script )
182 {
183  m_scriptThread.addToExecuteQueue( script );
184 }
185 
186 void WScriptInterpreterPython::executeFile( std::string const& filename )
187 {
188  // load file content into string
189  std::ifstream in( filename.c_str() );
190  std::string script;
191  std::string line;
192  while( std::getline( in, line ) )
193  {
194  script += line + "\n";
195  }
196  in.close();
197 
198  // execute
199  try
200  {
201  execute( script );
202  }
203  catch( WException const& e )
204  {
205  wlog::error( "Walnut" ) << "Error while executing script: " << e.what();
206  }
207 }
208 
209 void WScriptInterpreterPython::executeFileAsync( std::string const& filename )
210 {
211  // load file content into string
212  std::ifstream in( filename.c_str() );
213  std::string script;
214  std::string line;
215  while( std::getline( in, line ) )
216  {
217  script += line + "\n";
218  }
219  in.close();
220 
221  // execute
222  executeAsync( script );
223 }
224 
225 std::string const WScriptInterpreterPython::getName() const
226 {
227  return "python";
228 }
229 
230 std::string const WScriptInterpreterPython::getExtension() const
231 {
232  return ".py";
233 }
234 
235 WScriptInterpreterPython::ScriptThread::ScriptThread( WScriptInterpreterPython& interpreter ) // NOLINT reference
236  : WThreadedRunner(),
237  m_scriptQueue(),
238  m_queueMutex(),
239  m_condition( new WCondition() ),
240  m_conditionSet(),
241  m_interpreter( interpreter )
242 {
243  m_conditionSet.setResetable( true, true );
244  m_conditionSet.add( m_condition );
245 }
246 
247 WScriptInterpreterPython::ScriptThread::~ScriptThread()
248 {
249 }
250 
251 void WScriptInterpreterPython::ScriptThread::requestStop()
252 {
254  m_condition->notify();
255 }
256 
257 void WScriptInterpreterPython::ScriptThread::threadMain()
258 {
259  while( !m_shutdownFlag )
260  {
261  m_conditionSet.wait();
262 
263  if( m_shutdownFlag )
264  break;
265 
266  std::size_t numScripts = 0;
267  {
268  boost::unique_lock< boost::mutex > lock( m_queueMutex );
269  numScripts = m_scriptQueue.size();
270  }
271 
272  while( numScripts > 0 )
273  {
274  std::string script;
275 
276  // only getting the script content must be locked
277  {
278  boost::unique_lock< boost::mutex > lock( m_queueMutex );
279  script = m_scriptQueue.front();
280  m_scriptQueue.pop();
281  }
282 
283  if( script.length() != 0 )
284  {
285  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Executing script asyncronously.";
286  // note that this may block if the interpreter is currently executing another script
287  m_interpreter.execute( script );
288  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Done executing script.";
289  }
290  {
291  boost::unique_lock< boost::mutex > lock( m_queueMutex );
292  numScripts = m_scriptQueue.size();
293  }
294  }
295  }
296 }
297 
298 void WScriptInterpreterPython::ScriptThread::addToExecuteQueue( std::string const& script )
299 {
300  wlog::info( "WScriptInterpreterPython::ScriptThread" ) << "Queueing script for asyncronous execution.";
301 
302  boost::unique_lock< boost::mutex > lock( m_queueMutex );
303  m_scriptQueue.push( script );
304  m_condition->notify();
305 }
306 
307 #endif // PYTHON_FOUND