Flow123d  last_with_con_2.0.0-663-gd0e2296
python_loader.cc
Go to the documentation of this file.
1 /*!
2  *
3  * Copyright (C) 2015 Technical University of Liberec. All rights reserved.
4  *
5  * This program is free software; you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License version 3 as published by the
7  * Free Software Foundation. (http://www.gnu.org/licenses/gpl-3.0.en.html)
8  *
9  * This program is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11  * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12  *
13  *
14  * @file python_loader.cc
15  * @brief
16  */
17 
18 #include "global_defs.h"
19 
20 #ifdef FLOW123D_HAVE_PYTHON
21 
22 #include "system/python_loader.hh"
23 
24 #include "system/system.hh"
25 #include "system/file_path.hh"
26 #include <string>
27 #include <iostream>
28 #include <fstream>
29 #include <sstream>
30 
31 using namespace std;
32 
33 string python_sys_path = R"CODE(
34 
35 def get_paths():
36  import sys
37  import os
38  return os.pathsep.join(sys.path)
39 
40 )CODE";
41 
42 // default value
43 string PythonLoader::sys_path = "";
44 
45 void PythonLoader::initialize(const std::string &python_home)
46 {
47  static internal::PythonRunning _running(python_home);
48 }
49 
50 
51 PyObject * PythonLoader::load_module_from_file(const std::string& fname) {
52  initialize();
53 
54  // don't know direct way how to load module form file, so we read it into string and use load_module_from_string
55  std::ifstream file_stream;
56  // check correct openning
57  FilePath(fname, FilePath::input_file).open_stream(file_stream);
58  file_stream.exceptions ( ifstream::failbit | ifstream::badbit );
59 
60  std::stringstream buffer;
61  buffer << file_stream.rdbuf();
62 
63  string module_name;
64  unsigned int pos = fname.rfind("/");
65  if (pos != string::npos)
66  module_name = fname.substr(pos+1);
67  else
68  module_name = fname;
69 
70  // cout << "python module: " << module_name <<endl;
71  // DebugOut() << buffer.str() << "\n";
72  // TODO: use exceptions and catch it here to produce shorter and more precise error message
73  return load_module_from_string(module_name, buffer.str() );
74 }
75 
76 
77 
78 PyObject * PythonLoader::load_module_from_string(const std::string& module_name, const std::string& source_string) {
79  initialize();
80 
81  // for unknown reason module name is non-const, so we have to make char * copy
82  char * tmp_name = new char[ module_name.size() + 2 ];
83  strcpy( tmp_name, module_name.c_str() );
84 
85  PyObject * compiled_string = Py_CompileString( source_string.c_str(), module_name.c_str(), Py_file_input );
86  if (PyErr_Occurred()) {
87  PyObject *ptype, *pvalue, *ptraceback, *pystr;
88  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
89 
90  if ( !PyString_Check(pvalue) ) { // syntax error
91  stringstream ss;
92  PyObject* err_type = PySequence_GetItem(pvalue, 0);
93  pystr = PyObject_Str(err_type);
94  ss << PyString_AsString(pystr) << endl;
95  PyObject* descr = PySequence_GetItem(pvalue, 1);
96  PyObject* file = PySequence_GetItem(descr, 0);
97  pystr = PyObject_Str(file);
98  ss << " File \"" << PyString_AsString(pystr) << "\"";
99  PyObject* row = PySequence_GetItem(descr, 1);
100  pystr = PyObject_Str(row);
101  ss << ", line " << PyString_AsString(pystr) << endl;
102  PyObject* line = PySequence_GetItem(descr, 3);
103  pystr = PyObject_Str(line);
104  ss << PyString_AsString(pystr);
105  PyObject* col = PySequence_GetItem(descr, 2);
106  pystr = PyObject_Str(col);
107  int pos = atoi( PyString_AsString(pystr) );
108  ss << setw(pos) << "^" << endl;
109  THROW(ExcPythonError() << EI_PythonMessage( ss.str() ));
110  } else {
111  THROW(ExcPythonError() << EI_PythonMessage( PyString_AsString(pvalue) ));
112  }
113  }
114 
115  PyObject * result = PyImport_ExecCodeModule(tmp_name, compiled_string);
116  PythonLoader::check_error();
117 
118  delete[] tmp_name;
119  return result;
120 }
121 
122 PyObject * PythonLoader::load_module_by_name(const std::string& module_name) {
123  initialize();
124 
125  // import module by dot separated path and its name
126  PyObject * module_object = PyImport_ImportModule (module_name.c_str());
127  PythonLoader::check_error();
128 
129  return module_object;
130 }
131 
132 
133 void PythonLoader::check_error() {
134  if (PyErr_Occurred()) {
135  PyObject *ptype, *pvalue, *ptraceback;
136 
137  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
138  string error_description = string(PyString_AsString(PyObject_Str(pvalue)));
139 
140  /* See if we can get a full traceback */
141  PyObject *module_name = PyString_FromString("traceback");
142  PyObject *pyth_module = PyImport_Import(module_name);
143  Py_DECREF(module_name);
144 
145  string str_traceback;
146  if (pyth_module) {
147  PyObject *pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
148  if (pyth_func && PyCallable_Check(pyth_func)) {
149  PyObject *pyth_val = PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
150  if (pyth_val) {
151  str_traceback = string(PyString_AsString(PyObject_Str(pyth_val)));
152  Py_DECREF(pyth_val);
153  }
154  }
155  }
156 
157  // get value of python's "sys.path"
158  string python_path = PythonLoader::sys_path;
159  replace(python_path.begin(), python_path.end(), ':', '\n');
160 
161  string py_message =
162  "\nType: " + string(PyString_AsString(PyObject_Str(ptype))) + "\n"
163  + "Message: " + string(PyString_AsString(PyObject_Str(pvalue))) + "\n"
164  + "Traceback: " + str_traceback + "\n"
165  + "Paths: " + "\n" + python_path + "\n";
166 
167  THROW(ExcPythonError() << EI_PythonMessage( py_message ));
168  }
169 }
170 
171 
172 
173 PyObject * PythonLoader::get_callable(PyObject *module, const std::string &func_name) {
174  char func_char[func_name.size()+2];
175  strcpy(func_char, func_name.c_str());
176  PyObject * func = PyObject_GetAttrString(module, func_char );
177  PythonLoader::check_error();
178 
179  if (! PyCallable_Check(func)) {
180  stringstream ss;
181  ss << "Field '" << func_name << "' from the python module: " << PyModule_GetName(module) << " is not callable." << endl;
182  THROW(ExcPythonError() << EI_PythonMessage( ss.str() ));
183  }
184 
185  return func;
186 }
187 
188 
189 
190 wstring to_py_string(const string &str) {
191  wchar_t wbuff[ str.size() ];
192  size_t wstr_size = mbstowcs( wbuff, str.c_str(), str.size() );
193  return wstring( wbuff, wstr_size );
194 }
195 
196 string from_py_string(const wstring &wstr) {
197  char buff[ wstr.size() ];
198  size_t str_size = wcstombs( buff, wstr.c_str(), wstr.size() );
199  return string( buff, str_size );
200 }
201 
202 // currently we support only Python 2.7
203 //
204 #if FLOW123D_PYTHONLIBS_VERSION_MAJOR<3
205  #define to_py_string string
206  #define from_py_string string
207  #define PY_STRING string
208 #else
209  #define PY_STRING wstring
210 #endif
211 
212 #define STR_EXPAND(tok) #tok
213 #define STR(tok) string(STR_EXPAND(tok))
214 
215 namespace internal {
216 
217 PythonRunning::PythonRunning(const std::string& program_name)
218 {
219 #ifdef FLOW123D_PYTHON_PREFIX
220  static PY_STRING _python_program_name = to_py_string(program_name);
221  Py_SetProgramName( &(_python_program_name[0]) );
222  PY_STRING full_program_name = Py_GetProgramFullPath();
223  // cout << "full program name: " << from_py_string(full_program_name) << std::endl;
224 
225  // try to find string "flow123d" from right side of program_name
226  // if such a string is not present, we are most likely unit-testing
227  // in that case, full_flow_prefix is current dir '.'
228  size_t pos = full_program_name.rfind( to_py_string("flow123d") );
229  PY_STRING full_flow_prefix = ".";
230  if (pos != PY_STRING::npos) {
231  full_flow_prefix = full_program_name.substr(0,pos-string("/bin/").size() );
232  }
233  // cout << "full flow prefix: " << from_py_string(full_flow_prefix) << std::endl;
234  PY_STRING default_py_prefix(to_py_string(STR(FLOW123D_PYTHON_PREFIX)));
235  // cout << "default py prefix: " << from_py_string(default_py_prefix) << std::endl;
236 
237  static PY_STRING our_py_home(full_flow_prefix + ":" +default_py_prefix);
238  Py_SetPythonHome( &(our_py_home[0]) );
239 
240  /*
241  Py_GetPath();
242 
243  static PY_STRING our_py_path;
244  string python_subdir("/lib/python" + STR(FLOW123D_PYTHONLIBS_VERSION_MAJOR) + "." + STR(FLOW123D_PYTHONLIBS_VERSION_MINOR));
245  our_py_path+=full_flow_prefix + to_py_string( python_subdir + "/:");
246  our_py_path+=full_flow_prefix + to_py_string( python_subdir + "/plat-cygwin:");
247  our_py_path+=full_flow_prefix + to_py_string( python_subdir + "/lib-dynload:");
248  our_py_path+=default_py_prefix + to_py_string( python_subdir + "/lib-dynload:");
249  our_py_path+=default_py_prefix + to_py_string( python_subdir + "/lib-dynload:");
250  our_py_path+=default_py_prefix + to_py_string( python_subdir + "/lib-dynload:");
251 
252  Py_SetPath(our_py_path);
253  //our_py_path+=full_flow_prefix + to_py_string( python_subdir);
254 
255 // string prefix = ;
256  */
257  // cout << "Python path: " << from_py_string( Py_GetPath() ) << std::endl;
258  // cout << "Python home: " << from_py_string( Py_GetPythonHome() ) << std::endl;
259  // cout << "Python prefix: " << from_py_string( Py_GetPrefix() ) << std::endl;
260  // cout << "Python exec prefix: " << from_py_string( Py_GetExecPrefix() ) << std::endl;
261 
262  // 1. set program name
263  // 2. get prefix
264  // 3. get python full path
265  // 4. extract prefix from the full path
266  // 5. substitute the prefix in python path
267  // 6. set python path (suppress its computation during Py_Initialize)
268  /*
269  wchar_t wbuf[ python_home.size() ];
270  size_t num_chars = mbstowcs( wbuf, python_home.c_str(), python_home.size() );
271  static wstring _python_home_storage( wbuf, num_chars ); // permanent non-const storage required
272 
273  std::wcout << "new python home: " << _python_home_storage << std::endl;
274 
275  Py_SetProgramName( &(_python_home_storage[0]) );
276 
277  char buff[ 1024 ];
278  num_chars = wcstombs(buff, Py_GetPath(), 256);
279  buff[1024]=0;
280  string str(buff, num_chars);
281  std::cout << "Python path: " << str << std::endl;
282 
283 
284 
285  wchar_t wbuf[ python_home.size() ];
286  size_t num_chars = mbstowcs( wbuf, python_home.c_str(), python_home.size() );
287  static wstring _python_home_storage( wbuf, num_chars ); // permanent non-const storage required
288 
289  std::wcout << "new python home: " << _python_home_storage << std::endl;
290 
291  Py_SetProgramName( &(_python_home_storage[0]) );
292 
293  char buff[ 1024 ];
294  num_chars = wcstombs(buff, Py_GetPath(), 1024);
295  //std::cout << "num chars: " << num_chars << std::endl;
296  std::cout << "Python path: " << buff << std::endl;
297  //num_chars = wcstombs(buff, Py_GetPythonHome(), 1024);
298  //std::cout << "Python home: " << buff << std::endl;
299  num_chars = wcstombs(buff, Py_GetProgramName(), 1024);
300  std::cout << "Python prog. name: " << buff << std::endl;
301  num_chars = wcstombs(buff, Py_GetPrefix(), 1024);
302  std::cout << "Python prefix: " << buff << std::endl;
303  num_chars = wcstombs(buff, Py_GetProgramFullPath(), 1024);
304  std::cout << "Python full: " << buff << std::endl;
305 */
306 #endif //FLOW123D_PYTHON_PREFIX
307 
308  // initialize the Python interpreter.
309  Py_Initialize();
310 
311 #ifdef FLOW123D_PYTHON_EXTRA_MODULES_PATH
312  // update module path, first get current system path (Py_GetPath)
313  // than append flow123d Python modules path to sys.path
314  std::string path = Py_GetPath();
315  path = path + ":" + std::string(FLOW123D_PYTHON_EXTRA_MODULES_PATH);
316  // conversion to non const char
317  char * path_char = const_cast<char *>(path.c_str());
318  PySys_SetPath (path_char);
319 
320 
321 #endif //FLOW123D_PYTHON_EXTRA_MODULES_PATH
322 
323  // call python and get paths available
324  PyObject *moduleMainString = PyString_FromString("__main__");
325  PyObject *moduleMain = PyImport_Import(moduleMainString);
326  PyRun_SimpleString(python_sys_path.c_str());
327  PyObject *func = PyObject_GetAttrString(moduleMain, "get_paths");
328  PyObject *args = PyTuple_New (0);
329  PyObject *result = PyObject_CallObject(func, args);
330  PythonLoader::check_error();
331 
332  // save value so we dont have to call python again
333  PythonLoader::sys_path = string(PyString_AsString(result));
334 }
335 
336 
337 
338 PythonRunning::~PythonRunning() {
339  Py_Finalize();
340 }
341 
342 } // close namespace internal
343 
344 #endif // FLOW123D_HAVE_PYTHON
void open_stream(Stream &stream) const
Definition: file_path.cc:211
Global macros to enhance readability and debugging, general constants.
Dedicated class for storing path to input and output files.
Definition: file_path.hh:48
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:45