Skip to content

Commit 20ac0fc

Browse files
committed
Use PySequence_Fast() for flexibility with sequence types
1 parent 81bff12 commit 20ac0fc

File tree

1 file changed

+50
-35
lines changed

1 file changed

+50
-35
lines changed

plugins/python/src/modulewrap.cpp

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -221,32 +221,37 @@ namespace {
221221
void operator()(intptr_t arg0, intptr_t arg1, intptr_t arg2) { callv(arg0, arg1, arg2); }
222222
};
223223

224-
static std::vector<std::string> cseq(PyObject* coll)
225-
{
226-
size_t len = coll ? (size_t)PySequence_Size(coll) : 0;
227-
std::vector<std::string> cargs{len};
228-
229-
for (size_t i = 0; i < len; ++i) {
230-
PyObject* item = PySequence_GetItem(coll, i);
231-
if (item) {
232-
char const* p = PyUnicode_AsUTF8(item);
233-
if (p) {
234-
Py_ssize_t sz = PyUnicode_GetLength(item);
235-
cargs[i].assign(p, (std::string::size_type)sz);
236-
}
237-
Py_DECREF(item);
224+
static std::vector<std::string> cseq(PyObject* coll)
225+
{
226+
if (!coll) {
227+
return std::vector<std::string>{};
228+
}
238229

239-
if (!p) {
240-
PyErr_Format(PyExc_TypeError, "could not convert item %d to string", (int)i);
241-
break;
242-
}
243-
} else
244-
break; // Python error already set
230+
// coll is guaranteed to be a list or tuple (from PySequence_Fast)
231+
Py_ssize_t len = PySequence_Fast_GET_SIZE(coll);
232+
std::vector<std::string> cargs;
233+
cargs.reserve(static_cast<size_t>(len));
234+
235+
PyObject** items = PySequence_Fast_ITEMS(coll);
236+
for (Py_ssize_t i = 0; i < len; ++i) {
237+
PyObject* item = items[i]; // borrowed reference
238+
if (!PyUnicode_Check(item)) {
239+
PyErr_Format(PyExc_TypeError, "item %d must be a string", (int)i);
240+
return std::vector<std::string>{}; // Error set
241+
}
242+
243+
char const* p = PyUnicode_AsUTF8(item);
244+
if (!p) {
245+
return std::vector<std::string>{}; // Error already set
245246
}
246247

247-
return cargs;
248+
Py_ssize_t sz = PyUnicode_GetLength(item);
249+
cargs.emplace_back(p, static_cast<std::string::size_type>(sz));
248250
}
249251

252+
return cargs;
253+
}
254+
250255
} // unnamed namespace
251256

252257
namespace {
@@ -541,25 +546,35 @@ static PyObject* parse_args(PyObject* args,
541546
return nullptr;
542547
}
543548

544-
if (!PyList_Check(input) && !PyTuple_Check(input)) {
545-
PyErr_SetString(PyExc_TypeError, "input parameter must be a list or tuple");
546-
return nullptr;
547-
}
549+
// Accept any sequence type (list, tuple, custom sequences)
550+
PyObject* input_fast = PySequence_Fast(input, "input_family must be a sequence");
551+
if (!input_fast) {
552+
return nullptr; // TypeError already set by PySequence_Fast
553+
}
548554

549-
if (output && !PyList_Check(output) && !PyTuple_Check(output)) {
550-
PyErr_SetString(PyExc_TypeError, "output parameter must be a list or tuple");
555+
PyObject* output_fast = nullptr;
556+
if (output) {
557+
output_fast = PySequence_Fast(output, "output_products must be a sequence");
558+
if (!output_fast) {
559+
Py_DECREF(input_fast);
551560
return nullptr;
552561
}
562+
}
553563

554-
// convert input and output declarations, to be able to pass them to Phlex
555-
input_labels = cseq(input);
556-
output_labels = cseq(output);
557-
if (output_labels.size() > 1) {
558-
PyErr_SetString(PyExc_TypeError, "only a single output supported");
559-
return nullptr;
560-
}
564+
// convert input and output declarations, to be able to pass them to Phlex
565+
input_labels = cseq(input_fast);
566+
output_labels = cseq(output_fast);
567+
568+
// Clean up fast sequences
569+
Py_DECREF(input_fast);
570+
Py_XDECREF(output_fast);
571+
572+
if (output_labels.size() > 1) {
573+
PyErr_SetString(PyExc_TypeError, "only a single output supported");
574+
return nullptr;
575+
}
561576

562-
// retrieve C++ (matching) types from annotations
577+
// retrieve C++ (matching) types from annotations
563578
input_types.reserve(input_labels.size());
564579

565580
PyObject* sann = PyUnicode_FromString("__annotations__");

0 commit comments

Comments
 (0)