Drake
call_python.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <string>
4 #include <vector>
5 
8 
9 /// @file
10 /// @brief Utilities for calling Python from C++
11 ///
12 /// Provides functionality similar to `call_matlab` (i.e., one-directional RPC),
13 /// leveraging an API similar to `pybind11`.
14 ///
15 /// @see call_python_test.cc for C++ examples.
16 // TODO(eric.cousineau): Add (untested) example usage in IPython notebook.
17 
18 namespace drake {
19 namespace common {
20 
21 // begin forward declarations
22 // These are necessary for `PythonApi`.
23 
24 class PythonRemoteVariable;
25 
26 // TODO(eric.cousineau): Generalize RPC marshaling so that we do not need to
27 // overload a function named `ToMatlabArray`.
28 void ToMatlabArray(const PythonRemoteVariable& var, MatlabArray* matlab_array);
29 
30 template <typename... Types>
31 PythonRemoteVariable CallPython(const std::string& function_name,
32  Types... args);
33 
34 template <typename... Types>
35 PythonRemoteVariable ToPythonTuple(Types... args);
36 
37 template <typename T>
38 PythonRemoteVariable NewPythonVariable(T value);
39 
40 namespace internal {
41 
42 class PythonItemPolicy;
43 class PythonAttrPolicy;
44 template <typename Policy>
46 
47 // end forward declarations
48 
51 
52 // Mimic pybind11's `object_api` and `accessor<>` setup, such that we can
53 // chain operations together more conveniently.
54 template <typename Derived>
55 class PythonApi {
56  public:
57  /// Calls object with given arguments, returning the remote result.
58  template <typename... Types>
59  PythonRemoteVariable operator()(Types... args) const;
60 
61  /// Accesses an attribute.
62  PythonAttrAccessor attr(const std::string& name) const;
63 
64  /// Accesses an item.
65  template <typename Type>
66  PythonItemAccessor operator[](Type key) const;
67 
68  /// Accesses a NumPy-friendly slice.
69  template <typename... Types>
70  PythonItemAccessor slice(Types... args) const;
71 
72  private:
73  // Provides type-cast view for CRTP implementation.
74  const Derived& derived() const {
75  return static_cast<const Derived&>(*this);
76  }
77 };
78 
79 } // namespace internal
80 
81 /// Presents variable stored in Python side.
82 class PythonRemoteVariable : public internal::PythonApi<PythonRemoteVariable> {
83  public:
85  // TODO(eric.cousineau): To support deletion, disable copy constructor, only
86  // allow moving (if we want to avoid needing a global reference counter).
87  int64_t unique_id() const { return unique_id_; }
88 
89  private:
90  const int64_t unique_id_{};
91 };
92 
93 namespace internal {
94 
95 // Gets/sets an object's attribute.
97  public:
98  using KeyType = std::string;
100  const KeyType& key) {
101  return CallPython("getattr", obj, key);
102  }
104  const KeyType& key,
106  return CallPython("setattr", obj, key, value);
107  }
108 };
109 
110 // Gets/sets an object's item.
112  public:
115  const KeyType& key) {
116  return CallPython("getitem", obj, key);
117  }
119  const KeyType& key,
121  return CallPython("setitem", obj, key, value);
122  }
123 };
124 
125 // API-consistent mechanism to access a portion of an object (item or attr).
126 template <typename Policy>
127 class PythonAccessor : public internal::PythonApi<PythonAccessor<Policy>> {
128  public:
129  using KeyType = typename Policy::KeyType;
130 
131  // Given a variable (and key), makes a PythonAccessor.
133  : obj_(obj), key_(key) {}
134 
135  // Copying a PythonAccessor aliases the original remote variable (reference
136  // semantics), it does not create a new remote variable.
137  PythonAccessor(const PythonAccessor&) = default;
138 
139  // Implicitly converts to a PythonRemoteVariable.
140  operator PythonRemoteVariable() const { return value(); }
141 
142  // Assigning from another PythonAccessor delegates to set_value from that
143  // `value`'s underlying PythonRemoteVariable.
145  return set_value(value);
146  }
147 
148  // Assigning from another PythonRemoteVariable delegates to set_value from it.
150  return set_value(value);
151  }
152 
153  // Assigning from some literal value creates a new PythonRemoveVariable to
154  // bind the value.
155  template <typename T>
157  return set_value(NewPythonVariable(value));
158  }
159 
160  private:
161  PythonRemoteVariable value() const { return Policy::get(obj_, key_); }
162  PythonRemoteVariable set_value(const PythonRemoteVariable& value) const {
163  return Policy::set(obj_, key_, value);
164  }
166  KeyType key_;
167 };
168 
169 // Now that we have our types defined, we can implement the functionality for
170 // the API.
171 template <typename Derived>
172 PythonAttrAccessor PythonApi<Derived>::attr(const std::string& name) const {
173  return {derived(), name};
174 }
175 
176 template <typename Derived>
177 template <typename... Types>
179  return CallPython("call", derived(), args...);
180 }
181 
182 template <typename Derived>
183 template <typename Type>
185  return {derived(), NewPythonVariable(key)};
186 }
187 
188 template <typename Derived>
189 template <typename... Types>
191  return {derived(), CallPython("make_slice_arg", args...)};
192 }
193 
194 void PublishCallPython(const MatlabRPC& msg);
195 
196 } // namespace internal
197 
198 /// Initializes `CallPython` for a given file.
199 /// If this function is not called, then the file defaults to `/tmp/python_rpc`.
200 /// @throws std::runtime_error If either this function or `CallPython` have
201 /// already been called.
202 void CallPythonInit(const std::string& filename);
203 
204 /// Calls a Python client with a given function and arguments, returning
205 /// a handle to the result.
206 template <typename... Types>
207 PythonRemoteVariable CallPython(const std::string& function_name,
208  Types... args) {
209  PythonRemoteVariable output;
210  MatlabRPC msg;
211  msg.add_lhs(output.unique_id());
212  internal::AssembleCallMatlabMsg(&msg, args...);
213  msg.set_function_name(function_name);
215  return output;
216 }
217 
218 /// Creates a tuple in Python.
219 template <typename... Types>
221  return CallPython("make_tuple", args...);
222 }
223 
224 /// Creates a keyword-argument list to be unpacked.
225 /// @param args Argument list in the form of (key1, value1, key2, value2, ...).
226 template <typename... Types>
228  return CallPython("make_kwargs", args...);
229 }
230 
231 /// Creates a new remote variable with the corresponding value set.
232 template <typename T>
234  return CallPython("pass_through", value);
235 }
236 
237 } // namespace common
238 } // namespace drake
Definition: call_python.h:55
std::string KeyType
Definition: call_python.h:98
PythonItemAccessor operator[](Type key) const
Accesses an item.
Definition: call_python.h:184
YAML::Node get(const YAML::Node &parent, const std::string &key)
Definition: yaml_util.cc:61
double value
Definition: wrap_test_util_py.cc:12
PythonRemoteVariable ToPythonKwargs(Types...args)
Creates a keyword-argument list to be unpacked.
Definition: call_python.h:227
void PublishCallPython(const MatlabRPC &msg)
Definition: call_python.cc:56
Definition: automotive_demo.cc:90
Definition: call_python.h:45
void CallPythonInit(const std::string &filename)
Initializes CallPython for a given file.
Definition: call_python.cc:52
int64_t unique_id() const
Definition: call_python.h:87
PythonRemoteVariable operator=(const PythonAccessor &value)
Definition: call_python.h:144
Utilities for calling Matlab from C++.
PythonRemoteVariable NewPythonVariable(T value)
Creates a new remote variable with the corresponding value set.
Definition: call_python.h:233
PythonRemoteVariable operator=(const PythonRemoteVariable &value)
Definition: call_python.h:149
PythonRemoteVariable ToPythonTuple(Types...args)
Creates a tuple in Python.
Definition: call_python.h:220
void AssembleCallMatlabMsg(MatlabRPC *)
Definition: call_matlab.h:80
void ToMatlabArray(const MatlabRemoteVariable &var, MatlabArray *matlab_array)
Definition: call_matlab.cc:30
PythonAttrAccessor attr(const std::string &name) const
Accesses an attribute.
Definition: call_python.h:172
PythonRemoteVariable CallPython(const std::string &function_name, Types...args)
Calls a Python client with a given function and arguments, returning a handle to the result...
Definition: call_python.h:207
Definition: call_python.h:111
::testing::Types< RungeKutta3Integrator< double > > Types
Definition: runge_kutta3_integrator_test.cc:23
Definition: call_python.h:96
PythonRemoteVariable operator()(Types...args) const
Calls object with given arguments, returning the remote result.
Definition: call_python.h:178
typename Policy::KeyType KeyType
Definition: call_python.h:129
PythonItemAccessor slice(Types...args) const
Accesses a NumPy-friendly slice.
Definition: call_python.h:190
PythonRemoteVariable operator=(const T &value)
Definition: call_python.h:156
PythonAccessor(PythonRemoteVariable obj, const KeyType &key)
Definition: call_python.h:132
Presents variable stored in Python side.
Definition: call_python.h:82