Flow123d  build_with_4.0.3-ea9b1ad
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 #include "system/python_loader.hh"
21 
22 //#include "system/system.hh"
23 #include "system/file_path.hh"
24 #include <string>
25 //#include <iostream>
26 #include <fstream>
27 //#include <sstream>
28 //#include <pybind11/pybind11.h>
29 #include <pybind11/embed.h> // everything needed for embedding
30 
31 using namespace std;
32 namespace py = pybind11;
33 
34 
35 void PythonLoader::initialize()
36 {
37  static internal::PythonRunning _running;
38 }
39 
40 
41 py::module_ PythonLoader::load_module_from_file(const std::string& fname) {
42  initialize();
43 
44  std::ifstream file_stream;
45  FilePath f_path(fname, FilePath::input_file);
46  f_path.open_stream(file_stream); // checks if file exists
47  std::string parent_path = f_path.parent_path(); // add path to PythonPath
48  PythonLoader::add_sys_path(parent_path);
49 
50  string module_name = f_path.stem();
51  py::module_ module;
52  try {
53  module = py::module_::import(module_name.c_str());
54  } catch (const py::error_already_set &ex) {
56  }
57  return module;
58 }
59 
60 
61 
62 py::module_ PythonLoader::load_module_from_string(const std::string& module_name, const std::string& func_name, const std::string& source_string) {
63  initialize();
64 
65  py::module_ user_module;
66  py::dict globals = py::globals();
67 
68  try {
69  py::exec(source_string.c_str(), globals, globals);
70  user_module = py::module::create_extension_module(module_name.c_str(), "", new PyModuleDef());
71  user_module.add_object(func_name.c_str(), globals[func_name.c_str()]);
72  } catch (const py::error_already_set &ex) {
74  }
75  return user_module;
76 }
77 
78 py::module_ PythonLoader::load_module_by_name(const std::string& module_name) {
79  initialize();
80 
81  // import module by dot separated path and its name
82  py::module_ module_object;
83  try {
84  module_object = py::module_::import (module_name.c_str());
85  } catch (const py::error_already_set &ex) {
87  }
88 
89  return module_object;
90 }
91 
92 
93 void PythonLoader::throw_error(const py::error_already_set &ex) {
94  PyObject *ptype = ex.type().ptr();
95  PyObject *pvalue = ex.value().ptr();
96  PyObject *ptraceback = ex.trace().ptr();
97 
98  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
99  string error_description = string(PyUnicode_AsUTF8(PyObject_Str(pvalue)));
100 
101  // clear error indicator
102  PyErr_Clear();
103 
104  /* See if we can get a full traceback */
105  PyObject *traceback_module = PyImport_ImportModule("traceback");
106 
107  string str_traceback;
108  if (traceback_module && ptraceback) {
109  PyObject *format_tb_method = PyObject_GetAttrString(traceback_module, "format_tb");
110  if (format_tb_method) {
111  PyObject * traceback_lines = PyObject_CallFunctionObjArgs(format_tb_method, ptraceback, NULL);
112  if (traceback_lines) {
113  PyObject * join_str = PyUnicode_FromString("");
114  PyObject * join = PyObject_GetAttrString(join_str, "join");
115  PyObject * message = PyObject_CallFunctionObjArgs(join, traceback_lines, NULL);
116  if (message) {
117  str_traceback = string(PyUnicode_AsUTF8(PyObject_Str(message)));
118  }
119  Py_DECREF(traceback_lines);
120  Py_DECREF(join_str);
121  Py_DECREF(join);
122  Py_DECREF(message);
123  }
124  }
125  }
126 
127  // get value of python's "sys.path"
128  auto path_vec = PythonLoader::get_python_path();
129  string python_path;
130  for (auto p : path_vec) {
131  python_path += p + "\n";
132  }
133 
134  // construct error message
135  string py_message =
136  "\nType: " + string(PyUnicode_AsUTF8(PyObject_Str( ex.type().ptr() ))) + "\n"
137  + "Message: " + string(PyUnicode_AsUTF8(PyObject_Str( ex.value().attr("args").ptr() ))) + "\n"
138  + "Traceback: \n" + str_traceback
139  + "--------------------------------------------------------\n\n"
140  + "Python sys.path: " + "\n" + python_path
141  + "--------------------------------------------------------\n\n";
142 
143  THROW(ExcPythonError() << EI_PythonMessage( py_message ));
144 }
145 
146 
147 
148 void PythonLoader::add_sys_path(const std::string &path)
149 {
150  py::module_ sys = py::module_::import("sys");
151  auto sys_paths = sys.attr("path");
152  // Checks if path exists
153  for (auto sp : sys_paths) {
154  std::string sys_path = sp.cast<std::string>();
155  if (sys_path == path) {
156  WarningOut() << "Path '" << sys_path << "' already exists in Python sys path and cannot be added repeatedly!";
157  return;
158  }
159  }
160 
161  // Adds only one time
162  sys_paths.attr("append")(path);
163 }
164 
165 std::vector<std::string> PythonLoader::get_python_path()
166 {
167  py::module_ sys = py::module_::import("sys");
168  auto sys_paths = sys.attr("path");
169  std::vector<std::string> path_vec;
170  for (auto sp : sys_paths) {
171  std::string sys_path = sp.cast<std::string>();
172  path_vec.push_back(sys_path);
173  }
174 
175  return path_vec;
176 }
177 
178 
179 
180 namespace internal {
181 
182 PythonRunning::PythonRunning()
183 {
184  // initialize the Python interpreter.
185  py::initialize_interpreter();
186  std::string flowpy_path = std::string(FLOW123D_SOURCE_DIR) + "/src/python";
187  std::string fieldproxypy_path = std::string(FLOW123D_SOURCE_DIR) + "/build_tree/src";
188  PythonLoader::add_sys_path(flowpy_path);
189  PythonLoader::add_sys_path(fieldproxypy_path);
190 
191 #ifdef FLOW123D_PYTHON_EXTRA_MODULES_PATH
192  // update module path, append flow123d Python modules path to sys.path
193  std::stringstream extra_paths(FLOW123D_PYTHON_EXTRA_MODULES_PATH);
194  std::string extra_path;
195  while ( std::getline(extra_paths, extra_path, ':') )
196  {
197  PythonLoader::add_sys_path(extra_path);
198  }
199 #endif //FLOW123D_PYTHON_EXTRA_MODULES_PATH
200 }
201 
202 
203 
204 
205 PythonRunning::~PythonRunning() {
206  py::finalize_interpreter();
207 }
208 
209 } // close namespace internal
Dedicated class for storing path to input and output files.
Definition: file_path.hh:54
@ input_file
Definition: file_path.hh:68
Global macros to enhance readability and debugging, general constants.
#define THROW(whole_exception_expr)
Wrapper for throw. Saves the throwing point.
Definition: exceptions.hh:53
#define WarningOut()
Macro defining 'warning' record of log.
Definition: logger.hh:278
void throw_error(spirit_namespace::position_iterator< Iter_type > i, const std::string &reason)