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