All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules 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
12
13#include "pxr/pxr.h"
14
15#include "pxr/base/tf/api.h"
16#include "pxr/base/tf/pyObjWrapper.h"
17#include "pxr/base/tf/pyUtils.h"
18#include "pxr/base/tf/type.h"
19
21#include "pxr/base/tf/enum.h"
22#include "pxr/base/tf/hash.h"
23#include "pxr/base/tf/hashmap.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
42PXR_NAMESPACE_OPEN_SCOPE
43
47class Tf_PyEnum { };
48
53class Tf_PyEnumRegistry {
54
55 public:
56 typedef Tf_PyEnumRegistry This;
57
58 private:
59 Tf_PyEnumRegistry();
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>
73 void RegisterEnumConversions() {
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() {
87 pxr_boost::python::converter::registry::insert
88 (&convertible, &construct, pxr_boost::python::type_id<T>());
89 }
90 static void *convertible(PyObject *obj) {
91 TfHashMap<PyObject *, TfEnum, _ObjectHash> const &o2e =
92 Tf_PyEnumRegistry::GetInstance()._objectsToEnums;
93 TfHashMap<PyObject *, TfEnum, _ObjectHash>::const_iterator
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.
98 if (std::is_same<T, TfEnum>::value ||
99 (std::is_integral<T>::value && !std::is_enum<T>::value))
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
141 TfHashMap<TfEnum, PyObject *, TfHash> _enumsToObjects;
142 TfHashMap<PyObject *, TfEnum, _ObjectHash> _objectsToEnums;
143
144};
145
146TF_API_TEMPLATE_CLASS(TfSingleton<Tf_PyEnumRegistry>);
147
148// Private function used for __repr__ of wrapped enum types.
149TF_API
150std::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.
154struct Tf_PyEnumWrapper : public Tf_PyEnum
155{
156 typedef Tf_PyEnumWrapper This;
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 {
167 return TfEnum::GetDisplayName(value);
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) <
196 TfEnum::GetFullName(rhs.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;
281 TfEnum value;
282};
283
284// Private template class which is instantiated and exposed to python for each
285// registered enum type.
286template <typename T>
287struct Tf_TypedPyEnumWrapper : Tf_PyEnumWrapper
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
296 ? pxr_boost::python::object(value)
297 : pxr_boost::python::object();
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.
307TF_API
308std::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.
313TF_API
314void Tf_PyEnumAddAttribute(pxr_boost::python::scope &s,
315 const std::string &name,
316 const pxr_boost::python::object &value);
317
359
360// Detect scoped enums by using that the C++ standard does not allow them to
361// be converted to int implicitly.
362template <typename T, bool IsScopedEnum = !std::is_convertible<T, int>::value>
364
365private:
366 typedef pxr_boost::python::class_<
367 Tf_TypedPyEnumWrapper<T>, pxr_boost::python::bases<Tf_PyEnumWrapper> >
368 _EnumPyClassType;
369
370public:
371
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.
425 Tf_PyEnumRegistry::GetInstance().RegisterEnumConversions<T>();
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
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
490PXR_NAMESPACE_CLOSE_SCOPE
491
492#endif // PXR_BASE_TF_PY_ENUM_H
A simple iterator adapter for STL containers.
Miscellaneous Utilities for dealing with script.
TF_API void TfPyThrowTypeError(const char *msg)
Raises a Python TypeError with the given error msg and throws a pxr_boost::python::error_already_set ...
An enum class that records both enum type and enum value.
Definition: enum.h:120
static TF_API std::string GetFullName(TfEnum val)
Returns the fully-qualified name for an enumerated value.
static TF_API std::string GetDisplayName(TfEnum val)
Returns the display name for an enumerated value.
Manage a single instance of an object (see.
Definition: singleton.h:105
static T & GetInstance()
Return a reference to an object of type T, creating it if necessary.
Definition: singleton.h:120
TfType represents a dynamic runtime type.
Definition: type.h:48
TF_API void DefinePythonClass(const TfPyObjWrapper &classObj) const
Define the Python class object corresponding to this TfType.
bool IsUnknown() const
Return true if this is the unknown type, representing a type unknown to the TfType system.
Definition: type.h:374
Demangle C++ typenames generated by the typeid() facility.
std::string ArchGetDemangled()
Return demangled RTTI generated-type name.
Definition: demangle.h:86
TF_API std::string TfStringReplace(const std::string &source, const std::string &from, const std::string &to)
Replaces all occurrences of string from with to in source.
TF_API std::string TfStringGetBeforeSuffix(const std::string &name, char delimiter='.')
Returns everything up to the suffix of a string.
TF_API std::string TfStringGetSuffix(const std::string &name, char delimiter='.')
Returns the suffix of a string.
Manage a single instance of an object.
Definitions of basic string utilities in tf.
Used to wrap enum types for script.
Definition: pyEnum.h:363
TfPyWrapEnum(std::string const &name=std::string())
Construct an enum wrapper object.
Definition: pyEnum.h:376