HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyInvoke.h
Go to the documentation of this file.
1 //
2 // Copyright 2021 Pixar
3 //
4 // Licensed under the Apache License, Version 2.0 (the "Apache License")
5 // with the following modification; you may not use this file except in
6 // compliance with the Apache License and the following modification to it:
7 // Section 6. Trademarks. is deleted and replaced with:
8 //
9 // 6. Trademarks. This License does not grant permission to use the trade
10 // names, trademarks, service marks, or product names of the Licensor
11 // and its affiliates, except as required to comply with Section 4(c) of
12 // the License and to reproduce the content of the NOTICE file.
13 //
14 // You may obtain a copy of the Apache License at
15 //
16 // http://www.apache.org/licenses/LICENSE-2.0
17 //
18 // Unless required by applicable law or agreed to in writing, software
19 // distributed under the Apache License with the above modification is
20 // distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
21 // KIND, either express or implied. See the Apache License for the specific
22 // language governing permissions and limitations under the Apache License.
23 //
24 #ifndef PXR_BASE_TF_PY_INVOKE_H
25 #define PXR_BASE_TF_PY_INVOKE_H
26 
27 /// \file
28 /// Flexible, high-level interface for calling Python functions.
29 
30 #include "pxr/pxr.h"
31 #include "pxr/base/tf/api.h"
32 
34 #include "pxr/base/tf/pyError.h"
36 #include "pxr/base/tf/pyLock.h"
38 
39 #include <hboost/python/dict.hpp>
40 #include <hboost/python/extract.hpp>
41 #include <hboost/python/list.hpp>
42 #include <hboost/python/object.hpp>
43 
44 #include <cstddef>
45 #include <memory>
46 #include <string>
47 #include <type_traits>
48 
50 
51 ////////////////////////////////////////////////////////////////////////////////
52 // To-Python arg conversion
53 
54 #ifndef doxygen
55 
56 // Convert any type to hboost::python::object.
57 template <typename T>
59 {
60  return hboost::python::object(value);
61 }
62 
63 // Convert nullptr to None.
64 TF_API hboost::python::object Tf_ArgToPy(const std::nullptr_t &value);
65 
66 #endif // !doxygen
67 
68 ////////////////////////////////////////////////////////////////////////////////
69 // Keyword arg specification
70 
71 /// Wrapper object for a keyword-argument pair in a call to TfPyInvoke*. Any
72 /// value type may be provided, as long as it is convertible to Python.
73 /// Typically passed as an inline temporary object:
74 ///
75 /// \code
76 /// const bool ok = TfPyInvoke(
77 /// "MyModule", "MyFunction",
78 /// arg1value, arg2value, TfPyKwArg("arg4", arg4value));
79 /// \endcode
80 ///
81 /// \code{.py}
82 /// def MyFunction(arg1, arg2, arg3 = None, arg4 = None, arg5 = None):
83 /// # ...
84 /// \endcode
85 ///
86 struct TfPyKwArg
87 {
88  template <typename T>
89  TfPyKwArg(const std::string &nameIn, const T &valueIn)
90  : name(nameIn)
91  {
92  // Constructing hboost::python::object requires the GIL.
93  TfPyLock lock;
94 
95  // The object constructor throws if the type is not convertible.
96  value = Tf_ArgToPy(valueIn);
97  }
98 
101 };
102 
103 ////////////////////////////////////////////////////////////////////////////////
104 // Argument collection by variadic template functions
105 
106 #ifndef doxygen
107 
108 // Variadic helper: trivial base case.
110  hboost::python::dict *kwArgsOut);
111 
112 // Poisoned variadic template helper that provides an error message when
113 // non-keyword args are used after keyword args.
114 template <typename Arg, typename... RestArgs>
116  hboost::python::dict *kwArgsOut,
117  const Arg &kwArg,
118  RestArgs... rest)
119 {
120  // This assertion will always be false, since TfPyKwArg will select the
121  // overload below instead.
122  static_assert(
124  "Non-keyword args not allowed after keyword args");
125 }
126 
127 // Recursive variadic template helper for keyword args.
128 template <typename... RestArgs>
130  hboost::python::dict *kwArgsOut,
131  const TfPyKwArg &kwArg,
132  RestArgs... rest)
133 {
134  // Store mapping in kwargs dict.
135  (*kwArgsOut)[kwArg.name] = kwArg.value.Get();
136 
137  // Recurse to handle next arg.
138  Tf_BuildPyInvokeKwArgs(kwArgsOut, rest...);
139 }
140 
141 // Variadic helper: trivial base case.
143  hboost::python::list *posArgsOut,
144  hboost::python::dict *kwArgsOut);
145 
146 // Recursive general-purpose variadic template helper.
147 template <typename Arg, typename... RestArgs>
149  hboost::python::list *posArgsOut,
150  hboost::python::dict *kwArgsOut,
151  const Arg &arg,
152  RestArgs... rest)
153 {
154  // Convert value to Python, and store in args list.
155  // The object constructor throws if the type is not convertible.
156  posArgsOut->append(Tf_ArgToPy(arg));
157 
158  // Recurse to handle next arg.
159  Tf_BuildPyInvokeArgs(posArgsOut, kwArgsOut, rest...);
160 }
161 
162 // Recursive variadic template helper for keyword args.
163 template <typename... RestArgs>
165  hboost::python::list *posArgsOut,
166  hboost::python::dict *kwArgsOut,
167  const TfPyKwArg &kwArg,
168  RestArgs... rest)
169 {
170  // Switch to kwargs-only processing, enforcing (at compile time) the Python
171  // rule that there may not be non-kwargs after kwargs. If we relaxed this
172  // rule, some strange argument ordering could occur.
173  Tf_BuildPyInvokeKwArgs(kwArgsOut, kwArg, rest...);
174 }
175 
176 #endif // !doxygen
177 
178 ////////////////////////////////////////////////////////////////////////////////
179 // Declarations
180 
181 #ifndef doxygen
182 
183 // Helper for TfPyInvokeAndExtract.
185  const std::string &moduleName,
186  const std::string &callableExpr,
187  const hboost::python::list &posArgs,
188  const hboost::python::dict &kwArgs,
189  hboost::python::object *resultObjOut);
190 
191 // Forward declaration.
192 template <typename... Args>
194  const std::string &moduleName,
195  const std::string &callableExpr,
196  hboost::python::object *resultOut,
197  Args... args);
198 
199 #endif // !doxygen
200 
201 ////////////////////////////////////////////////////////////////////////////////
202 // Main entry points
203 
204 /// Call a Python function and obtain its return value.
205 ///
206 /// Example:
207 /// \code
208 /// // Call MyModule.MyFunction(arg1, arg2), which returns a string.
209 /// std::string result;
210 /// const bool ok = TfPyInvokeAndExtract(
211 /// "MyModule", "MyFunction", &result, arg1Value, arg2Value);
212 /// \endcode
213 ///
214 /// \p moduleName is the name of the module in which to find the function. This
215 /// name will be directly imported in an \c import statement, so anything that
216 /// you know is in \c sys.path should work. The module name will also be
217 /// prepended to \p callableExpr to look up the function.
218 ///
219 /// \p callableExpr is a Python expression that, when appended to \p moduleName
220 /// (with an intervening dot), yields a callable object. Typically this is just
221 /// a function name, optionally prefixed with object names (such as a class in
222 /// which the callable resides).
223 ///
224 /// \p resultOut is a pointer that will receive the Python function's return
225 /// value. A from-Python converter must be registered for the type of \c
226 /// *resultOut.
227 ///
228 /// \p args is zero or more function arguments, of any types for which to-Python
229 /// conversions are registered. Any \c nullptr arguments are converted to \c
230 /// None. \p args may also contain TfPyKwArg objects to pass keyword arguments.
231 /// As in Python, once a keyword argument is passed, all remaining arguments
232 /// must also be keyword arguments.
233 ///
234 /// The return value of TfPyInvokeAndExtract is true if the call completed,
235 /// false otherwise. When the return value is false, at least one TfError
236 /// should have been raised, describing the failure. TfPyInvokeAndExtract never
237 /// raises exceptions.
238 ///
239 /// It should be safe to call this function without doing any other setup
240 /// first. It is not necessary to call TfPyInitialize or lock the GIL; this
241 /// function does those things itself.
242 ///
243 /// If you don't need the function's return value, call TfPyInvoke instead.
244 ///
245 /// If you need the function's return value, but the return value isn't
246 /// guaranteed to be a consistent type that's convertible to C++, call
247 /// TfPyInvokeAndReturn instead. This includes cases where the function's
248 /// return value may be \c None.
249 ///
250 template <typename Result, typename... Args>
252  const std::string &moduleName,
253  const std::string &callableExpr,
254  Result *resultOut,
255  Args... args)
256 {
257  if (!resultOut) {
258  TF_CODING_ERROR("Bad pointer to TfPyInvokeAndExtract");
259  return false;
260  }
261 
262  // Init Python and grab the GIL.
263  TfPyInitialize();
264  TfPyLock lock;
265 
266  hboost::python::object resultObj;
267  if (!TfPyInvokeAndReturn(
268  moduleName, callableExpr, &resultObj, args...)) {
269  return false;
270  }
271 
272  // Extract return value.
273  hboost::python::extract<Result> extractor(resultObj);
274  if (!extractor.check()) {
275  TF_CODING_ERROR("Result type mismatched or not convertible");
276  return false;
277  }
278  *resultOut = extractor();
279 
280  return true;
281 }
282 
283 /// A version of TfPyInvokeAndExtract that provides the Python function's return
284 /// value as a \c hboost::python::object, rather than extracting a particular C++
285 /// type from it.
286 ///
287 template <typename... Args>
289  const std::string &moduleName,
290  const std::string &callableExpr,
291  hboost::python::object *resultOut,
292  Args... args)
293 {
294  if (!resultOut) {
295  TF_CODING_ERROR("Bad pointer to TfPyInvokeAndExtract");
296  return false;
297  }
298 
299  // Init Python and grab the GIL.
300  TfPyInitialize();
301  TfPyLock lock;
302 
303  try {
304  // Convert args to Python and store in list+dict form.
305  hboost::python::list posArgs;
306  hboost::python::dict kwArgs;
307  Tf_BuildPyInvokeArgs(&posArgs, &kwArgs, args...);
308 
309  // Import, find callable, and call.
310  if (!Tf_PyInvokeImpl(
311  moduleName, callableExpr, posArgs, kwArgs, resultOut)) {
312  return false;
313  }
314  }
315  catch (hboost::python::error_already_set const &) {
316  // Handle exceptions.
318  PyErr_Clear();
319  return false;
320  }
321 
322  return true;
323 }
324 
325 /// A version of TfPyInvokeAndExtract that ignores the Python function's return
326 /// value.
327 ///
328 template <typename... Args>
330  const std::string &moduleName,
331  const std::string &callableExpr,
332  Args... args)
333 {
334  // Init Python and grab the GIL.
335  TfPyInitialize();
336  TfPyLock lock;
337 
338  hboost::python::object ignoredResult;
339  return TfPyInvokeAndReturn(
340  moduleName, callableExpr, &ignoredResult, args...);
341 }
342 
344 
345 #endif // PXR_BASE_TF_PY_INVOKE_H
#define TF_API
Definition: api.h:40
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
GLsizei const GLfloat * value
Definition: glcorearb.h:824
bool TfPyInvokeAndReturn(const std::string &moduleName, const std::string &callableExpr, hboost::python::object *resultOut, Args...args)
Definition: pyInvoke.h:288
#define TF_CODING_ERROR
PXR_NAMESPACE_OPEN_SCOPE TF_API void TfPyInitialize()
std::string name
Definition: pyInvoke.h:99
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1736
bool TfPyInvoke(const std::string &moduleName, const std::string &callableExpr, Args...args)
Definition: pyInvoke.h:329
TF_API void TfPyConvertPythonExceptionToTfErrors()
TF_API void Tf_BuildPyInvokeArgs(hboost::python::list *posArgsOut, hboost::python::dict *kwArgsOut)
TfPyObjWrapper value
Definition: pyInvoke.h:100
GLuint const GLchar * name
Definition: glcorearb.h:786
TF_API bool Tf_PyInvokeImpl(const std::string &moduleName, const std::string &callableExpr, const hboost::python::list &posArgs, const hboost::python::dict &kwArgs, hboost::python::object *resultObjOut)
TF_API void Tf_BuildPyInvokeKwArgs(hboost::python::dict *kwArgsOut)
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
TfPyKwArg(const std::string &nameIn, const T &valueIn)
Definition: pyInvoke.h:89
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
**If you just want to fire and args
Definition: thread.h:609
Definition: core.h:1131
GA_API const UT_StringHolder rest
PXR_NAMESPACE_OPEN_SCOPE hboost::python::object Tf_ArgToPy(const T &value)
Definition: pyInvoke.h:58
bool TfPyInvokeAndExtract(const std::string &moduleName, const std::string &callableExpr, Result *resultOut, Args...args)
Definition: pyInvoke.h:251