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