@@ -509,15 +509,18 @@ match_class_attr(PyThreadState *tstate, PyObject *subject, PyObject *type,
509509 PyObject * name , PyObject * seen )
510510{
511511 assert (PyUnicode_CheckExact (name ));
512- assert (PySet_CheckExact (seen ));
513- if (PySet_Contains (seen , name ) || PySet_Add (seen , name )) {
514- if (!_PyErr_Occurred (tstate )) {
515- // Seen it before!
516- _PyErr_Format (tstate , PyExc_TypeError ,
517- "%s() got multiple sub-patterns for attribute %R" ,
518- ((PyTypeObject * )type )-> tp_name , name );
512+ // Only check for duplicates if seen is not NULL.
513+ if (seen != NULL ) {
514+ assert (PySet_CheckExact (seen ));
515+ if (PySet_Contains (seen , name ) || PySet_Add (seen , name )) {
516+ if (!_PyErr_Occurred (tstate )) {
517+ // Seen it before!
518+ _PyErr_Format (tstate , PyExc_TypeError ,
519+ "%s() got multiple sub-patterns for attribute %R" ,
520+ ((PyTypeObject * )type )-> tp_name , name );
521+ }
522+ return NULL ;
519523 }
520- return NULL ;
521524 }
522525 PyObject * attr ;
523526 (void )PyObject_GetOptionalAttr (subject , name , & attr );
@@ -540,14 +543,26 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type,
540543 if (PyObject_IsInstance (subject , type ) <= 0 ) {
541544 return NULL ;
542545 }
546+ // Short circuit if there aren't any arguments:
547+ Py_ssize_t nkwargs = PyTuple_GET_SIZE (kwargs );
548+ Py_ssize_t nattrs = nargs + nkwargs ;
549+ if (!nattrs ) {
550+ return PyTuple_New (0 );
551+ }
543552 // So far so good:
544- PyObject * seen = PySet_New (NULL );
545- if (seen == NULL ) {
546- return NULL ;
553+ PyObject * seen = NULL ;
554+ // Only check for duplicates if there is at least one positional attribute
555+ // and two or more attributes in total. Duplicate keyword attributes are
556+ // detected during the compile stage and raise a SyntaxError.
557+ if (nargs > 0 && nattrs > 1 ) {
558+ seen = PySet_New (NULL );
559+ if (seen == NULL ) {
560+ return NULL ;
561+ }
547562 }
548- PyObject * attrs = PyList_New ( 0 );
563+ PyObject * attrs = PyTuple_New ( nattrs );
549564 if (attrs == NULL ) {
550- Py_DECREF (seen );
565+ Py_XDECREF (seen );
551566 return NULL ;
552567 }
553568 // NOTE: From this point on, goto fail on failure:
@@ -588,9 +603,8 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type,
588603 }
589604 if (match_self ) {
590605 // Easy. Copy the subject itself, and move on to kwargs.
591- if (PyList_Append (attrs , subject ) < 0 ) {
592- goto fail ;
593- }
606+ assert (PyTuple_GET_ITEM (attrs , 0 ) == NULL );
607+ PyTuple_SET_ITEM (attrs , 0 , Py_NewRef (subject ));
594608 }
595609 else {
596610 for (Py_ssize_t i = 0 ; i < nargs ; i ++ ) {
@@ -606,36 +620,29 @@ _PyEval_MatchClass(PyThreadState *tstate, PyObject *subject, PyObject *type,
606620 if (attr == NULL ) {
607621 goto fail ;
608622 }
609- if (PyList_Append (attrs , attr ) < 0 ) {
610- Py_DECREF (attr );
611- goto fail ;
612- }
613- Py_DECREF (attr );
623+ assert (PyTuple_GET_ITEM (attrs , i ) == NULL );
624+ PyTuple_SET_ITEM (attrs , i , attr );
614625 }
615626 }
616627 Py_CLEAR (match_args );
617628 }
618629 // Finally, the keyword subpatterns:
619- for (Py_ssize_t i = 0 ; i < PyTuple_GET_SIZE ( kwargs ) ; i ++ ) {
630+ for (Py_ssize_t i = 0 ; i < nkwargs ; i ++ ) {
620631 PyObject * name = PyTuple_GET_ITEM (kwargs , i );
621632 PyObject * attr = match_class_attr (tstate , subject , type , name , seen );
622633 if (attr == NULL ) {
623634 goto fail ;
624635 }
625- if (PyList_Append (attrs , attr ) < 0 ) {
626- Py_DECREF (attr );
627- goto fail ;
628- }
629- Py_DECREF (attr );
636+ assert (PyTuple_GET_ITEM (attrs , nargs + i ) == NULL );
637+ PyTuple_SET_ITEM (attrs , nargs + i , attr );
630638 }
631- Py_SETREF (attrs , PyList_AsTuple (attrs ));
632- Py_DECREF (seen );
639+ Py_XDECREF (seen );
633640 return attrs ;
634641fail :
635642 // We really don't care whether an error was raised or not... that's our
636643 // caller's problem. All we know is that the match failed.
637644 Py_XDECREF (match_args );
638- Py_DECREF (seen );
645+ Py_XDECREF (seen );
639646 Py_DECREF (attrs );
640647 return NULL ;
641648}
0 commit comments