substitution_python.cpp
Go to the documentation of this file.
1 // Evaluates $(eval ...) substitutions
2 // Author: Max Schwarz <max.schwarz@uni-bonn.de>
3 
4 #include "substitution_python.h"
5 #include "substitution.h"
6 
7 #include "launch_config.h"
8 
9 #include <boost/python/exec.hpp>
10 #include <boost/python/extract.hpp>
11 #include <boost/python/dict.hpp>
12 #include <boost/python/make_function.hpp>
13 #include <boost/python/import.hpp>
14 
15 #include <boost/lexical_cast.hpp>
16 
17 #include <boost/mpl/vector.hpp>
18 
19 namespace py = boost::python;
20 
21 namespace rosmon
22 {
23 namespace launch
24 {
25 
26 #if HAVE_PYTHON
27 
28 template<class F>
29 py::object make_handler(F f)
30 {
31  return py::make_function(
32  f,
33  py::default_call_policies(),
34  boost::mpl::vector<std::string, const std::string&>()
35  );
36 }
37 
38 template<class F>
39 py::object make_handler0(F f)
40 {
41  return py::make_function(
42  f,
43  py::default_call_policies(),
44  boost::mpl::vector<std::string>()
45  );
46 }
47 
48 template<class F>
49 py::object make_handler2(F f)
50 {
51  return py::make_function(
52  f,
53  py::default_call_policies(),
54  boost::mpl::vector<std::string, const std::string&, const std::string&>()
55  );
56 }
57 
58 static py::object toPython(const std::string& value)
59 {
60  if(value == "true" || value == "True")
61  return py::object(true);
62 
63  if(value == "false" || value == "False")
64  return py::object(false);
65 
66  try { return py::object(boost::lexical_cast<int>(value)); }
67  catch(boost::bad_lexical_cast&) {}
68 
69  try { return py::object(boost::lexical_cast<float>(value)); }
70  catch(boost::bad_lexical_cast&) {}
71 
72  return py::object(value);
73 }
74 
75 static py::object pythonArg(const std::string& name, ParseContext& context)
76 {
77  return toPython(substitutions::arg(name, context));
78 }
79 
80 static bool g_initialized = false;
81 
82 std::string evaluatePython(const std::string& input, ParseContext& context)
83 {
84  if(!g_initialized)
85  {
86  Py_Initialize();
87  g_initialized = true;
88  }
89 
90  py::object main_module = py::import("__main__");
91  py::dict global = py::dict(main_module.attr("__dict__"));
92  py::dict local;
93 
94  // Add arguments
95  {
96  for(auto& arg : context.arguments())
97  {
98  local[arg.first] = toPython(arg.second);
99  }
100  }
101 
102  // Add substitution functions
103  {
104  local["anon"] = make_handler([&context](const std::string& name){
105  return substitutions::anon(name, context);
106  });
107  local["arg"] = py::make_function([&context](const std::string& name) -> py::object{
108  return pythonArg(name, context);
109  }, py::default_call_policies(), boost::mpl::vector<py::object, const std::string&>());
110  local["dirname"] = make_handler0([&context](){
111  return substitutions::dirname(context);
112  });
113  local["env"] = make_handler(substitutions::env);
114  local["optenv"] = make_handler2(substitutions::optenv);
115 
116  local["find"] = make_handler(substitutions::find_stupid);
117  }
118 
119  // Import math
120  {
121  py::object math = py::import("math");
122  py::object mathDict = math.attr("__dict__");
123  global.update(mathDict);
124  }
125 
126  // Lowercase booleans (why, roslaunch?)
127  {
128  global["true"] = py::object(true);
129  global["false"] = py::object(false);
130  }
131 
132  py::object result;
133  try
134  {
135  result = py::eval(input.c_str(), global, local);
136  }
137  catch(py::error_already_set&)
138  {
139  std::stringstream ss;
140  ss << "Caught Python exception while evaluating $(eval " << input << "):\n";
141 
142  PyObject *e, *v, *t;
143  PyErr_Fetch(&e, &v, &t);
144 
145  try
146  {
147  py::object t = py::extract<py::object>(e);
148  py::object t_name = t.attr("__name__");
149  std::string typestr = py::extract<std::string>(t_name);
150 
151  ss << typestr << ": ";
152  }
153  catch(py::error_already_set const &)
154  {}
155 
156  try
157  {
158  py::object vo = py::extract<py::object>(v);
159  std::string valuestr = py::extract<std::string>(vo.attr("__str__")());
160 
161  ss << valuestr;
162  }
163  catch(py::error_already_set const &)
164  {
165  ss << "<no str() handler>";
166  }
167 
168  throw SubstitutionException(ss.str());
169  }
170 
171 #if PY_MAJOR_VERSION >= 3
172  if(PyUnicode_Check(result.ptr()))
173  {
174  if(PyUnicode_READY(result.ptr()) != 0)
175  throw SubstitutionException("Could not read unicode object");
176 
177  return std::string(PyUnicode_AsUTF8(result.ptr()));
178  }
179 #else
180  if(PyString_Check(result.ptr()))
181  return py::extract<std::string>(result);
182 #endif
183 
184  if(PyBool_Check(result.ptr()))
185  {
186  if(py::extract<bool>(result))
187  return "true";
188  else
189  return "false";
190  }
191 
192 #if PY_MAJOR_VERSION >= 3
193  if(PyLong_Check(result.ptr()))
194  return std::to_string(py::extract<int64_t>(result));
195 #else
196  if(PyInt_Check(result.ptr()) || PyLong_Check(result.ptr()))
197  {
198  return std::to_string(py::extract<int64_t>(result));
199  }
200 #endif
201 
202  if(PyFloat_Check(result.ptr()))
203  {
204  // std::to_string has low precision here, so use boost::lexical_cast
205  return boost::lexical_cast<std::string>(
206  py::extract<float>(result)()
207  );
208  }
209 
210  throw SubstitutionException::format("$(eval '{}'): Got unknown python return type", input);
211 }
212 
213 double evaluateROSParamPython(const std::string& input)
214 {
215  if(!g_initialized)
216  {
217  Py_Initialize();
218  g_initialized = true;
219  }
220 
221  py::object main_module = py::import("__main__");
222  py::dict global = py::dict(main_module.attr("__dict__"));
223  py::dict local;
224 
225  // Import math
226  {
227  py::object math = py::import("math");
228  py::object mathDict = math.attr("__dict__");
229  global.update(mathDict);
230  }
231 
232  py::object result;
233  try
234  {
235  result = py::eval(input.c_str(), global, local);
236  }
237  catch(py::error_already_set&)
238  {
239  std::stringstream ss;
240  ss << "Caught Python exception while evaluating rosparam expression '" << input << "'):\n";
241 
242  PyObject *e, *v, *t;
243  PyErr_Fetch(&e, &v, &t);
244 
245  try
246  {
247  py::object t = py::extract<py::object>(e);
248  py::object t_name = t.attr("__name__");
249  std::string typestr = py::extract<std::string>(t_name);
250 
251  ss << typestr << ": ";
252  }
253  catch(py::error_already_set const &)
254  {}
255 
256  try
257  {
258  py::object vo = py::extract<py::object>(v);
259  std::string valuestr = py::extract<std::string>(vo.attr("__str__")());
260 
261  ss << valuestr;
262  }
263  catch(py::error_already_set const &)
264  {
265  ss << "<no str() handler>";
266  }
267 
268  throw SubstitutionException(ss.str());
269  }
270 
271  try
272  {
273  return py::extract<double>(result);
274  }
275  catch(py::error_already_set&)
276  {
277  throw SubstitutionException::format("got strange python type from rosparam python expression (should be numeric): '{}'", input);
278  }
279 }
280 
281 #else // HAVE_PYTHON
282 
283 std::string evaluatePython(const std::string& input, ParseContext& context)
284 {
285  throw SubstitutionException(
286  "rosmon was built without python support, so I cannot parse $(eval ...) substitution args."
287  );
288 }
289 
290 double evaluateROSParamPython(const std::string& input)
291 {
292  try
293  {
294  return boost::lexical_cast<double>(input);
295  }
296  catch(boost::bad_lexical_cast)
297  {
299  "rosmon was built without python support, so I cannot parse complex rosparam expressions (expression: {})",
300  input
301  );
302  }
303 }
304 
305 #endif
306 
307 }
308 }
std::string anon(const std::string &name, ParseContext &context)
static SubstitutionException format(const char *format, const Args &... args)
Definition: substitution.h:33
std::string evaluatePython(const std::string &input, ParseContext &context)
Evaluate a $(eval ...) python expression.
#define PyUnicode_AsUTF8
float f(float t)
Definition: husl.c:142
double evaluateROSParamPython(const std::string &input)
Evaluate a deg(...) rosparam expression.
static bool g_initialized
std::string dirname(const ParseContext &context)
std::string optenv(const std::string &name, const std::string &defaultValue)
std::string env(const std::string &name)
std::string arg(const std::string &name, const ParseContext &context)
std::string find_stupid(const std::string &name)
$(find ...) which always gives rospack find results


rosmon_core
Author(s): Max Schwarz
autogenerated on Fri Jun 16 2023 02:15:06