HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyUtils.h
Go to the documentation of this file.
1 //
2 // Copyright 2016 Pixar
3 //
4 // Licensed under the terms set forth in the LICENSE.txt file available at
5 // https://openusd.org/license.
6 //
7 #ifndef PXR_BASE_TF_PY_UTILS_H
8 #define PXR_BASE_TF_PY_UTILS_H
9 
10 /// \file tf/pyUtils.h
11 /// Miscellaneous Utilities for dealing with script.
12 
13 #include "pxr/pxr.h"
14 
15 #include "pxr/base/tf/refPtr.h"
16 #include "pxr/base/tf/weakPtr.h"
21 #include "pxr/base/tf/pyLock.h"
22 #include "pxr/base/tf/api.h"
23 
24 #include <functional>
25 #include <typeinfo>
26 #include <string>
27 #include <vector>
28 
29 #include "pxr/external/boost/python/dict.hpp"
30 #include "pxr/external/boost/python/extract.hpp"
31 #include "pxr/external/boost/python/handle.hpp"
32 #include "pxr/external/boost/python/object.hpp"
33 #include "pxr/external/boost/python/type_id.hpp"
34 
36 
37 /// A macro which expands to the proper __repr__ prefix for a library. This is
38 /// the "canonical" name of the module that the system uses to identify it
39 /// followed by a '.'. This can be used in the implementation of __repr__
40 ///
41 /// \hideinitializer
42 #define TF_PY_REPR_PREFIX \
43  std::string(TF_PP_STRINGIZE(MFB_PACKAGE_MODULE) ".")
44 
45 /// Returns true if python is initialized.
47 
48 /// Raises a Python \c IndexError with the given error \p msg and throws a
49 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
50 /// before calling this function.
51 TF_API void TfPyThrowIndexError(const char *msg);
52 
53 /// \overload
54 inline void TfPyThrowIndexError(std::string const &msg)
55 {
56  TfPyThrowIndexError(msg.c_str());
57 }
58 
59 /// Raises a Python \c RuntimeError with the given error \p msg and throws a
60 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
61 /// before calling this function.
62 TF_API void TfPyThrowRuntimeError(const char *msg);
63 
64 /// \overload
65 inline void TfPyThrowRuntimeError(std::string const &msg)
66 {
67  TfPyThrowRuntimeError(msg.c_str());
68 }
69 
70 /// Raises a Python \c StopIteration with the given error \p msg and throws a
71 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
72 /// before calling this function.
73 TF_API void TfPyThrowStopIteration(const char *msg);
74 
75 /// \overload
76 inline void TfPyThrowStopIteration(std::string const &msg)
77 {
78  TfPyThrowStopIteration(msg.c_str());
79 }
80 
81 /// Raises a Python \c KeyError with the given error \p msg and throws a
82 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
83 /// before calling this function.
84 TF_API void TfPyThrowKeyError(const char *msg);
85 
86 /// \overload
87 inline void TfPyThrowKeyError(std::string const &msg)
88 {
89  TfPyThrowKeyError(msg.c_str());
90 }
91 
92 /// Raises a Python \c ValueError with the given error \p msg and throws a
93 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
94 /// before calling this function.
95 TF_API void TfPyThrowValueError(const char *msg);
96 
97 /// \overload
98 inline void TfPyThrowValueError(std::string const &msg)
99 {
100  TfPyThrowValueError(msg.c_str());
101 }
102 
103 /// Raises a Python \c TypeError with the given error \p msg and throws a
104 /// pxr_boost::python::error_already_set exception. Callers must hold the GIL
105 /// before calling this function.
106 TF_API void TfPyThrowTypeError(const char *msg);
107 
108 /// \overload
109 inline void TfPyThrowTypeError(std::string const &msg)
110 {
111  TfPyThrowTypeError(msg.c_str());
112 }
113 
114 /// Return true iff \a obj is None.
116 
117 /// Return true iff \a obj is None.
118 TF_API bool TfPyIsNone(pxr_boost::python::handle<> const &obj);
119 
120 // Helper for \c TfPyObject().
121 TF_API void Tf_PyObjectError(bool printError);
122 
123 /// Return a python object for the given C++ object, loading the appropriate
124 /// wrapper code if necessary. Spams users if complainOnFailure is true and
125 /// conversion fails.
126 template <typename T>
127 pxr_boost::python::object TfPyObject(T const &t, bool complainOnFailure = true) {
128  // initialize python if it isn't already, so at least we can try to return
129  // an object
130  if (!TfPyIsInitialized()) {
131  TF_CODING_ERROR("Called TfPyObject without python being initialized!");
132  TfPyInitialize();
133  }
134 
135  TfPyLock pyLock;
136 
137  // Will only be able to return objects which have been wrapped.
138  // Returns None otherwise
139  try {
140  return pxr_boost::python::object(t);
141  } catch (pxr_boost::python::error_already_set const &) {
142  Tf_PyObjectError(complainOnFailure);
143  return pxr_boost::python::object();
144  }
145 }
146 
147 inline
148 pxr_boost::python::object TfPyObject(PyObject* t, bool complainOnFailure = true) {
149  TfPyLock pyLock;
150  return pxr_boost::python::object(pxr_boost::python::handle<>(t));
151 }
152 
153 /// Return repr(t).
154 ///
155 /// Calls PyObject_Repr on the given python object.
157 
158 /// Return repr(t).
159 ///
160 /// Converts t to its equivalent python object and then calls PyObject_Repr on
161 /// that.
162 template <typename T>
163 std::string TfPyRepr(T const &t) {
164  if (!TfPyIsInitialized())
165  return "<python not initialized>";
166  TfPyLock lock;
167  return TfPyObjectRepr(TfPyObject(t));
168 }
169 
170 /// Return repr(t) for a vector as a python list.
171 template <typename T>
172 std::string TfPyRepr(const std::vector<T> &v) {
173  std::string result("[");
174  typename std::vector<T>::const_iterator i = v.begin();
175  if (i != v.end()) {
176  result += TfPyRepr(*i);
177  ++i;
178  }
179  while (i != v.end()) {
180  result += ", " + TfPyRepr(*i);
181  ++i;
182  }
183  result += "]";
184  return result;
185 }
186 
187 /// Evaluate python expression \a expr with all the known script modules
188 /// imported under their standard names. Additional globals may be provided in
189 /// the \p extraGlobals dictionary.
190 TF_API
193  std::string const &expr,
194  pxr_boost::python::dict const &extraGlobals = pxr_boost::python::dict());
195 
196 /// Return a positive index in the range [0,size). If \a throwError is true,
197 /// this will throw an index error if the resulting index is out of range.
198 TF_API
199 int64_t
200 TfPyNormalizeIndex(int64_t index, uint64_t size, bool throwError = false);
201 
202 /// Return the name of the class of \a obj.
203 TF_API std::string TfPyGetClassName(pxr_boost::python::object const &obj);
204 
205 
206 /// Return the python class object for \a type if \a type has been wrapped.
207 /// Otherwise return None.
209 TfPyGetClassObject(std::type_info const &type);
210 
211 /// Return the python class object for T if T has been wrapped.
212 /// Otherwise return None.
213 template <typename T>
216  return TfPyGetClassObject(typeid(T));
217 }
218 
219 TF_API
220 void
221 Tf_PyWrapOnceImpl(pxr_boost::python::type_info const &,
222  std::function<void()> const&,
223  bool *);
224 
225 /// Invokes \p wrapFunc to wrap type \c T if \c T is not already wrapped.
226 ///
227 /// Executing \p wrapFunc *must* register \c T with boost python. Otherwise,
228 /// \p wrapFunc may be executed more than once.
229 ///
230 /// TfPyWrapOnce will acquire the GIL prior to invoking \p wrapFunc. Does not
231 /// invoke \p wrapFunc if Python has not been initialized.
232 template <typename T>
233 void
234 TfPyWrapOnce(std::function<void()> const &wrapFunc)
235 {
236  // Don't try to wrap if python isn't initialized.
237  if (!TfPyIsInitialized()) {
238  return;
239  }
240 
241  static bool isTypeWrapped = false;
242  if (isTypeWrapped) {
243  return;
244  }
245 
246  Tf_PyWrapOnceImpl(pxr_boost::python::type_id<T>(), wrapFunc, &isTypeWrapped);
247 }
248 
249 /// Load the python module \a moduleName. This is used by some low-level
250 /// infrastructure code to load python wrapper modules corresponding to C++
251 /// shared libraries when they are needed. It should generally not need to be
252 /// called from normal user code.
253 TF_API
254 void Tf_PyLoadScriptModule(std::string const &name);
255 
256 /// Creates a python dictionary from a std::map.
257 template <class Map>
258 pxr_boost::python::dict TfPyCopyMapToDictionary(Map const &map) {
259  TfPyLock lock;
260  pxr_boost::python::dict d;
261  for (typename Map::const_iterator i = map.begin(); i != map.end(); ++i)
262  d[i->first] = i->second;
263  return d;
264 }
265 
266 template<class Seq>
267 pxr_boost::python::list TfPyCopySequenceToList(Seq const &seq) {
268  TfPyLock lock;
269  pxr_boost::python::list l;
270  for (typename Seq::const_iterator i = seq.begin();
271  i != seq.end(); ++i)
272  l.append(*i);
273  return l;
274 }
275 
276 /// Create a python set from an iterable sequence.
277 ///
278 /// If Seq::value_type is not hashable, TypeError is raised via throwing
279 /// pxr_boost::python::error_already_set.
280 template <class Seq>
282  TfPyLock lock;
283  pxr_boost::python::handle<> set{pxr_boost::python::allow_null(PySet_New(nullptr))};
284  if (!set) {
285  pxr_boost::python::throw_error_already_set();
286  }
287  for (auto const& item : seq) {
288  pxr_boost::python::object obj(item);
289  if (PySet_Add(set.get(), obj.ptr()) == -1) {
290  pxr_boost::python::throw_error_already_set();
291  }
292  }
294 }
295 
296 template<class Seq>
297 pxr_boost::python::tuple TfPyCopySequenceToTuple(Seq const &seq) {
298  return pxr_boost::python::tuple(TfPyCopySequenceToList(seq));
299 }
300 
301 /// Create a python bytearray from an input buffer and size.
302 ///
303 /// If a size of zero is passed in this function will return a valid python
304 /// bytearray of size zero.
305 ///
306 /// An invalid object handle is returned on failure.
307 TF_API
309 
310 /// Return a vector of strings containing the current python traceback.
311 ///
312 /// The vector contains the same strings that python's traceback.format_stack()
313 /// returns.
314 TF_API
315 std::vector<std::string> TfPyGetTraceback();
316 
317 /// Set an environment variable in \c os.environ.
318 ///
319 /// This function is equivalent to
320 ///
321 /// \code
322 /// def PySetenv(name, value):
323 /// try:
324 /// import os
325 /// os.environ[name] = value
326 /// return True
327 /// except:
328 /// return False
329 /// \endcode
330 ///
331 /// Calling this function without first initializing Python is an error and
332 /// returns \c false.
333 ///
334 /// Note that this function will import the \c os module, causing \c
335 /// os.environ to be poputated. All modifications to the environment after \c
336 /// os has been imported must be made with this function or \c TfSetenv if it
337 /// important that they appear in \c os.environ.
338 TF_API
339 bool TfPySetenv(const std::string & name, const std::string & value);
340 
341 /// Remove an environment variable from \c os.environ.
342 ///
343 /// This function is equivalent to
344 ///
345 /// \code
346 /// def PyUnsetenv(name):
347 /// try:
348 /// import os
349 /// if name in os.environ:
350 /// del os.environ[name]
351 /// return True
352 /// except:
353 /// return False
354 /// \endcode
355 ///
356 /// Calling this function without first initializing Python is an error and
357 /// returns \c false.
358 ///
359 /// Note that this function will import the \c os module, causing \c
360 /// os.environ to be poputated. All modifications to the environment after \c
361 /// os has been imported must be made with this function or \c TfUnsetenv if
362 /// it important that they appear in \c os.environ.
363 TF_API
364 bool TfPyUnsetenv(const std::string & name);
365 
366 // Private helper method to TfPyEvaluateAndExtract.
367 //
369  const std::string & expr, pxr_boost::python::object * obj);
370 
371 /// Safely evaluates \p expr and extracts the return object of type T. If
372 /// successful, returns \c true and sets *t to the return value, otherwise
373 /// returns \c false.
374 template <typename T>
375 bool TfPyEvaluateAndExtract(const std::string & expr, T * t)
376 {
377  if (expr.empty())
378  return false;
379 
380  // Take the lock before doing anything with pxr_boost::python.
381  TfPyLock lock;
382 
383  // Though TfPyEvaluate (called by Tf_PyEvaluateWithErroCheck) takes the
384  // python lock, it is important that we lock before we initialize the
385  // pxr_boost::python::object, since it will increment and decrement ref counts
386  // outside of the call to TfPyEvaluate.
388  if (!Tf_PyEvaluateWithErrorCheck(expr, &obj))
389  return false;
390 
391  pxr_boost::python::extract<T> extractor(obj);
392 
393  if (!extractor.check())
394  return false;
395 
396  *t = extractor();
397 
398  return true;
399 }
400 
401 /// Print a standard traceback to sys.stderr and clear the error indicator.
402 /// If the error is a KeyboardInterrupt then this does nothing. Call this
403 /// function only when the error indicator is set.
404 TF_API
405 void TfPyPrintError();
406 
408 
409 #endif // PXR_BASE_TF_PY_UTILS_H
TF_API bool TfPyIsInitialized()
Returns true if python is initialized.
TF_API void Tf_PyObjectError(bool printError)
TF_API void Tf_PyLoadScriptModule(std::string const &name)
#define TF_API
Definition: api.h:23
pxr_boost::python::object TfPyObject(T const &t, bool complainOnFailure=true)
Definition: pyUtils.h:127
TF_API std::string TfPyObjectRepr(pxr_boost::python::object const &t)
const GLdouble * v
Definition: glcorearb.h:837
pxr_boost::python::list TfPyCopySequenceToList(Seq const &seq)
Definition: pyUtils.h:267
GLsizei const GLfloat * value
Definition: glcorearb.h:824
#define TF_CODING_ERROR
TF_API bool TfPyUnsetenv(const std::string &name)
TF_API void TfPyPrintError()
TF_API pxr_boost::python::object TfPyEvaluate(std::string const &expr, pxr_boost::python::dict const &extraGlobals=pxr_boost::python::dict())
PXR_NAMESPACE_OPEN_SCOPE TF_API void TfPyInitialize()
TF_API void TfPyThrowStopIteration(const char *msg)
TF_API void TfPyThrowValueError(const char *msg)
**But if you need a result
Definition: thread.h:622
GLuint buffer
Definition: glcorearb.h:660
std::string TfPyRepr(T const &t)
Definition: pyUtils.h:163
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
TF_API void Tf_PyWrapOnceImpl(pxr_boost::python::type_info const &, std::function< void()> const &, bool *)
TF_API bool TfPyIsNone(pxr_boost::python::object const &obj)
Return true iff obj is None.
TF_API std::string TfPyGetClassName(pxr_boost::python::object const &obj)
Return the name of the class of obj.
constexpr auto set(type rhs) -> int
Definition: core.h:610
TF_API void TfPyThrowTypeError(const char *msg)
void TfPyWrapOnce(std::function< void()> const &wrapFunc)
Definition: pyUtils.h:234
GLuint const GLchar * name
Definition: glcorearb.h:786
TF_API pxr_boost::python::object TfPyCopyBufferToByteArray(const char *buffer, size_t size)
GLdouble t
Definition: glad.h:2397
GLsizeiptr size
Definition: glcorearb.h:664
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
bool TfPyEvaluateAndExtract(const std::string &expr, T *t)
Definition: pyUtils.h:375
GLuint index
Definition: glcorearb.h:786
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
TF_API void TfPyThrowKeyError(const char *msg)
pxr_boost::python::tuple TfPyCopySequenceToTuple(Seq const &seq)
Definition: pyUtils.h:297
TF_API void TfPyThrowIndexError(const char *msg)
TF_API pxr_boost::python::object TfPyGetClassObject(std::type_info const &type)
pxr_boost::python::object TfPyCopySequenceToSet(Seq const &seq)
Definition: pyUtils.h:281
TF_API int64_t TfPyNormalizeIndex(int64_t index, uint64_t size, bool throwError=false)
TF_API bool Tf_PyEvaluateWithErrorCheck(const std::string &expr, pxr_boost::python::object *obj)
TF_API bool TfPySetenv(const std::string &name, const std::string &value)
TF_API void TfPyThrowRuntimeError(const char *msg)
TF_API std::vector< std::string > TfPyGetTraceback()
pxr_boost::python::dict TfPyCopyMapToDictionary(Map const &map)
Creates a python dictionary from a std::map.
Definition: pyUtils.h:258