HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyEnum.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_BASE_TF_PY_ENUM_H
8 #define PXR_BASE_TF_PY_ENUM_H
9 
10 /// \file tf/pyEnum.h
11 /// Provide facilities for wrapping enums for script.
12 
13 #include "pxr/pxr.h"
14 
15 #include "pxr/base/tf/api.h"
17 #include "pxr/base/tf/pyUtils.h"
18 #include "pxr/base/tf/type.h"
19 
20 #include "pxr/base/arch/demangle.h"
21 #include "pxr/base/tf/enum.h"
22 #include "pxr/base/tf/hash.h"
23 #include "pxr/base/tf/hashmap.h"
24 #include "pxr/base/tf/iterator.h"
25 #include "pxr/base/tf/singleton.h"
27 
28 #include "pxr/external/boost/python/class.hpp"
29 #include "pxr/external/boost/python/converter/from_python.hpp"
30 #include "pxr/external/boost/python/converter/registered.hpp"
31 #include "pxr/external/boost/python/converter/rvalue_from_python_data.hpp"
32 #include "pxr/external/boost/python/list.hpp"
33 #include "pxr/external/boost/python/object.hpp"
34 #include "pxr/external/boost/python/operators.hpp"
35 #include "pxr/external/boost/python/refcount.hpp"
36 #include "pxr/external/boost/python/scope.hpp"
37 #include "pxr/external/boost/python/to_python_converter.hpp"
38 #include "pxr/external/boost/python/tuple.hpp"
39 
40 #include <string>
41 
43 
44 /// \class Tf_PyEnum
45 ///
46 /// Base class of all python enum classes.
47 class Tf_PyEnum { };
48 
49 /// \class Tf_PyEnumRegistry
50 ///
51 /// This is a private class that manages registered enum objects.
52 /// \private
54 
55  public:
57 
58  private:
60  virtual ~Tf_PyEnumRegistry();
61  friend class TfSingleton<This>;
62 
63  public:
64 
65  TF_API static This &GetInstance() {
67  }
68 
69  TF_API
70  void RegisterValue(TfEnum const &e, pxr_boost::python::object const &obj);
71 
72  template <typename T>
74  // Register conversions to and from python.
75  pxr_boost::python::to_python_converter<T, _EnumToPython<T> >();
76  _EnumFromPython<T>();
77  }
78 
79  private:
80 
81  TF_API
82  PyObject *_ConvertEnumToPython(TfEnum const &e);
83 
84  template <typename T>
85  struct _EnumFromPython {
86  _EnumFromPython() {
88  (&convertible, &construct, pxr_boost::python::type_id<T>());
89  }
90  static void *convertible(PyObject *obj) {
92  Tf_PyEnumRegistry::GetInstance()._objectsToEnums;
94  i = o2e.find(obj);
95  // In the case of producing a TfEnum or an integer, any
96  // registered enum type is fine. In all other cases, the
97  // enum types must match.
100  return i != o2e.end() ? obj : 0;
101  else
102  return (i != o2e.end() && i->second.IsA<T>()) ? obj : 0;
103  }
104  static void construct(PyObject *src, pxr_boost::python::converter::
105  rvalue_from_python_stage1_data *data) {
106  void *storage =
107  ((pxr_boost::python::converter::
108  rvalue_from_python_storage<T> *)data)->storage.bytes;
109  new (storage) T(_GetEnumValue(src, (T *)0));
110  data->convertible = storage;
111  }
112  private:
113  // Overloads to explicitly allow conversion of the TfEnum integer
114  // value to other enum/integral types.
115  template <typename U>
116  static U _GetEnumValue(PyObject *src, U *) {
117  return U(Tf_PyEnumRegistry::GetInstance()._objectsToEnums[src].
118  GetValueAsInt());
119  }
120  static TfEnum _GetEnumValue(PyObject *src, TfEnum *) {
121  return Tf_PyEnumRegistry::GetInstance()._objectsToEnums[src];
122  }
123  };
124 
125  template <class T>
126  struct _EnumToPython {
127  static PyObject *convert(T t) {
128  return Tf_PyEnumRegistry
129  ::GetInstance()._ConvertEnumToPython(TfEnum(t));
130  }
131  };
132 
133  // Since our enum objects live as long as the registry does, we can use the
134  // pointer values for a hash.
135  struct _ObjectHash {
136  size_t operator()(PyObject *o) const {
137  return reinterpret_cast<size_t>(o);
138  }
139  };
140 
143 
144 };
145 
147 
148 // Private function used for __repr__ of wrapped enum types.
149 TF_API
150 std::string Tf_PyEnumRepr(pxr_boost::python::object const &self);
151 
152 // Private base class for types which are instantiated and exposed to python
153 // for each registered enum type.
155 {
157 
158  Tf_PyEnumWrapper(std::string const &n, TfEnum const &val) :
159  name(n), value(val) {}
160  long GetValue() const {
161  return value.GetValueAsInt();
162  }
163  std::string GetName() const{
164  return name;
165  }
166  std::string GetDisplayName() const {
168  }
169  std::string GetFullName() const {
170  return TfEnum::GetFullName(value);
171  }
172  friend bool operator ==(Tf_PyEnumWrapper const &self,
173  long other) {
174  return self.value.GetValueAsInt() == other;
175  }
176 
177  friend bool operator ==(Tf_PyEnumWrapper const &lhs,
178  Tf_PyEnumWrapper const &rhs) {
179  return lhs.value == rhs.value;
180  }
181 
182  friend bool operator !=(Tf_PyEnumWrapper const &lhs,
183  Tf_PyEnumWrapper const &rhs) {
184  return !(lhs == rhs);
185  }
186 
187  friend bool operator <(Tf_PyEnumWrapper const &lhs,
188  Tf_PyEnumWrapper const &rhs)
189  {
190  // If same, not less.
191  if (lhs == rhs)
192  return false;
193  // If types don't match, string compare names.
194  if (!lhs.value.IsA(rhs.value.GetType()))
195  return TfEnum::GetFullName(lhs.value) <
197  // If types do match, numerically compare values.
198  return lhs.GetValue() < rhs.GetValue();
199  }
200 
201  friend bool operator >(Tf_PyEnumWrapper const& lhs,
202  Tf_PyEnumWrapper const& rhs)
203  {
204  return rhs < lhs;
205  }
206 
207  friend bool operator <=(Tf_PyEnumWrapper const& lhs,
208  Tf_PyEnumWrapper const& rhs)
209  {
210  return !(lhs > rhs);
211  }
212 
213  friend bool operator >=(Tf_PyEnumWrapper const& lhs,
214  Tf_PyEnumWrapper const& rhs)
215  {
216  return !(lhs < rhs);
217  }
218 
219  //
220  // XXX Bitwise operators for Enums are a temporary measure to support the
221  // use of Enums as Bitmasks in libSd. It should be noted that Enums are
222  // NOT closed under these operators. The proper place for such operators
223  // is in a yet-nonexistent Bitmask type.
224  //
225 
226  friend TfEnum operator |(Tf_PyEnumWrapper const &lhs,
227  Tf_PyEnumWrapper const &rhs) {
228  if (lhs.value.IsA(rhs.value.GetType())) {
229  return TfEnum(lhs.value.GetType(),
230  lhs.value.GetValueAsInt() |
231  rhs.value.GetValueAsInt());
232  }
233  TfPyThrowTypeError("Enum type mismatch");
234  return TfEnum();
235  }
236  friend TfEnum operator |(Tf_PyEnumWrapper const &lhs, long rhs) {
237  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() | rhs);
238  }
239  friend TfEnum operator |(long lhs, Tf_PyEnumWrapper const &rhs) {
240  return TfEnum(rhs.value.GetType(), lhs | rhs.value.GetValueAsInt());
241  }
242 
243  friend TfEnum operator &(Tf_PyEnumWrapper const &lhs,
244  Tf_PyEnumWrapper const &rhs) {
245  if (lhs.value.IsA(rhs.value.GetType())) {
246  return TfEnum(lhs.value.GetType(),
247  lhs.value.GetValueAsInt() &
248  rhs.value.GetValueAsInt());
249  }
250  TfPyThrowTypeError("Enum type mismatch");
251  return TfEnum();
252  }
253  friend TfEnum operator &(Tf_PyEnumWrapper const &lhs, long rhs) {
254  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() & rhs);
255  }
256  friend TfEnum operator &(long lhs, Tf_PyEnumWrapper const &rhs) {
257  return TfEnum(rhs.value.GetType(), lhs & rhs.value.GetValueAsInt());
258  }
259 
260  friend TfEnum operator ^(Tf_PyEnumWrapper const &lhs,
261  Tf_PyEnumWrapper const &rhs) {
262  if (lhs.value.IsA(rhs.value.GetType())) {
263  return TfEnum(lhs.value.GetType(),
264  lhs.value.GetValueAsInt() ^
265  rhs.value.GetValueAsInt());
266  }
267  TfPyThrowTypeError("Enum type mismatch");
268  return TfEnum();
269  }
270  friend TfEnum operator ^(Tf_PyEnumWrapper const &lhs, long rhs) {
271  return TfEnum(lhs.value.GetType(), lhs.value.GetValueAsInt() ^ rhs);
272  }
273  friend TfEnum operator ^(long lhs, Tf_PyEnumWrapper const &rhs) {
274  return TfEnum(rhs.value.GetType(), lhs ^ rhs.value.GetValueAsInt());
275  }
276 
277  friend TfEnum operator ~(Tf_PyEnumWrapper const &rhs) {
278  return TfEnum(rhs.value.GetType(), ~rhs.value.GetValueAsInt());
279  }
280  std::string name;
282 };
283 
284 // Private template class which is instantiated and exposed to python for each
285 // registered enum type.
286 template <typename T>
288 {
289  Tf_TypedPyEnumWrapper(std::string const &n, TfEnum const &val) :
290  Tf_PyEnumWrapper(n, val) {}
291 
292  static pxr_boost::python::object GetValueFromName(const std::string& name) {
293  bool found = false;
294  const TfEnum value = TfEnum::GetValueFromName<T>(name, &found);
295  return found
298  }
299 };
300 
301 // Sanitizes the given \p name for use as a Python identifier. This includes
302 // replacing spaces with '_' and appending '_' to names matching Python
303 // keywords.
304 //
305 // If \p stripPackageName is true and \p name begins with the package name,
306 // it will be stripped off.
307 TF_API
308 std::string Tf_PyCleanEnumName(std::string name,
309  bool stripPackageName = false);
310 
311 // Adds attribute of given name with given value to given scope.
312 // Issues a coding error if attribute by that name already existed.
313 TF_API
314 void Tf_PyEnumAddAttribute(pxr_boost::python::scope &s,
315  const std::string &name,
317 
318 /// \class TfPyWrapEnum
319 ///
320 /// Used to wrap enum types for script.
321 ///
322 /// TfPyWrapEnum provides a way to wrap enums for python, tying in with the \a
323 /// TfEnum system, and potentially providing automatic wrapping by using names
324 /// registered with the \a TfEnum system and by making some assumptions about
325 /// the way we structure our code. Enums that are not registered with TfEnum
326 /// may be manually wrapped using pxr_boost::python::enum_ instead.
327 ///
328 /// Example usage. For an enum that looks like this:
329 /// \code
330 /// enum FooChoices {
331 /// FooFirst,
332 /// FooSecond,
333 /// FooThird
334 /// };
335 /// \endcode
336 ///
337 /// Which has been registered in the \a TfEnum system and has names provided for
338 /// all values, it may be wrapped like this:
339 /// \code
340 /// TfPyWrapEnum<FooChoices>();
341 /// \endcode
342 ///
343 /// The enum will appear in script as Foo.Choices.{First, Second, Third} and
344 /// the values will also appear as Foo.{First, Second, Third}.
345 ///
346 /// An enum may be given an explicit name by passing a string to
347 /// TfPyWrapEnum's constructor.
348 ///
349 /// If the enum is a C++11 scoped enum (aka enum class), the values will appear
350 /// as Foo.Choices.{First, Second, Third} in the following example:
351 /// \code
352 /// enum class FooChoices {
353 /// First,
354 /// Second,
355 /// Third
356 /// };
357 /// \endcode
358 ///
359 
360 // Detect scoped enums by using that the C++ standard does not allow them to
361 // be converted to int implicitly.
363 struct TfPyWrapEnum {
364 
365 private:
366  typedef pxr_boost::python::class_<
367  Tf_TypedPyEnumWrapper<T>, pxr_boost::python::bases<Tf_PyEnumWrapper> >
368  _EnumPyClassType;
369 
370 public:
371 
372  /// Construct an enum wrapper object.
373  /// If \a name is provided, it is used as the name of the enum. Otherwise
374  /// the type name of \a T is used, with a leading MFB package name
375  /// stripped.
376  explicit TfPyWrapEnum( std::string const &name = std::string())
377  {
378  using namespace pxr_boost::python;
379 
380  const bool explicitName = !name.empty();
381 
382  // First, take either the given name, or the demangled type name.
383  std::string enumName = explicitName ? name :
384  TfStringReplace(ArchGetDemangled(typeid(T)), "::", ".");
385 
386  // If the name is dotted, take everything before the dot as the base
387  // name. This is used in repr.
388  std::string baseName = TfStringGetBeforeSuffix(enumName);
389  if (baseName == enumName)
390  baseName = std::string();
391 
392  // If the name is dotted, take the last element as the enum name.
393  if (!TfStringGetSuffix(enumName).empty())
394  enumName = TfStringGetSuffix(enumName);
395 
396  // If the name was not explicitly given, then clean it up by removing
397  // the package name prefix if it exists.
398  if (!explicitName) {
399  if (!baseName.empty()) {
400  baseName = Tf_PyCleanEnumName(
401  baseName, /* stripPackageName = */ true);
402  }
403  else {
404  enumName = Tf_PyCleanEnumName(
405  enumName, /* stripPackageName = */ true);
406  }
407  }
408 
409  if (IsScopedEnum) {
410  // Make the enumName appear in python representation
411  // for scoped enums.
412  if (!baseName.empty()) {
413  baseName += ".";
414  }
415  baseName += enumName;
416  }
417 
418  // Make a python type for T.
419  _EnumPyClassType enumClass(enumName.c_str(), no_init);
420  enumClass.def("GetValueFromName", &Tf_TypedPyEnumWrapper<T>::GetValueFromName, arg("name"));
421  enumClass.staticmethod("GetValueFromName");
422  enumClass.setattr("_baseName", baseName);
423 
424  // Register conversions for it.
426 
427  // Export values.
428  //
429  // Only strip the package name from top-level enum values.
430  // For example, if an enum named "Foo" is declared at top-level
431  // scope in Tf with values "TfBar" and "TfBaz", we want to strip
432  // off Tf so that the values in Python will be Tf.Bar and Tf.Baz.
433  const bool stripPackageName = baseName.empty();
434  _ExportValues(stripPackageName, enumClass);
435 
436  // Register with Tf so that python clients of a TfType
437  // that represents an enum are able to get to the equivalent
438  // python class with .pythonclass
439  const TfType &type = TfType::Find<T>();
440  if (!type.IsUnknown())
441  type.DefinePythonClass(enumClass);
442  }
443 
444  private:
445 
446  /// Export all values in this enum to the enclosing scope.
447  /// If no explicit names have been registered, this will export the TfEnum
448  /// registered names and values (if any).
449  void _ExportValues(bool stripPackageName, _EnumPyClassType &enumClass) {
450  pxr_boost::python::list valueList;
451 
452  for (const std::string& name : TfEnum::GetAllNames<T>()) {
453  bool success = false;
454  TfEnum enumValue = TfEnum::GetValueFromName<T>(name, &success);
455  if (!success) {
456  continue;
457  }
458 
459  const std::string cleanedName =
460  Tf_PyCleanEnumName(name, stripPackageName);
461 
462  // convert value to python.
463  Tf_TypedPyEnumWrapper<T> wrappedValue(cleanedName, enumValue);
464  pxr_boost::python::object pyValue(wrappedValue);
465 
466  // register it as the python object for this value.
467  Tf_PyEnumRegistry::GetInstance().RegisterValue(enumValue, pyValue);
468 
469  // Take all the values and export them into the current scope.
470  std::string valueName = wrappedValue.GetName();
471  if (IsScopedEnum) {
472  // If scoped enum, enum values appear on the enumClass ...
473  pxr_boost::python::scope s(enumClass);
474  Tf_PyEnumAddAttribute(s, valueName, pyValue);
475  } else {
476  // ... otherwise, enum values appear on the enclosing scope.
477  pxr_boost::python::scope s;
478  Tf_PyEnumAddAttribute(s, valueName, pyValue);
479  }
480 
481  valueList.append(pyValue);
482  }
483 
484  // Add a tuple of all the values to the enum class.
485  enumClass.setattr("allValues", pxr_boost::python::tuple(valueList));
486  }
487 
488 };
489 
491 
492 #endif // PXR_BASE_TF_PY_ENUM_H
static T & GetInstance()
Definition: singleton.h:120
TfPyWrapEnum(std::string const &name=std::string())
Definition: pyEnum.h:376
#define TF_API
Definition: api.h:23
Tf_PyEnumWrapper(std::string const &n, TfEnum const &val)
Definition: pyEnum.h:158
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
iterator end()
Definition: hashmap.h:301
TF_API std::string TfStringGetSuffix(const std::string &name, char delimiter= '.')
GLsizei const GLfloat * value
Definition: glcorearb.h:824
ARCH_API std::string ArchGetDemangled(const std::string &typeName)
std::string GetFullName() const
Definition: pyEnum.h:169
GLdouble s
Definition: glad.h:3009
Tf_TypedPyEnumWrapper(std::string const &n, TfEnum const &val)
Definition: pyEnum.h:289
Definition: enum.h:119
Tto convert(const Tfrom &source)
friend TfEnum operator|(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:226
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3556
auto arg(const Char *name, const T &arg) -> detail::named_arg< Char, T >
Definition: core.h:1859
static pxr_boost::python::object GetValueFromName(const std::string &name)
Definition: pyEnum.h:292
friend bool operator<(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:187
friend TfEnum operator^(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:260
long GetValue() const
Definition: pyEnum.h:160
friend bool operator<=(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:207
friend bool operator==(Tf_PyEnumWrapper const &self, long other)
Definition: pyEnum.h:172
Tf_PyEnumWrapper This
Definition: pyEnum.h:156
friend bool operator>(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:201
GLdouble n
Definition: glcorearb.h:2008
GLint GLint GLsizei GLint GLenum GLenum type
Definition: glcorearb.h:108
TF_API std::string Tf_PyCleanEnumName(std::string name, bool stripPackageName=false)
const std::type_info & GetType() const
Returns the type of the enum value, as an std::type_info.
Definition: enum.h:226
void RegisterEnumConversions()
Definition: pyEnum.h:73
TF_API void TfPyThrowTypeError(const char *msg)
static TF_API This & GetInstance()
Definition: pyEnum.h:65
static TF_API std::string GetFullName(TfEnum val)
TF_API_TEMPLATE_CLASS(TfSingleton< Tf_PyEnumRegistry >)
const int & GetValueAsInt() const
Returns the integral value of the enum value.
Definition: enum.h:231
GLuint const GLchar * name
Definition: glcorearb.h:786
TF_API void RegisterValue(TfEnum const &e, pxr_boost::python::object const &obj)
TfEnum value
Definition: pyEnum.h:281
GLdouble t
Definition: glad.h:2397
TF_API std::string Tf_PyEnumRepr(pxr_boost::python::object const &self)
friend TfEnum operator&(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:243
std::string name
Definition: pyEnum.h:280
friend TfEnum operator~(Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:277
friend bool operator>=(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:213
TF_API void Tf_PyEnumAddAttribute(pxr_boost::python::scope &s, const std::string &name, const pxr_boost::python::object &value)
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1425
static TF_API std::string GetDisplayName(TfEnum val)
GLuint GLfloat * val
Definition: glcorearb.h:1608
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:74
Tf_PyEnumRegistry This
Definition: pyEnum.h:56
Definition: type.h:47
std::string GetDisplayName() const
Definition: pyEnum.h:166
std::string GetName() const
Definition: pyEnum.h:163
TF_API std::string TfStringGetBeforeSuffix(const std::string &name, char delimiter= '.')
bool IsA() const
True if *this has been assigned any enumerated value of type T.
Definition: enum.h:215
TF_API std::string TfStringReplace(const std::string &source, const std::string &from, const std::string &to)
Definition: format.h:1821
friend bool operator!=(Tf_PyEnumWrapper const &lhs, Tf_PyEnumWrapper const &rhs)
Definition: pyEnum.h:182
GLenum src
Definition: glcorearb.h:1793