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