Loading...
Searching...
No Matches
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
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"
37#include "pxr/base/tf/pyObjWrapper.h"
38
39#include <boost/python/dict.hpp>
40#include <boost/python/extract.hpp>
41#include <boost/python/list.hpp>
42#include <boost/python/object.hpp>
43
44#include <cstddef>
45#include <memory>
46#include <string>
47#include <type_traits>
48
49PXR_NAMESPACE_OPEN_SCOPE
50
52// To-Python arg conversion
53
54#ifndef doxygen
55
56// Convert any type to boost::python::object.
57template <typename T>
58boost::python::object Tf_ArgToPy(const T &value)
59{
60 return boost::python::object(value);
61}
62
63// Convert nullptr to None.
64TF_API boost::python::object Tf_ArgToPy(const std::nullptr_t &value);
65
66#endif // !doxygen
67
69// Keyword arg specification
70
87{
88 template <typename T>
89 TfPyKwArg(const std::string &nameIn, const T &valueIn)
90 : name(nameIn)
91 {
92 // Constructing boost::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
99 std::string name;
100 TfPyObjWrapper value;
101};
102
104// Argument collection by variadic template functions
105
106#ifndef doxygen
107
108// Variadic helper: trivial base case.
109TF_API void Tf_BuildPyInvokeKwArgs(
110 boost::python::dict *kwArgsOut);
111
112// Poisoned variadic template helper that provides an error message when
113// non-keyword args are used after keyword args.
114template <typename Arg, typename... RestArgs>
115void Tf_BuildPyInvokeKwArgs(
116 boost::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(
123 std::is_same<Arg, TfPyKwArg>::value,
124 "Non-keyword args not allowed after keyword args");
125}
126
127// Recursive variadic template helper for keyword args.
128template <typename... RestArgs>
129void Tf_BuildPyInvokeKwArgs(
130 boost::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.
142TF_API void Tf_BuildPyInvokeArgs(
143 boost::python::list *posArgsOut,
144 boost::python::dict *kwArgsOut);
145
146// Recursive general-purpose variadic template helper.
147template <typename Arg, typename... RestArgs>
148void Tf_BuildPyInvokeArgs(
149 boost::python::list *posArgsOut,
150 boost::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.
163template <typename... RestArgs>
164void Tf_BuildPyInvokeArgs(
165 boost::python::list *posArgsOut,
166 boost::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
179// Declarations
180
181#ifndef doxygen
182
183// Helper for TfPyInvokeAndExtract.
184TF_API bool Tf_PyInvokeImpl(
185 const std::string &moduleName,
186 const std::string &callableExpr,
187 const boost::python::list &posArgs,
188 const boost::python::dict &kwArgs,
189 boost::python::object *resultObjOut);
190
191// Forward declaration.
192template <typename... Args>
194 const std::string &moduleName,
195 const std::string &callableExpr,
196 boost::python::object *resultOut,
197 Args... args);
198
199#endif // !doxygen
200
202// Main entry points
203
250template <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.
264 TfPyLock lock;
265
266 boost::python::object resultObj;
268 moduleName, callableExpr, &resultObj, args...)) {
269 return false;
270 }
271
272 // Extract return value.
273 boost::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
287template <typename... Args>
289 const std::string &moduleName,
290 const std::string &callableExpr,
291 boost::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.
301 TfPyLock lock;
302
303 try {
304 // Convert args to Python and store in list+dict form.
305 boost::python::list posArgs;
306 boost::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 (boost::python::error_already_set const &) {
316 // Handle exceptions.
317 TfPyConvertPythonExceptionToTfErrors();
318 PyErr_Clear();
319 return false;
320 }
321
322 return true;
323}
324
328template <typename... Args>
330 const std::string &moduleName,
331 const std::string &callableExpr,
332 Args... args)
333{
334 // Init Python and grab the GIL.
336 TfPyLock lock;
337
338 boost::python::object ignoredResult;
339 return TfPyInvokeAndReturn(
340 moduleName, callableExpr, &ignoredResult, args...);
341}
342
343PXR_NAMESPACE_CLOSE_SCOPE
344
345#endif // PXR_BASE_TF_PY_INVOKE_H
Convenience class for accessing the Python Global Interpreter Lock.
Definition: pyLock.h:122
Boost Python object wrapper.
Definition: pyObjWrapper.h:97
object const & Get() const
Underlying object access.
Definition: pyObjWrapper.h:116
Stripped down version of diagnostic.h that doesn't define std::string.
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:85
Python runtime utilities.
TF_API void TfPyInitialize()
Starts up the python runtime.
bool TfPyInvoke(const std::string &moduleName, const std::string &callableExpr, Args... args)
A version of TfPyInvokeAndExtract that ignores the Python function's return value.
Definition: pyInvoke.h:329
bool TfPyInvokeAndExtract(const std::string &moduleName, const std::string &callableExpr, Result *resultOut, Args... args)
Call a Python function and obtain its return value.
Definition: pyInvoke.h:251
bool TfPyInvokeAndReturn(const std::string &moduleName, const std::string &callableExpr, boost::python::object *resultOut, Args... args)
A version of TfPyInvokeAndExtract that provides the Python function's return value as a boost::python...
Definition: pyInvoke.h:288
Wrapper object for a keyword-argument pair in a call to TfPyInvoke*.
Definition: pyInvoke.h:87