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