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 
65 
66 #include "pxr/pxr.h"
67 #include "pxr/usd/sdf/api.h"
68 
69 #include <boost/python/def_visitor.hpp>
70 #include <boost/python/dict.hpp>
71 #include <boost/python/errors.hpp>
72 #include <boost/python/raw_function.hpp>
73 #include <boost/python/pointee.hpp>
74 #include <boost/python/to_python_converter.hpp>
75 #include <boost/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 boost{
90 namespace python {
91 
92 template <typename T>
93 struct pointee<PXR_NS::SdfHandle<T> > {
94  typedef T type;
95 };
96 }
97 }
98 
99 PXR_NAMESPACE_OPEN_SCOPE
100 
101 class SdfSpec;
102 
103 namespace Sdf_PySpecDetail {
104 
105 namespace bp = boost::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  // boost 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  // boost 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 
168  friend class bp::def_visitor_access;
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>
199 Sdf_PySpecDetail::NewVisitor<typename Sdf_PySpecDetail::NewCtor<T> >
200 SdfMakePySpecConstructor(T *func, const std::string &doc = std::string())
201 {
202  // Instantiate to set static constructor pointer, then return the visitor.
203  Sdf_PySpecDetail::NewCtor<T> Ctor(func);
204  return Sdf_PySpecDetail::NewVisitor<Sdf_PySpecDetail::NewCtor<T> >(doc);
205 }
206 
207 namespace Sdf_PySpecDetail {
208 
209 // Create the repr for a spec using Sdf.Find().
210 SDF_API std::string _SpecRepr(const bp::object&, const SdfSpec*);
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>
219 struct _ConstHandleToPython {
220  typedef _SpecType SpecType;
221  typedef SdfHandle<SpecType> Handle;
222  typedef SdfHandle<const SpecType> ConstHandle;
223  _ConstHandleToPython() {
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>
233 struct _HandleToPython {
234 public:
235  typedef _SpecType SpecType;
236  typedef _Holder Holder;
237  typedef _Held Handle;
238  typedef _HandleToPython<SpecType, Handle, Holder> This;
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 boost 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>
297 struct _HandleFromPython {
298  typedef _SpecType SpecType;
299  typedef SdfHandle<SpecType> Handle;
300 
301  _HandleFromPython()
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 NonZero(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(TfPyBoolBuiltinFuncName, &_Helper<CLS>::NonZero);
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.
424  _ConstHandleToPython<SpecType>();
425 
426  // Add python conversion for SdfHandle<SpecType>.
427  _HandleFromPython<SpecType>();
428  _HandleFromPython<const SpecType>();
429  _HandleToPython<SpecType, HeldArgType, HolderType>::Register();
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
444 Sdf_PySpecDetail::SpecVisitor<false>
445 SdfPySpec()
446 {
447  return Sdf_PySpecDetail::SpecVisitor<false>();
448 }
449 
450 inline
451 Sdf_PySpecDetail::SpecVisitor<true>
452 SdfPyAbstractSpec()
453 {
454  return Sdf_PySpecDetail::SpecVisitor<true>();
455 }
456 
457 inline
458 Sdf_PySpecDetail::SpecVisitor<false>
459 SdfPySpecNoRepr()
460 {
461  return Sdf_PySpecDetail::SpecVisitor<false>(false);
462 }
463 
464 inline
465 Sdf_PySpecDetail::SpecVisitor<true>
466 SdfPyAbstractSpecNoRepr()
467 {
468  return Sdf_PySpecDetail::SpecVisitor<true>(false);
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...));
488  if (TfPyConvertTfErrorsToPythonException(m))
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 
505 PXR_NAMESPACE_CLOSE_SCOPE
506 
507 #endif // PXR_USD_SDF_PY_SPEC_H
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:85
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
Return demangled RTTI-generated type name.
Base class for all Sdf spec classes.
Definition: spec.h:51
Definitions of basic string utilities in tf.
Low-level utilities for informing users of various internal and external diagnostic conditions...
boost::python::object TfPyObject(T const &t, bool complainOnFailure=true)
Return a python object for the given C++ object, loading the appropriate wrapper code if necessary...
Definition: pyUtils.h:102
Miscellaneous Utilities for dealing with script.
Demangle C++ typenames generated by the typeid() facility.
SdfHandle is a smart ptr that calls IsDormant() on the pointed-to object as an extra expiration check...
Class used to record the end of the error-list.
Definition: errorMark.h:66
TF_API bool TfPyIsNone(boost::python::object const &obj)
Return true iff obj is None.
TF_API void TfPyThrowRuntimeError(std::string const &msg)
Raises a python RuntimError and throws a C++ exception.
A file containing basic constants and definitions.