HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pySpec.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_USD_SDF_PY_SPEC_H
8 #define PXR_USD_SDF_PY_SPEC_H
9 
10 /// \file sdf/pySpec.h
11 ///
12 /// SdfSpec Python wrapping utilities.
13 ///
14 /// An SdfSpec subclass is not the representation of scene data. An SdfSpec
15 /// simply provides an interface to data stored in some internal representation.
16 /// SdfSpec subclasses are value types and their lifetimes don't reflect the
17 /// lifetime of the scene data. However, clients still create scene data using
18 /// the New methods on SdfSpec subclasses.
19 ///
20 /// When wrapping to Python we need to wrap the New methods as the constructors.
21 /// This used to look like this:
22 ///
23 /// \code
24 /// class_<MyClass, MyClassHandle>("MyClass", no_init)
25 /// .def(TfPyRefAndWeakPtr())
26 /// .def(TfMakePyConstructor(&MyClass::New))
27 /// ...
28 /// \endcode
29 ///
30 /// But we can't use TfMakePyConstructor() because an SdfSpec handle is
31 /// not a weak pointer. Furthermore, we don't have the problem of needing
32 /// to store a ref pointer in the Python object. But we do still need
33 /// conversion of spec types to yield the most-derived type in python.
34 ///
35 /// This file introduces a few pxr_boost::python::class_ def visitors to make
36 /// wrapping specs easy. Spec wrapping should now look like:
37 ///
38 /// \code
39 /// class_<MyClass, SdfHandle<MyClass>, bases<SdfSpec>, noncopyable>
40 /// ("MyClass", no_init)
41 /// .def(SdfPySpec()) // or SdfPyAbstractSpec()
42 /// .def(SdfMakePySpecConstructor(&MyClass::New))
43 /// ...
44 /// \endcode
45 ///
46 /// If you need a custom repr you can use SdfPySpecNoRepr() or
47 /// SdfPyAbstractSpecNoRepr() and def("__repr__", ...).
48 
49 #include "pxr/pxr.h"
50 #include "pxr/usd/sdf/api.h"
51 
52 #include "pxr/external/boost/python/def_visitor.hpp"
53 #include "pxr/external/boost/python/dict.hpp"
54 #include "pxr/external/boost/python/errors.hpp"
55 #include "pxr/external/boost/python/raw_function.hpp"
56 #include "pxr/external/boost/python/pointee.hpp"
57 #include "pxr/external/boost/python/to_python_converter.hpp"
58 #include "pxr/external/boost/python/tuple.hpp"
59 
60 #include "pxr/base/tf/pyError.h"
61 #include "pxr/base/tf/pyUtils.h"
62 
64 #include "pxr/base/tf/tf.h"
65 #include "pxr/base/tf/diagnostic.h"
67 #include "pxr/base/arch/demangle.h"
68 
69 #include <string>
70 #include <type_traits>
71 
72 namespace PXR_BOOST_NAMESPACE {
73 namespace python {
74 
75 template <typename T>
76 struct pointee<PXR_NS::SdfHandle<T> > {
77  typedef T type;
78 };
79 
80 }
81 }
82 
84 
85 class SdfSpec;
86 
87 namespace Sdf_PySpecDetail {
88 
89 namespace bp = pxr_boost::python;
90 
91 SDF_API bp::object _DummyInit(bp::tuple const & /* args */, bp::dict const & /* kw */);
92 
93 template <typename CTOR>
94 struct NewVisitor : bp::def_visitor<NewVisitor<CTOR> > {
95 public:
96  NewVisitor(const std::string &doc = std::string()) : _doc(doc) {}
97 
98  template <typename CLS>
99  void visit(CLS& c) const
100  {
101  // If there's already a __new__ method, look through the staticmethod to
102  // get the underlying function, replace __new__ with that, then add the
103  // overload, and recreate the staticmethod. This is required because
104  // boost python needs to have all overloads exported before you say
105  // .staticmethod.
106 
107  // Note that it looks like this should do nothing, but it actually does
108  // something! Here's what it does: looking up __new__ on c doesn't
109  // actually produce the staticmethod object -- it does a "descriptor
110  // __get__" which produces the underlying function. Replacing __new__
111  // with that underlying thing has the effect of unwrapping the
112  // staticmethod, which is exactly what we want.
113  if (PyObject_HasAttrString(c.ptr(), "__new__"))
114  c.attr("__new__") = c.attr("__new__");
115  c.def("__new__", CTOR::template __new__<CLS>, _doc.c_str());
116  c.staticmethod("__new__");
117 
118  c.def("__init__", bp::raw_function(_DummyInit));
119  }
120 
121  template <class CLS, class Options>
122  void visit(CLS& c, char const* name, Options& options) const
123  {
124  // If there's already a __new__ method, look through the staticmethod to
125  // get the underlying function, replace __new__ with that, then add the
126  // overload, and recreate the staticmethod. This is required because
127  // boost python needs to have all overloads exported before you say
128  // .staticmethod.
129 
130  // Note that it looks like this should do nothing, but it actually does
131  // something! Here's what it does: looking up __new__ on c doesn't
132  // actually produce the staticmethod object -- it does a "descriptor
133  // __get__" which produces the underlying function. Replacing __new__
134  // with that underlying thing has the effect of unwrapping the
135  // staticmethod, which is exactly what we want.
136  if (PyObject_HasAttrString(c.ptr(), "__new__"))
137  c.attr("__new__") = c.attr("__new__");
138  c.def("__new__", CTOR::template __new__<CLS>,
139  // Note: we ignore options.doc() in favor of _doc
140  _doc.c_str(),
141  options.keywords(),
142  options.policies()
143  );
144  c.staticmethod("__new__");
145 
146  c.def("__init__", bp::raw_function(_DummyInit));
147  }
148 
149 private:
150  const std::string _doc;
151 
153 };
154 
155 template <typename SIG>
156 struct CtorBase {
157 public:
158  typedef SIG Sig;
159  static Sig *_func;
160 
161  static void SetFunc(Sig *func)
162  {
163  if (! _func) {
164  _func = func;
165  }
166  else {
167  // CODE_COVERAGE_OFF
168  TF_CODING_ERROR("Ctor with signature '%s' is already registered. "
169  "Duplicate will be ignored.",
170  ArchGetDemangled(typeid(Sig)).c_str());
171  // CODE_COVERAGE_ON
172  }
173  }
174 };
175 
176 template <typename SIG> SIG *CtorBase<SIG>::_func = 0;
177 
178 template <typename SIG> struct NewCtor;
179 
180 } // namespace Sdf_PySpecDetail
181 
182 template <typename T>
184 SdfMakePySpecConstructor(T *func, const std::string &doc = std::string())
185 {
186  // Instantiate to set static constructor pointer, then return the visitor.
187  Sdf_PySpecDetail::NewCtor<T> Ctor(func);
189 }
190 
191 namespace Sdf_PySpecDetail {
192 
193 // Create the repr for a spec using Sdf.Find().
194 SDF_API std::string _SpecRepr(const bp::object&, const SdfSpec*);
195 
196 // Registration for spec types to functions to create a holder with the spec
197 // corresponding to the spec type.
198 typedef PyObject* (*_HolderCreator)(const SdfSpec&);
199 SDF_API void _RegisterHolderCreator(const std::type_info&, _HolderCreator);
200 SDF_API PyObject* _CreateHolder(const std::type_info&, const SdfSpec&);
201 
202 template <class _SpecType>
204  typedef _SpecType SpecType;
208  bp::to_python_converter<ConstHandle, _ConstHandleToPython<SpecType> >();
209  }
210  static PyObject *convert(ConstHandle const &p) {
211  return bp::incref(bp::object(TfConst_cast<Handle>(p)).ptr());
212  }
213 };
214 
215 // Register and perform python conversions of SdfHandles to holders.
216 template <class _SpecType, class _Held, class _Holder>
218 public:
219  typedef _SpecType SpecType;
220  typedef _Holder Holder;
221  typedef _Held Handle;
223 
224  static void Register()
225  {
226  _originalConverter = _RegisterConverter<Handle>(&This::_Convert);
227  _RegisterHolderCreator(typeid(SpecType), &This::_Creator);
228  }
229 
230  static PyObject* convert(const Handle& x)
231  {
232  return _CreateHolder(typeid(SpecType), x.GetSpec());
233  }
234 
235 private:
236  static PyObject* _Creator(const SdfSpec& spec)
237  {
238  Handle x(Sdf_CastAccess::CastSpec<SpecType,SdfSpec>(spec));
239  return bp::objects::make_ptr_instance<SpecType, Holder>::execute(x);
240  }
241 
242  template <class T>
243  static
244  bp::converter::to_python_function_t
245  _RegisterConverter(bp::converter::to_python_function_t f)
246  {
247  // Replace the old converter, installed automatically when we
248  // registered the class. WBN if boost python let us do this
249  // without playing games.
250  bp::converter::registration* r =
251  const_cast<bp::converter::registration*>(
252  bp::converter::registry::query(bp::type_id<T>()));
253  if (r) {
254  bp::converter::to_python_function_t old = r->m_to_python;
255  r->m_to_python = f;
256  return old;
257  }
258  else {
259  // CODE_COVERAGE_OFF Can only happen if there's a bug.
260  TF_CODING_ERROR("No python registration for '%s'!",
261  ArchGetDemangled(typeid(Handle)).c_str());
262  return 0;
263  // CODE_COVERAGE_ON
264  }
265  }
266 
267  static PyObject* _Convert(const void* p)
268  {
269  const Handle& x = *static_cast<const Handle*>(p);
270  return _CreateHolder(typeid(SpecType), x.GetSpec());
271  }
272 
273 private:
274  static bp::converter::to_python_function_t _originalConverter;
275 };
276 template <class SpecType, class Held, class Holder>
277 bp::converter::to_python_function_t
278 _HandleToPython<SpecType, Held, Holder>::_originalConverter = 0;
279 
280 template <class _SpecType>
282  typedef _SpecType SpecType;
284 
286  {
287  bp::converter::registry::insert(&convertible, &construct,
288  bp::type_id<Handle>());
289  }
290 
291  private:
292  static void *convertible(PyObject *p)
293  {
294  if (p == Py_None)
295  return p;
296  void *result =
297  bp::converter::get_lvalue_from_python(p,
298  bp::converter::registered<SpecType>::converters);
299  return result;
300  }
301 
302  static void construct(PyObject* source,
303  bp::converter::rvalue_from_python_stage1_data* data)
304  {
305  void* const storage =
306  ((bp::converter::rvalue_from_python_storage<Handle>*)
307  data)->storage.bytes;
308  // Deal with the "None" case.
309  if (data->convertible == source)
310  new (storage) Handle();
311  else {
312  new (storage) Handle(*static_cast<SpecType*>(data->convertible));
313  }
314  data->convertible = storage;
315  }
316 };
317 
318 // Visitor for def().
319 template <bool Abstract>
320 struct SpecVisitor : bp::def_visitor<SpecVisitor<Abstract> > {
321 
322  template<typename CLS>
323  struct _Helper {
324  typedef typename CLS::wrapped_type SpecType;
325  typedef typename CLS::metadata::held_type HeldType;
326  typedef typename CLS::metadata::held_type_arg HeldArgType;
327  typedef typename CLS::metadata::holder HolderType;
328 
329  public:
330  static std::string Repr(const bp::object& self)
331  {
332  const HeldType& held = bp::extract<const HeldType&>(self);
333  return _SpecRepr(self, get_pointer(held));
334  }
335 
336  static bool IsExpired(const HeldType& self)
337  {
338  return !self;
339  }
340 
341  static bool IsValid(const HeldType& self)
342  {
343  return self;
344  }
345 
346  static size_t __hash__(const HeldType& self)
347  {
348  return hash_value(self);
349  }
350 
351  static bool __eq__(const HeldType& a, const HeldType& b)
352  {
353  return a == b;
354  }
355 
356  static bool __ne__(const HeldType& a, const HeldType& b)
357  {
358  return a != b;
359  }
360 
361  static bool __lt__(const HeldType& a, const HeldType& b)
362  {
363  return a < b;
364  }
365 
366  static bool __le__(const HeldType& a, const HeldType& b)
367  {
368  return a <= b;
369  }
370 
371  static bool __gt__(const HeldType& a, const HeldType& b)
372  {
373  return a > b;
374  }
375 
376  static bool __ge__(const HeldType& a, const HeldType& b)
377  {
378  return a >= b;
379  }
380  };
381 
382 public:
383  SpecVisitor(bool addRepr = true) : _addRepr(addRepr) { }
384 
385  template <typename CLS>
386  void visit(CLS& c) const
387  {
388  typedef typename CLS::wrapped_type SpecType;
389  typedef typename CLS::metadata::held_type HeldType;
390  typedef typename CLS::metadata::held_type_arg HeldArgType;
391  typedef typename CLS::metadata::holder HolderType;
392 
393  static_assert(std::is_same<HeldType, SdfHandle<SpecType> >::value,
394  "HeldType must be SdfHandle<SpecType>.");
395 
396  // Add methods.
397  c.add_property("expired", &_Helper<CLS>::IsExpired);
398  c.def("__bool__", &_Helper<CLS>::IsValid);
399  c.def("__hash__", &_Helper<CLS>::__hash__);
400  c.def("__eq__", &_Helper<CLS>::__eq__);
401  c.def("__ne__", &_Helper<CLS>::__ne__);
402  c.def("__lt__", &_Helper<CLS>::__lt__);
403  c.def("__le__", &_Helper<CLS>::__le__);
404  c.def("__gt__", &_Helper<CLS>::__gt__);
405  c.def("__ge__", &_Helper<CLS>::__ge__);
406 
407  // Add python conversion to cast away constness.
409 
410  // Add python conversion for SdfHandle<SpecType>.
414 
415  // Add __repr__.
416  if (_addRepr) {
417  c.def("__repr__", &_Helper<CLS>::Repr);
418  }
419  }
420 
421 private:
422  bool _addRepr;
423 };
424 
425 } // namespace Sdf_PySpecDetail
426 
427 inline
430 {
432 }
433 
434 inline
437 {
439 }
440 
441 inline
444 {
446 }
447 
448 inline
451 {
453 }
454 
455 
456 namespace Sdf_PySpecDetail
457 {
458 
459 // This generates multi-argument specializations for NewCtor.
460 
461 template <typename R, typename... Args>
462 struct NewCtor<R(Args...)> : CtorBase<R(Args...)> {
463  typedef CtorBase<R(Args...)> Base;
464  typedef typename Base::Sig Sig;
465  NewCtor(Sig *func) { Base::SetFunc(func); }
466 
467  template <class CLS>
468  static bp::object __new__(bp::object &cls, Args... args) {
469  typedef typename CLS::metadata::held_type HeldType;
470  TfErrorMark m;
471  HeldType specHandle(Base::_func(args...));
473  bp::throw_error_already_set();
474  bp::object result = TfPyObject(specHandle);
475  if (TfPyIsNone(result))
476  TfPyThrowRuntimeError("could not construct " +
477  ArchGetDemangled(typeid(HeldType)));
478 
479  bp::detail::initialize_wrapper(result.ptr(), get_pointer(specHandle));
480  // make the object have the right class.
481  bp::setattr(result, "__class__", cls);
482 
483  return result;
484  }
485 };
486 
487 } // namespace Sdf_PySpecDetail
488 
490 
491 #endif // PXR_USD_SDF_PY_SPEC_H
GLenum query
Definition: glad.h:2772
SdfHandle< SpecType > Handle
Definition: pySpec.h:205
void visit(CLS &c) const
Definition: pySpec.h:386
Sdf_PySpecDetail::SpecVisitor< false > SdfPySpec()
Definition: pySpec.h:429
static std::string Repr(const bp::object &self)
Definition: pySpec.h:330
pxr_boost::python::object TfPyObject(T const &t, bool complainOnFailure=true)
Definition: pyUtils.h:127
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
static bool __gt__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:371
GLsizei const GLfloat * value
Definition: glcorearb.h:824
SDF_API bp::object _DummyInit(bp::tuple const &, bp::dict const &)
#define TF_CODING_ERROR
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
Definition: spec.h:32
static bool __ne__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:356
GLboolean GLboolean GLboolean GLboolean a
Definition: glcorearb.h:1222
static Sig * _func
Definition: pySpec.h:159
SdfHandle< const SpecType > ConstHandle
Definition: pySpec.h:206
**But if you need a result
Definition: thread.h:622
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3556
PXR_NAMESPACE_OPEN_SCOPE TF_API bool TfPyConvertTfErrorsToPythonException(TfErrorMark const &m)
PyObject *(* _HolderCreator)(const SdfSpec &)
Definition: pySpec.h:198
static bool IsValid(const HeldType &self)
Definition: pySpec.h:341
Y * get_pointer(TfWeakPtrFacade< X, Y > const &p)
Definition: weakPtrFacade.h:63
static bool IsExpired(const HeldType &self)
Definition: pySpec.h:336
SDF_API std::string _SpecRepr(const bp::object &, const SdfSpec *)
static bool __le__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:366
CLS::metadata::holder HolderType
Definition: pySpec.h:327
CLS::metadata::held_type_arg HeldArgType
Definition: pySpec.h:326
#define PXR_NS
Definition: pxr.h:54
Sdf_PySpecDetail::SpecVisitor< true > SdfPyAbstractSpecNoRepr()
Definition: pySpec.h:450
GLfloat f
Definition: glcorearb.h:1926
CLS::metadata::held_type HeldType
Definition: pySpec.h:325
TF_API bool TfPyIsNone(pxr_boost::python::object const &obj)
Return true iff obj is None.
Sdf_PySpecDetail::NewVisitor< typename Sdf_PySpecDetail::NewCtor< T > > SdfMakePySpecConstructor(T *func, const std::string &doc=std::string())
Definition: pySpec.h:184
SDF_API PyObject * _CreateHolder(const std::type_info &, const SdfSpec &)
GLsizei GLsizei GLchar * source
Definition: glcorearb.h:803
friend class bp::def_visitor_access
Definition: pySpec.h:152
SDF_API void _RegisterHolderCreator(const std::type_info &, _HolderCreator)
void visit(CLS &c, char const *name, Options &options) const
Definition: pySpec.h:122
GLuint const GLchar * name
Definition: glcorearb.h:786
static bool __eq__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:351
Sdf_PySpecDetail::SpecVisitor< true > SdfPyAbstractSpec()
Definition: pySpec.h:436
GLboolean GLboolean GLboolean b
Definition: glcorearb.h:1222
GLint GLenum GLint x
Definition: glcorearb.h:409
static size_t __hash__(const HeldType &self)
Definition: pySpec.h:346
static bool __ge__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:376
#define SDF_API
Definition: api.h:23
GLenum func
Definition: glcorearb.h:783
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
CtorBase< R(Args...)> Base
Definition: pySpec.h:463
static bool __lt__(const HeldType &a, const HeldType &b)
Definition: pySpec.h:361
static PyObject * convert(const Handle &x)
Definition: pySpec.h:230
void visit(CLS &c) const
Definition: pySpec.h:99
static bp::object __new__(bp::object &cls, Args...args)
Definition: pySpec.h:468
_HandleToPython< SpecType, Handle, Holder > This
Definition: pySpec.h:222
static PyObject * convert(ConstHandle const &p)
Definition: pySpec.h:210
auto ptr(T p) -> const void *
Definition: format.h:4331
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
**If you just want to fire and args
Definition: thread.h:618
OIIO_UTIL_API const char * c_str(string_view str)
GLboolean r
Definition: glcorearb.h:1222
NewVisitor(const std::string &doc=std::string())
Definition: pySpec.h:96
Sdf_PySpecDetail::SpecVisitor< false > SdfPySpecNoRepr()
Definition: pySpec.h:443
SdfHandle< SpecType > Handle
Definition: pySpec.h:283
size_t hash_value(const CH_ChannelRef &ref)
static void SetFunc(Sig *func)
Definition: pySpec.h:161
TF_API void TfPyThrowRuntimeError(const char *msg)
Definition: format.h:1821
SpecVisitor(bool addRepr=true)
Definition: pySpec.h:383