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