HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pyFunction.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_FUNCTION_H
25 #define PXR_BASE_TF_PY_FUNCTION_H
26 
27 #include "pxr/pxr.h"
28 
29 #include "pxr/base/tf/pyCall.h"
30 #include "pxr/base/tf/pyLock.h"
32 #include "pxr/base/tf/pyUtils.h"
33 
34 #include <hboost/python/converter/from_python.hpp>
35 #include <hboost/python/converter/registered.hpp>
36 #include <hboost/python/converter/rvalue_from_python_data.hpp>
37 #include <hboost/python/extract.hpp>
38 #include <hboost/python/handle.hpp>
39 #include <hboost/python/object.hpp>
40 
41 #include <hboost/function.hpp>
42 
43 #include <functional>
44 
46 
47 template <typename T>
49 
50 template <typename Ret, typename... Args>
51 struct TfPyFunctionFromPython<Ret (Args...)>
52 {
53  struct Call
54  {
56 
57  Ret operator()(Args... args) {
58  TfPyLock lock;
59  return TfPyCall<Ret>(callable)(args...);
60  }
61  };
62 
63  struct CallWeak
64  {
66 
67  Ret operator()(Args... args) {
68  using namespace hboost::python;
69  // Attempt to get the referenced callable object.
70  TfPyLock lock;
71  object callable(handle<>(borrowed(PyWeakref_GetObject(weak.ptr()))));
72  if (TfPyIsNone(callable)) {
73  TF_WARN("Tried to call an expired python callback");
74  return Ret();
75  }
76  return TfPyCall<Ret>(callable)(args...);
77  }
78  };
79 
80  struct CallMethod
81  {
84 
85  Ret operator()(Args... args) {
86  using namespace hboost::python;
87  // Attempt to get the referenced self parameter, then build a new
88  // instance method and call it.
89  TfPyLock lock;
90  PyObject *self = PyWeakref_GetObject(weakSelf.ptr());
91  if (self == Py_None) {
92  TF_WARN("Tried to call a method on an expired python instance");
93  return Ret();
94  }
95  object method(handle<>(PyMethod_New(func.ptr(), self)));
96  return TfPyCall<Ret>(method)(args...);
97  }
98  };
99 
101  RegisterFunctionType<hboost::function<Ret (Args...)>>();
102  RegisterFunctionType<std::function<Ret (Args...)>>();
103  }
104 
105  template <typename FuncType>
106  static void
108  using namespace hboost::python;
110  insert(&convertible, &construct<FuncType>, type_id<FuncType>());
111  }
112 
113  static void *convertible(PyObject *obj) {
114  return ((obj == Py_None) || PyCallable_Check(obj)) ? obj : 0;
115  }
116 
117  template <typename FuncType>
118  static void construct(PyObject *src, hboost::python::converter::
119  rvalue_from_python_stage1_data *data) {
120  using std::string;
121  using namespace hboost::python;
122 
123  void *storage = ((converter::rvalue_from_python_storage<FuncType> *)
124  data)->storage.bytes;
125 
126  if (src == Py_None) {
127  new (storage) FuncType();
128  } else {
129 
130  // In the case of instance methods, holding a strong reference will
131  // keep the bound 'self' argument alive indefinitely, which is
132  // undesirable. Unfortunately, we can't just keep a weak reference to
133  // the instance method, because python synthesizes these on-the-fly.
134  // Instead we do something like what PyQt's SIP does, and break the
135  // method into three parts: the class, the function, and the self
136  // parameter. We keep strong references to the class and the
137  // function, but a weak reference to 'self'. Then at call-time, if
138  // self has not expired, we build a new instancemethod and call it.
139  //
140  // Otherwise if the callable is a lambda (checked in a hacky way, but
141  // mirroring SIP), we take a strong reference.
142  //
143  // For all other callables, we attempt to take weak references to
144  // them. If that fails, we take a strong reference.
145  //
146  // This is all sort of contrived, but seems to have the right behavior
147  // for most usage patterns.
148 
149  object callable(handle<>(borrowed(src)));
150  PyObject *pyCallable = callable.ptr();
151  PyObject *self =
152  PyMethod_Check(pyCallable) ?
153  PyMethod_GET_SELF(pyCallable) : NULL;
154 
155  if (self) {
156  // Deconstruct the method and attempt to get a weak reference to
157  // the self instance.
158  object func(handle<>(borrowed(PyMethod_GET_FUNCTION(
159  pyCallable))));
160  object weakSelf(handle<>(PyWeakref_NewRef(self, NULL)));
161  new (storage)
162  FuncType(CallMethod{
163  TfPyObjWrapper(func),
164  TfPyObjWrapper(weakSelf)
165  });
166 
167  } else if (PyObject_HasAttrString(pyCallable, "__name__") &&
168  extract<string>(callable.attr("__name__"))()
169  == "<lambda>") {
170  // Explicitly hold on to strong references to lambdas.
171  new (storage) FuncType(Call{TfPyObjWrapper(callable)});
172  } else {
173  // Attempt to get a weak reference to the callable.
174  if (PyObject *weakCallable =
175  PyWeakref_NewRef(pyCallable, NULL)) {
176  new (storage)
177  FuncType(
178  CallWeak{TfPyObjWrapper(
179  object(handle<>(weakCallable)))});
180  } else {
181  // Fall back to taking a strong reference.
182  PyErr_Clear();
183  new (storage) FuncType(Call{TfPyObjWrapper(callable)});
184  }
185  }
186  }
187 
188  data->convertible = storage;
189  }
190 };
191 
193 
194 #endif // PXR_BASE_TF_PY_FUNCTION_H
getFileOption("OpenEXR:storage") storage
Definition: HDK_Image.dox:276
GLsizei const GLchar *const * string
Definition: glcorearb.h:814
OIIO_FORCEINLINE vbool4 insert(const vbool4 &a, bool val)
Helper: substitute val for a[i].
Definition: simd.h:3436
#define TF_WARN
static void * convertible(PyObject *obj)
Definition: pyFunction.h:113
GLenum func
Definition: glcorearb.h:783
PXR_NAMESPACE_CLOSE_SCOPE PXR_NAMESPACE_OPEN_SCOPE
Definition: path.h:1441
#define PXR_NAMESPACE_CLOSE_SCOPE
Definition: pxr.h:91
**If you just want to fire and args
Definition: thread.h:609
TF_API bool TfPyIsNone(hboost::python::object const &obj)
Return true iff obj is None.
Definition: format.h:895
static void construct(PyObject *src, hboost::python::converter::rvalue_from_python_stage1_data *data)
Definition: pyFunction.h:118
GLenum src
Definition: glcorearb.h:1793