Drake
call_python.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include <string>
4 #include <vector>
5 
6 #include "drake/common/copyable_unique_ptr.h"
7 #include "drake/common/proto/call_matlab.h"
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;
131  : obj_(obj), key_(key) {}
132 
133  operator PythonRemoteVariable() const { return value(); }
134 
136  return set_value(value);
137  }
138 
140  return set_value(value);
141  }
142 
143  template <typename T>
145  return set_value(NewPythonVariable(value));
146  }
147 
148  private:
149  PythonRemoteVariable value() const { return Policy::get(obj_, key_); }
150  PythonRemoteVariable set_value(const PythonRemoteVariable& value) const {
151  return Policy::set(obj_, key_, value);
152  }
154  KeyType key_;
155 };
156 
157 // Now that we have our types defined, we can implement the functionality for
158 // the API.
159 template <typename Derived>
160 PythonAttrAccessor PythonApi<Derived>::attr(const std::string& name) const {
161  return {derived(), name};
162 }
163 
164 template <typename Derived>
165 template <typename... Types>
167  return CallPython("call", derived(), args...);
168 }
169 
170 template <typename Derived>
171 template <typename Type>
173  return {derived(), NewPythonVariable(key)};
174 }
175 
176 template <typename Derived>
177 template <typename... Types>
179  return {derived(), CallPython("make_slice_arg", args...)};
180 }
181 
182 void PublishCallPython(const MatlabRPC& msg);
183 
184 } // namespace internal
185 
186 /// Initializes `CallPython` for a given file.
187 /// If this function is not called, then the file defaults to `/tmp/python_rpc`.
188 /// @throws std::runtime_error If either this function or `CallPython` have
189 /// already been called.
190 void CallPythonInit(const std::string& filename);
191 
192 /// Calls a Python client with a given function and arguments, returning
193 /// a handle to the result.
194 template <typename... Types>
195 PythonRemoteVariable CallPython(const std::string& function_name,
196  Types... args) {
197  PythonRemoteVariable output;
198  MatlabRPC msg;
199  msg.add_lhs(output.unique_id());
200  internal::AssembleCallMatlabMsg(&msg, args...);
201  msg.set_function_name(function_name);
203  return output;
204 }
205 
206 /// Creates a tuple in Python.
207 template <typename... Types>
209  return CallPython("make_tuple", args...);
210 }
211 
212 /// Creates a keyword-argument list to be unpacked.
213 /// @param args Argument list in the form of (key1, value1, key2, value2, ...).
214 template <typename... Types>
216  return CallPython("make_kwargs", args...);
217 }
218 
219 /// Creates a new remote variable with the corresponding value set.
220 template <typename T>
222  return CallPython("pass_through", value);
223 }
224 
225 } // namespace common
226 } // 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:172
YAML::Node get(const YAML::Node &parent, const std::string &key)
Definition: yaml_util.cc:61
double T
Definition: lifetime_test_util_py.cc:14
PythonRemoteVariable ToPythonKwargs(Types...args)
Creates a keyword-argument list to be unpacked.
Definition: call_python.h:215
void PublishCallPython(const MatlabRPC &msg)
Definition: call_python.cc:55
Definition: automotive_demo.cc:89
Definition: call_python.h:45
void CallPythonInit(const std::string &filename)
Initializes CallPython for a given file.
Definition: call_python.cc:51
int64_t unique_id() const
Definition: call_python.h:87
PythonRemoteVariable operator=(const PythonAccessor &value)
Definition: call_python.h:135
PythonRemoteVariable NewPythonVariable(T value)
Creates a new remote variable with the corresponding value set.
Definition: call_python.h:221
PythonRemoteVariable operator=(const PythonRemoteVariable &value)
Definition: call_python.h:139
PythonRemoteVariable ToPythonTuple(Types...args)
Creates a tuple in Python.
Definition: call_python.h:208
int value
Definition: copyable_unique_ptr_test.cc:61
void AssembleCallMatlabMsg(MatlabRPC *)
Definition: call_matlab.h:80
void ToMatlabArray(const MatlabRemoteVariable &var, MatlabArray *matlab_array)
Definition: call_matlab.cc:29
PythonAttrAccessor attr(const std::string &name) const
Accesses an attribute.
Definition: call_python.h:160
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:195
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:166
typename Policy::KeyType KeyType
Definition: call_python.h:129
PythonItemAccessor slice(Types...args) const
Accesses a NumPy-friendly slice.
Definition: call_python.h:178
PythonRemoteVariable operator=(const T &value)
Definition: call_python.h:144
PythonAccessor(PythonRemoteVariable obj, const KeyType &key)
Definition: call_python.h:130
Presents variable stored in Python side.
Definition: call_python.h:82