@@ -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
252257namespace {
@@ -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