BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
PyEmbeddedUtils.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Base/Utils/PyEmbeddedUtils.cpp
6 //! @brief IOmplements various functions from PyEmbeddedUtils namespace
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************** //
14 
15 #ifdef BORNAGAIN_PYTHON
16 
18 #include "Base/Utils/PythonCore.h"
19 #include "Base/Utils/SysUtils.h"
20 #include <iostream>
21 #include <sstream>
22 #include <stdexcept>
23 
25 {
26  std::string result;
27 #if PY_MAJOR_VERSION >= 3
28  PyObject* pyStr = PyUnicode_AsEncodedString(obj, "utf-8", "Error ~");
29  result = std::string(PyBytes_AsString(pyStr));
30  Py_DecRef(pyStr);
31 #else
32  result = std::string(PyString_AsString(obj));
33 #endif
34 
35  return result;
36 }
37 
38 std::vector<std::string> PyEmbeddedUtils::toVectorString(PyObject* obj)
39 {
40  std::vector<std::string> result;
41 
42  if (PyTuple_Check(obj)) {
43  for (Py_ssize_t i = 0; i < PyTuple_Size(obj); i++) {
44  PyObject* value = PyTuple_GetItem(obj, i);
45  result.push_back(toString(value));
46  }
47 
48  } else if (PyList_Check(obj)) {
49  for (Py_ssize_t i = 0; i < PyList_Size(obj); i++) {
50  PyObject* value = PyList_GetItem(obj, i);
51  result.push_back(toString(value));
52  }
53 
54  } else {
55  throw std::runtime_error("PyEmbeddedUtils::toVectorString() -> Error. Unexpected object.");
56  }
57 
58  return result;
59 }
60 
61 std::string PyEmbeddedUtils::toString(char* c)
62 {
63  if (c)
64  return c;
65  else
66  return "";
67 }
68 
69 std::string PyEmbeddedUtils::toString(wchar_t* c)
70 {
71  if (c) {
72  std::wstring wstr(c);
73  std::string result(wstr.begin(), wstr.end());
74  return result;
75  } else {
76  return "";
77  }
78 }
79 
80 void PyEmbeddedUtils::import_bornagain(const std::string& path)
81 {
82  if (!Py_IsInitialized()) {
83  Py_InitializeEx(0);
84 
85  if (!path.empty()) {
86  PyObject* sysPath = PySys_GetObject((char*)"path");
87  PyList_Append(sysPath, PyString_FromString(path.c_str()));
88  }
89 
90  // Stores signal handler before numpy's mess it up.
91  // This is to make ctrl-c working from terminal.
92 #ifndef _WIN32
93  PyOS_sighandler_t sighandler;
94  sighandler = PyOS_getsig(SIGINT);
95 #endif
96  PyObject* pmod = PyImport_ImportModule("bornagain");
97  if (!pmod) {
98  PyErr_Print();
99  throw std::runtime_error("Can't load bornagain");
100  }
101 
102  // restores single handler to make ctr-c alive.
103 #ifndef _WIN32
104  PyOS_setsig(SIGINT, sighandler);
105 #endif
106  }
107 }
108 
110 {
111  Py_InitializeEx(0);
112 
113  std::stringstream result;
114 
115  // Runtime environment
116  result << std::string(60, '=') << "\n";
117  result << "PATH: " << SysUtils::getenv("PATH") << "\n";
118  result << "PYTHONPATH: " << SysUtils::getenv("PYTHONPATH") << "\n";
119  result << "PYTHONHOME: " << SysUtils::getenv("PYTHONHOME") << "\n";
120 
121  // Embedded Python details
122  result << "Py_GetProgramName(): " << PyEmbeddedUtils::toString(Py_GetProgramName()) << "\n";
123  result << "Py_GetProgramFullPath(): " << PyEmbeddedUtils::toString(Py_GetProgramFullPath())
124  << "\n";
125  result << "Py_GetPath(): " << PyEmbeddedUtils::toString(Py_GetPath()) << "\n";
126  result << "Py_GetPythonHome(): " << PyEmbeddedUtils::toString(Py_GetPythonHome()) << "\n";
127 
128  // Runtime Python's sys.path
129  PyObject* sysPath = PySys_GetObject((char*)"path");
130  auto content = PyEmbeddedUtils::toVectorString(sysPath);
131  result << "sys.path: ";
132  for (auto s : content)
133  result << s << ",";
134  result << "\n";
135 
136  return result.str();
137 }
138 
139 // Attempt to retrieve Python stack trace
140 // https://stackoverflow.com/questions/1796510/accessing-a-python-traceback-from-the-c-api
141 
143 {
144  std::stringstream result;
145 
146  if (PyErr_Occurred()) {
147  PyObject *ptype, *pvalue, *ptraceback, *pystr;
148 
149  PyErr_Fetch(&ptype, &pvalue, &ptraceback);
150  pystr = PyObject_Str(pvalue);
151  if (char* str = PyString_AsString(pystr))
152  result << std::string(str) << "\n";
153 
154  PyObject* module_name = PyString_FromString("traceback");
155  PyObject* pyth_module = PyImport_Import(module_name);
156  Py_DecRef(module_name);
157 
158  if (pyth_module) {
159  result << "\n";
160  PyObject* pyth_func = PyObject_GetAttrString(pyth_module, "format_exception");
161  if (pyth_func && PyCallable_Check(pyth_func)) {
162  PyObject* pyth_val =
163  PyObject_CallFunctionObjArgs(pyth_func, ptype, pvalue, ptraceback, NULL);
164  if (pyth_val) {
165  pystr = PyObject_Str(pyth_val);
166  if (char* str = PyString_AsString(pystr))
167  result << std::string(str);
168  Py_DecRef(pyth_val);
169  }
170  }
171  result << "\n";
172  }
173  }
174 
175  result << "\n";
176  result << pythonRuntimeInfo();
177  result << "\n";
178 
179  return result.str();
180 }
181 
182 #endif // BORNAGAIN_PYTHON
Defines PyEmbeddedUtils namespace.
_object PyObject
Definition: PyObject.h:20
Includes python header and takes care of warnings.
Defines various stuff in namespace Utils.
std::string toString(PyObject *obj)
Converts PyObject into string, if possible, or throws exception.
std::vector< std::string > toVectorString(PyObject *obj)
Converts PyObject into vector of strings, if possible, or throws exception.
std::string pythonStackTrace()
Returns string representing python stack trace.
void import_bornagain(const std::string &path="")
Imports BornAgain from given location. If path is empty, tries to rely on PYTHONPATH.
std::string pythonRuntimeInfo()
Returns multi-line string representing PATH, PYTHONPATH, sys.path and other info.
std::string getenv(const std::string &name)
Returns environment variable.
Definition: SysUtils.cpp:48