Loading...
Searching...
No Matches
pySpan.h
Go to the documentation of this file.
1//
2// Copyright 2026 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_SPAN_H
8#define PXR_BASE_TF_PY_SPAN_H
9
39
40#include "pxr/pxr.h"
41#include "pxr/base/tf/api.h"
43#include "pxr/base/tf/span.h"
44
45#include "pxr/external/boost/python/converter/arg_from_python.hpp"
46#include "pxr/external/boost/python/converter/registered.hpp"
47#include "pxr/external/boost/python/extract.hpp"
48
49#include <cstddef>
50#include <initializer_list>
51#include <memory>
52#include <type_traits>
53#include <typeinfo>
54#include <utility>
55
56PXR_NAMESPACE_OPEN_SCOPE
57
61using Tf_PySpanSourceFn =
62 bool (*)(PyObject *obj, bool wantMutable, void **data, size_t *size);
63
64TF_API void
65Tf_PyRegisterSpanSource(std::type_info const &elemType, Tf_PySpanSourceFn fn);
66
67TF_API void
68Tf_PyRegisterSpanBufferLayout(std::type_info const &elemType, size_t elemSize,
69 char scalarFormat,
70 Py_ssize_t const *elemShape, int elemNdim);
71
72TF_API bool
73Tf_PyGetSpanFromSources(PyObject *obj, std::type_info const &elemType,
74 bool wantMutable, void **data, size_t *size);
75
76// On success, *view holds a buffer that the caller must release.
77TF_API bool
78Tf_PyGetSpanFromBuffer(PyObject *obj, std::type_info const &elemType,
79 bool wantMutable, Py_buffer *view,
80 void **data, size_t *size);
81
82template <class Source, class T>
83bool
84Tf_PySpanSourceFromLValue(PyObject *obj, bool wantMutable,
85 void **data, size_t *size)
86{
87 using namespace PXR_BOOST_NAMESPACE::python::converter;
88 using Value = std::remove_cv_t<T>;
89 if (std::is_const_v<T> && wantMutable) {
90 return false;
91 }
92 void *p = get_lvalue_from_python(obj, registered<Source>::converters);
93 if (!p) {
94 return false;
95 }
96 Source &src = *static_cast<Source *>(p);
97 if constexpr (!std::is_const_v<T>) {
98 if (wantMutable) {
99 TfSpan<Value> span(src);
100 *data = span.data();
101 *size = span.size();
102 return true;
103 }
104 }
105 // Convert from const Source so that containers like VtArray do not detach.
106 TfSpan<const Value> span(std::as_const(src));
107 *data = const_cast<Value *>(span.data());
108 *size = span.size();
109 return true;
110}
111
118template <class Source, class T>
119void
121{
122 using Value = std::remove_cv_t<T>;
123 static_assert(std::is_constructible_v<TfSpan<const Value>, Source const &>,
124 "Source must convert to TfSpan<const T>");
125 static_assert(std::is_const_v<T> ||
126 std::is_constructible_v<TfSpan<Value>, Source &>,
127 "Source must convert to TfSpan<T>; for read-only sources, "
128 "register with const T");
129 Tf_PyRegisterSpanSource(typeid(Value),
130 Tf_PySpanSourceFromLValue<Source, T>);
131}
132
140template <class T>
141void
143 std::initializer_list<Py_ssize_t> elemShape = {})
144{
145 Tf_PyRegisterSpanBufferLayout(
146 typeid(T), sizeof(T), scalarFormat,
147 elemShape.begin(), static_cast<int>(elemShape.size()));
148}
149
150// Holds a TfSpan<T> and whatever keeps its elements alive for the duration of a
151// wrapped function call.
152template <class T>
153class Tf_PySpanArgConverter
154{
155 using Value = std::remove_cv_t<T>;
156 static constexpr bool _isMutable = !std::is_const_v<T>;
157
158public:
159 explicit Tf_PySpanArgConverter(PyObject *obj) {
160 void *data;
161 size_t size;
162 if (Tf_PyGetSpanFromSources(
163 obj, typeid(Value), _isMutable, &data, &size) ||
164 (_hasView = Tf_PyGetSpanFromBuffer(
165 obj, typeid(Value), _isMutable, &_view, &data, &size))) {
166 _span = TfSpan<T>(static_cast<T *>(data), size);
167 _convertible = true;
168 }
169 else if constexpr (!_isMutable) {
170 _convertible = _CopyFromSequence(obj);
171 }
172 }
173
174 ~Tf_PySpanArgConverter() {
175 if (_hasView) {
176 PyBuffer_Release(&_view);
177 }
178 }
179
180 Tf_PySpanArgConverter(Tf_PySpanArgConverter const &) = delete;
181 Tf_PySpanArgConverter &operator=(Tf_PySpanArgConverter const &) = delete;
182
183 bool convertible() const { return _convertible; }
184
185 TfSpan<T> &operator()() { return _span; }
186
187private:
188 bool _CopyFromSequence(PyObject *obj) {
189 if (!PySequence_Check(obj) ||
190 PyUnicode_Check(obj) || PyBytes_Check(obj)) {
191 return false;
192 }
193 Py_ssize_t len = PySequence_Size(obj);
194 if (len < 0) {
195 PyErr_Clear();
196 return false;
197 }
198 std::unique_ptr<Value []> storage(new Value[len]);
199 for (Py_ssize_t i = 0; i != len; ++i) {
200 PyObject *item = PySequence_GetItem(obj, i);
201 if (!item) {
202 PyErr_Clear();
203 return false;
204 }
205 PXR_BOOST_NAMESPACE::python::extract<Value> get(item);
206 bool ok = get.check();
207 if (ok) {
208 storage[i] = get();
209 }
210 Py_DECREF(item);
211 if (!ok) {
212 return false;
213 }
214 }
215 _span = TfSpan<T>(storage.get(), len);
216 _storage = std::move(storage);
217 return true;
218 }
219
220 TfSpan<T> _span;
221 std::unique_ptr<Value []> _storage;
222 Py_buffer _view;
223 bool _hasView = false;
224 bool _convertible = false;
225};
226
227PXR_NAMESPACE_CLOSE_SCOPE
228
229namespace PXR_BOOST_NAMESPACE::python::converter {
230
231template <class T>
232struct arg_rvalue_from_python<PXR_NS::TfSpan<T>>
233 : PXR_NS::Tf_PySpanArgConverter<T>
234{
235 using result_type = PXR_NS::TfSpan<T> &;
236 using PXR_NS::Tf_PySpanArgConverter<T>::Tf_PySpanArgConverter;
237};
238
239template <class T>
240struct arg_rvalue_from_python<PXR_NS::TfSpan<T> const &>
241 : PXR_NS::Tf_PySpanArgConverter<T>
242{
243 using result_type = PXR_NS::TfSpan<T> &;
244 using PXR_NS::Tf_PySpanArgConverter<T>::Tf_PySpanArgConverter;
245};
246
247} // namespace PXR_BOOST_NAMESPACE::python::converter
248
249#endif // PXR_BASE_TF_PY_SPAN_H
Represents a range of contiguous elements.
Definition span.h:72
Intended to replace a direct include of Python.h, which causes several build problems with certain co...
void TfPyRegisterSpanBufferLayout(char scalarFormat, std::initializer_list< Py_ssize_t > elemShape={})
Register the buffer layout of T: a C-contiguous array of elemShape-shaped blocks of scalars described...
Definition pySpan.h:142
void TfPyRegisterSpanSource()
Register the wrapped C++ type Source as a span source for TfSpan<T>.
Definition pySpan.h:120