Loading...
Searching...
No Matches
pyIdentity.h
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_IDENTITY_H
8#define PXR_BASE_TF_PY_IDENTITY_H
9
10#include "pxr/pxr.h"
11
12#include "pxr/base/tf/api.h"
13#include "pxr/base/tf/pyLock.h"
14#include "pxr/base/tf/pyUtils.h"
15
18#include "pxr/base/tf/refPtr.h"
21#include "pxr/base/tf/weakPtr.h"
22
23#include <boost/python/class.hpp>
24#include <boost/python/handle.hpp>
25#include <boost/python/object.hpp>
26
27#include "pxr/base/tf/hashmap.h"
28
29// Specializations for boost::python::pointee and get_pointer for TfRefPtr and
30// TfWeakPtr.
31namespace boost { namespace python {
32
33// TfWeakPtrFacade
34template <template <class> class X, class Y>
35struct pointee< PXR_NS::TfWeakPtrFacade<X, Y> > {
36 typedef Y type;
37};
38
39// TfRefPtr
40template <typename T>
41struct pointee< PXR_NS::TfRefPtr<T> > {
42 typedef T type;
43};
44
45}}
46
47PXR_NAMESPACE_OPEN_SCOPE
48
49struct Tf_PyIdentityHelper
50{
51 // Set the identity of ptr (which derives from TfPtrBase) to be the
52 // python object \a obj.
53 TF_API
54 static void Set(void const *id, PyObject *obj);
55
56 // Return a new reference to the python object associated with ptr. If
57 // there is none, return 0.
58 TF_API
59 static PyObject *Get(void const *id);
60
61 TF_API
62 static void Erase(void const *id);
63
64 // Acquire a reference to the python object associated with ptrBase
65 // if not already acquired.
66 TF_API
67 static void Acquire(void const *id);
68
69 // Release a reference to the python object associated with ptrBase
70 // if we own a reference.
71 TF_API
72 static void Release(void const *id);
73
74};
75
76template <class Ptr>
77void Tf_PyReleasePythonIdentity(Ptr const &ptr, PyObject *obj)
78{
79 Tf_PySetPythonIdentity(ptr, obj);
80 Tf_PyIdentityHelper::Release(ptr.GetUniqueIdentifier());
81}
82
83void Tf_PyOwnershipRefBaseUniqueChanged(TfRefBase const *refBase,
84 bool isNowUnique);
85
86struct Tf_PyOwnershipPtrMap
87{
88 typedef TfHashMap<TfRefBase const *, void const *, TfHash>
89 _CacheType;
90 TF_API
91 static void Insert(TfRefBase *refBase, void const *uniqueId);
92 TF_API
93 static void const *Lookup(TfRefBase const *refBase);
94 TF_API
95 static void Erase(TfRefBase *refBase);
96 private:
97 static _CacheType _cache;
98};
99
100
101// Doxygen generates files whose names are mangled typenames. This is fine
102// except when the filenames get longer than 256 characters. This is one case
103// of that, so we'll just disable doxygen. There's no actual doxygen doc here,
104// so this is fine. If/when this gets solved for real, we can remove this
105// (6/06)
106#ifndef doxygen
107
108
109template <class Ptr, typename Enable = void>
110struct Tf_PyOwnershipHelper {
111 template <typename U>
112 static void Add(U const &, const void *, PyObject *) {}
113 template <typename U>
114 static void Remove(U const &, PyObject *) {}
115};
116
117template <typename Ptr>
118struct Tf_PyOwnershipHelper<Ptr,
119 std::enable_if_t<
120 std::is_same<TfRefPtr<typename Ptr::DataType>, Ptr>::value &&
121 std::is_base_of<TfRefBase, typename Ptr::DataType>::value>>
122{
123 struct _RefPtrHolder {
124 static boost::python::object
125 Get(Ptr const &refptr) {
126 TfPyLock pyLock;
127 _WrapIfNecessary();
128 return boost::python::object(_RefPtrHolder(refptr));
129 }
130 static void _WrapIfNecessary() {
131 TfPyLock pyLock;
132 if (TfPyIsNone(TfPyGetClassObject<_RefPtrHolder>())) {
133 std::string name =
134 "__" + ArchGetDemangled(typeid(typename Ptr::DataType)) +
135 "__RefPtrHolder";
136 name = TfStringReplace(name, "<", "_");
137 name = TfStringReplace(name, ">", "_");
138 name = TfStringReplace(name, "::", "_");
139 boost::python::class_<_RefPtrHolder>(name.c_str(),
140 boost::python::no_init);
141 }
142 }
143 private:
144 explicit _RefPtrHolder(Ptr const &refptr) : _refptr(refptr) {}
145 Ptr _refptr;
146 };
147
148 static void Add(Ptr ptr, const void *uniqueId, PyObject *self) {
149
150 TfPyLock pyLock;
151
152 // Make the python object keep the c++ object alive.
153 int ret = PyObject_SetAttrString(self, "__owner",
154 _RefPtrHolder::Get(ptr).ptr());
155 if (ret == -1) {
156 // CODE_COVERAGE_OFF
157 TF_WARN("Could not set __owner attribute on python object!");
158 PyErr_Clear();
159 return;
160 // CODE_COVERAGE_ON
161 }
162 TfRefBase *refBase =
163 static_cast<TfRefBase *>(get_pointer(ptr));
164 Tf_PyOwnershipPtrMap::Insert(refBase, uniqueId);
165 }
166
167 static void Remove(Ptr ptr, PyObject *obj) {
168 TfPyLock pyLock;
169
170 if (!ptr) {
171 // CODE_COVERAGE_OFF Can only happen if there's a bug.
172 TF_CODING_ERROR("Removing ownership from null/expired ptr!");
173 return;
174 // CODE_COVERAGE_ON
175 }
176
177 if (PyObject_HasAttrString(obj, "__owner")) {
178 // We are guaranteed that ptr is not unique at this point,
179 // as __owner has a reference and ptr is itself a
180 // reference. This also guarantees us that the object owns
181 // a reference to its python object, so we don't need to
182 // explicitly acquire a reference here.
183 TF_AXIOM(!ptr->IsUnique());
184 // Remove this object from the cache of refbase to uniqueId
185 // that we use for python-owned things.
186 Tf_PyOwnershipPtrMap::Erase(get_pointer(ptr));
187 // Remove the __owner attribute.
188 if (PyObject_DelAttrString(obj, "__owner") == -1) {
189 // CODE_COVERAGE_OFF It's hard to make this occur.
190 TF_WARN("Undeletable __owner attribute on python object!");
191 PyErr_Clear();
192 // CODE_COVERAGE_ON
193 }
194 }
195 }
196};
197
198#endif // doxygen -- see comment above.
199
200
201template <typename Ptr>
202struct Tf_PyIsRefPtr {
203 static const bool value = false;
204};
205
206template <typename T>
207struct Tf_PyIsRefPtr<TfRefPtr<T> > {
208 static const bool value = true;
209};
210
211
212template <class Ptr>
213std::enable_if_t<Tf_PyIsRefPtr<Ptr>::value>
214Tf_PySetPythonIdentity(Ptr const &, PyObject *)
215{
216}
217
218template <class Ptr>
219std::enable_if_t<!Tf_PyIsRefPtr<Ptr>::value>
220Tf_PySetPythonIdentity(Ptr const &ptr, PyObject *obj)
221{
222 if (ptr.GetUniqueIdentifier()) {
223 Tf_PyIdentityHelper::Set(ptr.GetUniqueIdentifier(), obj);
224 // Make sure we hear about it when this weak base dies so we can remove
225 // it from the map.
226 ptr.EnableExtraNotification();
227 }
228}
229
230template <class Ptr>
231PyObject *Tf_PyGetPythonIdentity(Ptr const &ptr)
232{
233 PyObject *ret = Tf_PyIdentityHelper::Get(ptr.GetUniqueIdentifier());
234 return ret;
235}
236
237template <class Ptr>
238void Tf_PyRemovePythonOwnership(Ptr const &t, PyObject *obj)
239{
240 Tf_PyOwnershipHelper<Ptr>::Remove(t, obj);
241}
242
243template <class Ptr>
244void Tf_PyAddPythonOwnership(Ptr const &t, const void *uniqueId, PyObject *obj)
245{
246 Tf_PyOwnershipHelper<Ptr>::Add(t, uniqueId, obj);
247}
248
249PXR_NAMESPACE_CLOSE_SCOPE
250
251#endif // PXR_BASE_TF_PY_IDENTITY_H
Low-level utilities for informing users of various internal and external diagnostic conditions.
Miscellaneous Utilities for dealing with script.
TF_API bool TfPyIsNone(boost::python::object const &obj)
Return true iff obj is None.
Convenience class for accessing the Python Global Interpreter Lock.
Definition: pyLock.h:105
Enable a concrete base class for use with TfRefPtr.
Definition: refBase.h:56
Reference-counted smart pointer utility class.
Definition: refPtr.h:584
Demangle C++ typenames generated by the typeid() facility.
std::string ArchGetDemangled()
Return demangled RTTI generated-type name.
Definition: demangle.h:86
#define TF_AXIOM(cond)
Aborts if the condition cond is not met.
Definition: diagnostic.h:193
#define TF_CODING_ERROR(fmt, args)
Issue an internal programming error, but continue execution.
Definition: diagnostic.h:68
#define TF_WARN(...)
Issue a warning, but continue execution.
Definition: diagnostic.h:132
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.
STL namespace.
Reference counting.
Safely compare C++ RTTI type structures.
Definitions of basic string utilities in tf.
Pointer storage with deletion detection.