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