diff options
Diffstat (limited to 'Cython/Utility/MatchCase.c')
-rw-r--r-- | Cython/Utility/MatchCase.c | 456 |
1 files changed, 211 insertions, 245 deletions
diff --git a/Cython/Utility/MatchCase.c b/Cython/Utility/MatchCase.c index fea08e0c1..85f8236eb 100644 --- a/Cython/Utility/MatchCase.c +++ b/Cython/Utility/MatchCase.c @@ -1,28 +1,14 @@ ///////////////////////////// ABCCheck ////////////////////////////// #if PY_VERSION_HEX < 0x030A0000 -static int __Pyx_MatchCase_IsExactSequence(PyObject *o) { +static CYTHON_INLINE int __Pyx_MatchCase_IsExactSequence(PyObject *o) { // is one of the small list of builtin types known to be a sequence - if (PyList_CheckExact(o) || PyTuple_CheckExact(o)) { + if (PyList_CheckExact(o) || PyTuple_CheckExact(o) || + PyType_CheckExact(o, PyRange_Type) || PyType_CheckExact(o, PyMemoryView_Type)) { // Use exact type match for these checks. I in the event of inheritence we need to make sure // that it isn't a mapping too return 1; } - if (PyRange_Check(o) || PyMemoryView_Check(o)) { - // Exact check isn't possible so do exact check in another way - PyObject *mro = PyObject_GetAttrString((PyObject*)Py_TYPE(o), "__mro__"); - if (mro) { - Py_ssize_t len = PyObject_Length(mro); - Py_DECREF(mro); - if (len < 0) { - PyErr_Clear(); // doesn't really matter, just proceed with other checks - } else if (len == 2) { - return 1; // the type and "object" and no other bases - } - } else { - PyErr_Clear(); // doesn't really matter, just proceed with other checks - } - } return 0; } @@ -34,10 +20,13 @@ static CYTHON_INLINE int __Pyx_MatchCase_IsExactMapping(PyObject *o) { } static int __Pyx_MatchCase_IsExactNeitherSequenceNorMapping(PyObject *o) { - if (PyUnicode_Check(o) || PyBytes_Check(o) || PyByteArray_Check(o)) { + if (PyType_GetFlags(Py_TYPE(o)) & (Py_TPFLAGS_BYTES_SUBCLASS | Py_TPFLAGS_UNICODE_SUBCLASS)) || + PyByteArray_Check(o)) { return 1; // these types are deliberately excluded from the sequence test // even though they look like sequences for most other purposes. - // They're therefore "inexact" checks + // Leave them as inexact checks since they do pass + // "isinstance(o, collections.abc.Sequence)" so it's very hard to + // reason about their subclasses } if (o == Py_None || PyLong_CheckExact(o) || PyFloat_CheckExact(o)) { return 1; @@ -73,6 +62,16 @@ static int __Pyx_MatchCase_IsExactNeitherSequenceNorMapping(PyObject *o) { #define __PYX_SEQUENCE_MAPPING_ERROR (1U<<4) // only used by the ABCCheck function #endif +static int __Pyx_MatchCase_InitAndIsInstanceAbc(PyObject *o, PyObject *abc_module, + PyObject **abc_type, PyObject *name) { + assert(!abc_type); + abc_type = PyObject_GetAttr(abc_module, name); + if (!abc_type) { + return -1; + } + return PyObject_IsInstance(o, abc_type); +} + // the result is defined using the specification for sequence_mapping_temp // (detailed in "is_sequence") static unsigned int __Pyx_MatchCase_ABCCheck(PyObject *o, int sequence_first, int definitely_not_sequence, int definitely_not_mapping) { @@ -101,12 +100,7 @@ static unsigned int __Pyx_MatchCase_ABCCheck(PyObject *o, int sequence_first, in result = __PYX_DEFINITELY_SEQUENCE_FLAG; goto end; } - sequence_type = PyObject_GetAttr(abc_module, PYIDENT("Sequence")); - if (!sequence_type) { - result = __PYX_SEQUENCE_MAPPING_ERROR; - goto end; - } - sequence_result = PyObject_IsInstance(o, sequence_type); + sequence_result = __Pyx_MatchCase_InitAndIsInstanceAbc(o, abc_module, &sequence_type, PYIDENT("Sequence")); if (sequence_result < 0) { result = __PYX_SEQUENCE_MAPPING_ERROR; goto end; @@ -114,41 +108,32 @@ static unsigned int __Pyx_MatchCase_ABCCheck(PyObject *o, int sequence_first, in result |= __PYX_DEFINITELY_NOT_SEQUENCE_FLAG; goto end; } - // else wait to see what mapping is + // else wait to see what mapping is } if (!definitely_not_mapping) { - mapping_type = PyObject_GetAttr(abc_module, PYIDENT("Mapping")); - if (!mapping_type) { + mapping_result = __Pyx_MatchCase_InitAndIsInstanceAbc(o, abc_module, &mapping_type, PYIDENT("Mapping")); + if (mapping_result < 0) { + result = __PYX_SEQUENCE_MAPPING_ERROR; goto end; - } - mapping_result = PyObject_IsInstance(o, mapping_type); - } - if (mapping_result < 0) { - result = __PYX_SEQUENCE_MAPPING_ERROR; - goto end; - } else if (mapping_result == 0) { - result |= __PYX_DEFINITELY_NOT_MAPPING_FLAG; - if (sequence_first) { - assert(sequence_result); - result |= __PYX_DEFINITELY_SEQUENCE_FLAG; - } - goto end; - } else /* mapping_result == 1 */ { - if (sequence_first && !sequence_result) { - result |= __PYX_DEFINITELY_MAPPING_FLAG; + } else if (mapping_result == 0) { + result |= __PYX_DEFINITELY_NOT_MAPPING_FLAG; + if (sequence_first) { + assert(sequence_result); + result |= __PYX_DEFINITELY_SEQUENCE_FLAG; + } goto end; + } else /* mapping_result == 1 */ { + if (sequence_first && !sequence_result) { + result |= __PYX_DEFINITELY_MAPPING_FLAG; + goto end; + } } } if (!sequence_first) { // here we know mapping_result is true because we'd have returned otherwise assert(mapping_result); if (!definitely_not_sequence) { - sequence_type = PyObject_GetAttr(abc_module, PYIDENT("Sequence")); - if (!sequence_type) { - result = __PYX_SEQUENCE_MAPPING_ERROR; - goto end; - } - sequence_result = PyObject_IsInstance(o, sequence_type); + sequence_result = __Pyx_MatchCase_InitAndIsInstanceAbc(o, abc_module, &sequence_type, PYIDENT("Sequence")); } if (sequence_result < 0) { result = __PYX_SEQUENCE_MAPPING_ERROR; @@ -167,7 +152,7 @@ static unsigned int __Pyx_MatchCase_ABCCheck(PyObject *o, int sequence_first, in if (!mro) { PyErr_Clear(); goto end; - } + } if (!PyTuple_Check(mro)) { Py_DECREF(mro); goto end; @@ -322,7 +307,7 @@ static PyObject *__Pyx_MatchCase_OtherSequenceSliceToList(PyObject *x, Py_ssize_ PyObject *list; ssizeargfunc slot; PyTypeObject *type = Py_TYPE(x); - + list = PyList_New(total); if (!list) { return NULL; @@ -454,17 +439,15 @@ static int __Pyx_MatchCase_IsMapping(PyObject *o, unsigned int *sequence_mapping #endif } -//////////////////////// DuplicateKeyCheck.proto /////////////////////// +//////////////////////// MappingKeyCheck.proto ///////////////////////// -// Returns an new reference to any duplicate key. -// NULL can indicate no duplicate keys or an error (so use PyErr_Occurred) -static PyObject* __Pyx_MatchCase_CheckDuplicateKeys(PyObject *fixed_keys, PyObject *var_keys, Py_ssize_t n_var_keys); /*proto*/ +static int __Pyx_MatchCase_CheckMappingDuplicateKeys(PyObject *keys[], Py_ssize_t nFixedKeys, Py_ssize_t nKeys); -//////////////////////// DuplicateKeyCheck ///////////////////////////// +//////////////////////// MappingKeyCheck /////////////////////////////// -static PyObject* __Pyx_MatchCase_CheckDuplicateKeys(PyObject *fixed_keys, PyObject *var_keys, Py_ssize_t n_var_keys) { - // Inputs are tuples, and typically fairly small. It may be more efficient to - // loop over the tuple than create a set. +static int __Pyx_MatchCase_CheckMappingDuplicateKeys(PyObject *keys[], Py_ssize_t nFixedKeys, Py_ssize_t nKeys) { + // Inputs are arrays, and typically fairly small. It may be more efficient to + // loop over the array than create a set. // The CPython implementation (match_keys in ceval.c) does this concurrently with // taking the keys out of the dictionary. I'm choosing to do it separately since the @@ -472,71 +455,55 @@ static PyObject* __Pyx_MatchCase_CheckDuplicateKeys(PyObject *fixed_keys, PyObje // this step completely. PyObject *var_keys_set; - PyObject *key = NULL; + PyObject *key; Py_ssize_t n; int contains; var_keys_set = PySet_New(NULL); - if (!var_keys_set) return NULL; + if (!var_keys_set) return -1; - n_var_keys = (n_var_keys < 0) ? PyTuple_GET_SIZE(var_keys) : n_var_keys; - for (n=0; n < n_var_keys; ++n) { - key = PyTuple_GET_ITEM(var_keys, n); + for (n=nFixedKeys; n < nKeys; ++n) { + key = keys[n]; contains = PySet_Contains(var_keys_set, key); if (contains < 0) { - key = NULL; - goto end; + goto bad; } else if (contains == 1) { - Py_INCREF(key); - goto end; + goto raise_error; } else { if (PySet_Add(var_keys_set, key)) { - key = NULL; - goto end; + goto bad; } } } - for (n=0; n < PyTuple_GET_SIZE(fixed_keys); ++n) { - key = PyTuple_GET_ITEM(fixed_keys, n); + for (n=0; n < nFixedKeys; ++n) { + key = keys[n]; contains = PySet_Contains(var_keys_set, key); if (contains < 0) { - key = NULL; - goto end; + goto bad; } else if (contains == 1) { - Py_INCREF(key); - goto end; + goto raise_error; } } - key = NULL; - end: Py_DECREF(var_keys_set); - return key; -} - -//////////////////////// MappingKeyCheck.proto ///////////////////////// - -static int __Pyx_MatchCase_CheckMappingDuplicateKeys(PyObject *fixed_keys, PyObject *var_keys); /* proto */ - -//////////////////////// MappingKeyCheck /////////////////////////////// -//@requires: DuplicateKeyCheck - -static int __Pyx_MatchCase_CheckMappingDuplicateKeys(PyObject *fixed_keys, PyObject *var_keys) { - PyObject *key = __Pyx_MatchCase_CheckDuplicateKeys(fixed_keys, var_keys, -1); - if (key) { - PyErr_Format(PyExc_ValueError, "mapping pattern checks duplicate key (%R)", key); - Py_DECREF(key); - return -1; - } else if (PyErr_Occurred()) { - return -1; - } else { - return 0; - } + return 0; + + raise_error: + #if PY_MAJOR_VERSION > 2 + PyErr_Format(PyExc_ValueError, + "mapping pattern checks duplicate key (%R)", key); + #else + // DW really can't be bothered working around features that don't exist in + // Python 2, so just provide less information! + PyErr_SetString(PyExc_ValueError, + "mapping pattern checks duplicate key"); + #endif + bad: + Py_DECREF(var_keys_set); + return -1; } /////////////////////////// ExtractExactDict.proto //////////////// -#include <stdarg.h> - // the variadic arguments are a list of PyObject** to subjects to be filled. They may be NULL // in which case they're ignored. // @@ -544,48 +511,31 @@ static int __Pyx_MatchCase_CheckMappingDuplicateKeys(PyObject *fixed_keys, PyObj #if CYTHON_REFNANNY #define __Pyx_MatchCase_Mapping_ExtractDict(...) __Pyx__MatchCase_Mapping_ExtractDict(__pyx_refnanny, __VA_ARGS__) -#define __Pyx_MatchCase_Mapping_ExtractDictV(...) __Pyx__MatchCase_Mapping_ExtractDictV(__pyx_refnanny, __VA_ARGS__) #else #define __Pyx_MatchCase_Mapping_ExtractDict(...) __Pyx__MatchCase_Mapping_ExtractDict(NULL, __VA_ARGS__) -#define __Pyx_MatchCase_Mapping_ExtractDictV(...) __Pyx__MatchCase_Mapping_ExtractDictV(NULL, __VA_ARGS__) #endif -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractDict(void *__pyx_refnanny, PyObject *dict, PyObject *fixed_keys, PyObject *var_keys, ...); /* proto */ -static int __Pyx__MatchCase_Mapping_ExtractDictV(void *__pyx_refnanny, PyObject *dict, PyObject *fixed_keys, PyObject *var_keys, va_list subjects); /* proto */ +static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractDict(void *__pyx_refnanny, PyObject *dict, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]); /* proto */ /////////////////////////// ExtractExactDict //////////////// -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractDict(void *__pyx_refnanny, PyObject *dict, PyObject *fixed_keys, PyObject *var_keys, ...) { - int result; - va_list subjects; - - va_start(subjects, var_keys); - result = __Pyx_MatchCase_Mapping_ExtractDictV(dict, fixed_keys, var_keys, subjects); - va_end(subjects); - return result; -} +static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractDict(void *__pyx_refnanny, PyObject *dict, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]) { + Py_ssize_t i; -static int __Pyx__MatchCase_Mapping_ExtractDictV(void *__pyx_refnanny, PyObject *dict, PyObject *fixed_keys, PyObject *var_keys, va_list subjects) { - PyObject *keys[] = {fixed_keys, var_keys}; - Py_ssize_t i, j; - - for (i=0; i<2; ++i) { - PyObject *tuple = keys[i]; - for (j=0; j<PyTuple_GET_SIZE(tuple); ++j) { - PyObject *key = PyTuple_GET_ITEM(tuple, j); - PyObject **subject = va_arg(subjects, PyObject**); - if (!subject) { - int contains = PyDict_Contains(dict, key); - if (contains <= 0) { - return -1; // any subjects that were already set will be cleaned up externally - } - } else { - PyObject *value = __Pyx_PyDict_GetItemStrWithError(dict, key); - if (!value) { - return (PyErr_Occurred()) ? -1 : 0; // any subjects that were already set will be cleaned up externally - } - __Pyx_XDECREF_SET(*subject, value); - __Pyx_INCREF(*subject); // capture this incref with refnanny! + for (i=0; i<nKeys; ++i) { + PyObject *key = keys[i]; + PyObject **subject = subjects[i]; + if (!subject) { + int contains = PyDict_Contains(dict, key); + if (contains <= 0) { + return -1; // any subjects that were already set will be cleaned up externally + } + } else { + PyObject *value = __Pyx_PyDict_GetItemStrWithError(dict, key); + if (!value) { + return (PyErr_Occurred()) ? -1 : 0; // any subjects that were already set will be cleaned up externally } + __Pyx_XDECREF_SET(*subject, value); + __Pyx_INCREF(*subject); // capture this incref with refnanny! } } return 1; // success @@ -598,74 +548,55 @@ static int __Pyx__MatchCase_Mapping_ExtractDictV(void *__pyx_refnanny, PyObject // // This is a specialized version for the rarer case when the type isn't an exact dict. -#include <stdarg.h> - #if CYTHON_REFNANNY #define __Pyx_MatchCase_Mapping_ExtractNonDict(...) __Pyx__MatchCase_Mapping_ExtractNonDict(__pyx_refnanny, __VA_ARGS__) -#define __Pyx_MatchCase_Mapping_ExtractNonDictV(...) __Pyx__MatchCase_Mapping_ExtractNonDictV(__pyx_refnanny, __VA_ARGS__) #else #define __Pyx_MatchCase_Mapping_ExtractNonDict(...) __Pyx__MatchCase_Mapping_ExtractNonDict(NULL, __VA_ARGS__) -#define __Pyx_MatchCase_Mapping_ExtractNonDictV(...) __Pyx__MatchCase_Mapping_ExtractNonDictV(NULL, __VA_ARGS__) #endif -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractNonDict(void *__pyx_refnanny, PyObject *mapping, PyObject *fixed_keys, PyObject *var_keys, ...); /* proto */ -static int __Pyx__MatchCase_Mapping_ExtractNonDictV(void *__pyx_refnanny, PyObject *mapping, PyObject *fixed_keys, PyObject *var_keys, va_list subjects); /* proto */ +static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractNonDict(void *__pyx_refnanny, PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]); /* proto */ ///////////////////////// ExtractNonDict ////////////////////////////////////// //@requires: ObjectHandling.c::PyObjectCall2Args // largely adapted from match_keys in CPython ceval.c -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_ExtractNonDict(void *__pyx_refnanny, PyObject *map, PyObject *fixed_keys, PyObject *var_keys, ...) { - int result; - va_list subjects; - - va_start(subjects, var_keys); - result = __Pyx_MatchCase_Mapping_ExtractNonDictV(map, fixed_keys, var_keys, subjects); - va_end(subjects); - return result; -} - -static int __Pyx__MatchCase_Mapping_ExtractNonDictV(void *__pyx_refnanny, PyObject *map, PyObject *fixed_keys, PyObject *var_keys, va_list subjects) { +static int __Pyx__MatchCase_Mapping_ExtractNonDict(void *__pyx_refnanny, PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]) { PyObject *dummy=NULL, *get=NULL; - PyObject *keys[] = {fixed_keys, var_keys}; - Py_ssize_t i, j; + Py_ssize_t i; int result = 0; dummy = PyObject_CallObject((PyObject *)&PyBaseObject_Type, NULL); if (!dummy) { return -1; } - get = PyObject_GetAttrString(map, "get"); + get = PyObject_GetAttrString(mapping, "get"); if (!get) { result = -1; goto end; } - for (i=0; i<2; ++i) { - PyObject *tuple = keys[i]; - for (j=0; j<PyTuple_GET_SIZE(tuple); ++j) { - PyObject **subject; - PyObject *value = NULL; - PyObject *key = PyTuple_GET_ITEM(tuple, j); + for (i=0; i<nKeys; ++i) { + PyObject **subject; + PyObject *value = NULL; + PyObject *key = keys[i]; - // TODO - there's an optimization here (although it deviates from the strict definition of pattern matching). - // If we don't need the values then we can call PyObject_Contains instead of "get". If we don't need *any* - // of the values then we can skip initialization "get" and "dummy" - value = __Pyx_PyObject_Call2Args(get, key, dummy); - if (!value) { - result = -1; - goto end; - } else if (value == dummy) { - Py_DECREF(value); - goto end; // failed + // TODO - there's an optimization here (although it deviates from the strict definition of pattern matching). + // If we don't need the values then we can call PyObject_Contains instead of "get". If we don't need *any* + // of the values then we can skip initialization "get" and "dummy" + value = __Pyx_PyObject_Call2Args(get, key, dummy); + if (!value) { + result = -1; + goto end; + } else if (value == dummy) { + Py_DECREF(value); + goto end; // failed + } else { + subject = subjects[i]; + if (subject) { + __Pyx_XDECREF_SET(*subject, value); + __Pyx_GOTREF(*subject); } else { - subject = va_arg(subjects, PyObject**); - if (subject) { - __Pyx_XDECREF_SET(*subject, value); - __Pyx_GOTREF(*subject); - } else { - Py_DECREF(value); - } + Py_DECREF(value); } } } @@ -679,36 +610,28 @@ static int __Pyx__MatchCase_Mapping_ExtractNonDictV(void *__pyx_refnanny, PyObje ///////////////////////// ExtractGeneric.proto //////////////////////////////// -#include <stdarg.h> - #if CYTHON_REFNANNY #define __Pyx_MatchCase_Mapping_Extract(...) __Pyx__MatchCase_Mapping_Extract(__pyx_refnanny, __VA_ARGS__) #else #define __Pyx_MatchCase_Mapping_Extract(...) __Pyx__MatchCase_Mapping_Extract(NULL, __VA_ARGS__) #endif -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_Extract(void *__pyx_refnanny, PyObject *map, PyObject *fixed_keys, PyObject *var_keys, ...); /* proto */ +static CYTHON_INLINE int __Pyx__MatchCase_Mapping_Extract(void *__pyx_refnanny, PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]); /* proto */ ////////////////////// ExtractGeneric ////////////////////////////////////// //@requires: ExtractExactDict //@requires: ExtractNonDict -static CYTHON_INLINE int __Pyx__MatchCase_Mapping_Extract(void *__pyx_refnanny, PyObject *map, PyObject *fixed_keys, PyObject *var_keys, ...) { - va_list subjects; - int result; - - va_start(subjects, var_keys); - if (PyDict_CheckExact(map)) { - result = __Pyx_MatchCase_Mapping_ExtractDictV(map, fixed_keys, var_keys, subjects); +static CYTHON_INLINE int __Pyx__MatchCase_Mapping_Extract(void *__pyx_refnanny, PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys, PyObject **subjects[]) { + if (PyDict_CheckExact(mapping)) { + return __Pyx_MatchCase_Mapping_ExtractDict(mapping, keys, nKeys, subjects); } else { - result = __Pyx_MatchCase_Mapping_ExtractNonDictV(map, fixed_keys, var_keys, subjects); + return __Pyx_MatchCase_Mapping_ExtractNonDict(mapping, keys, nKeys, subjects); } - va_end(subjects); - return result; } ///////////////////////////// DoubleStarCapture.proto ////////////////////// -static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *map, PyObject *const_temps, PyObject *var_temps); /* proto */ +static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys); /* proto */ //////////////////////////// DoubleStarCapture ////////////////////////////// @@ -717,31 +640,30 @@ static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *map, PyObjec // https://github.com/python/cpython/blob/145bf269df3530176f6ebeab1324890ef7070bf8/Python/ceval.c#L3977 // (now removed in favour of building the same thing from a combination of opcodes) // The differences are: -// 1. We loop over separate tuples for constant and runtime keys -// 2. We add a shortcut for when there will be no left over keys (because I'm guess it's pretty common) +// 1. We use an array of keys rather than a tuple of keys +// 2. We add a shortcut for when there will be no left over keys (because I guess it's pretty common) // // Tempita variable 'tag' can be "NonDict", "ExactDict" or empty -static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *map, PyObject *const_temps, PyObject *var_temps) { +static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *mapping, PyObject *keys[], Py_ssize_t nKeys) { PyObject *dict_out; - PyObject *tuples[] = { const_temps, var_temps }; - Py_ssize_t i, j; + Py_ssize_t i; {{if tag != "NonDict"}} // shortcut for when there are no left-over keys - if ({{if tag=="ExactDict"}}(1){{else}}PyDict_CheckExact(map){{endif}}) { - Py_ssize_t s = PyDict_Size(map); + if ({{if tag=="ExactDict"}}(1){{else}}PyDict_CheckExact(mapping){{endif}}) { + Py_ssize_t s = PyDict_Size(mapping); if (s == -1) { return NULL; } - if (s == (PyTuple_GET_SIZE(const_temps) + PyTuple_GET_SIZE(var_temps))) { + if (s == nKeys) { return PyDict_New(); } } {{endif}} {{if tag=="ExactDict"}} - dict_out = PyDict_Copy(map); + dict_out = PyDict_Copy(mapping); {{else}} dict_out = PyDict_New(); {{endif}} @@ -749,19 +671,16 @@ static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *map, PyObjec return NULL; } {{if tag!="ExactDict"}} - if (PyDict_Update(dict_out, map)) { + if (PyDict_Update(dict_out, mapping)) { Py_DECREF(dict_out); return NULL; } {{endif}} - for (i=0; i<2; ++i) { - PyObject *keys = tuples[i]; - for (j=0; j<PyTuple_GET_SIZE(keys); ++j) { - if (PyDict_DelItem(dict_out, PyTuple_GET_ITEM(keys, j))) { - Py_DECREF(dict_out); - return NULL; - } + for (i=0; i<nKeys; ++i) { + if (PyDict_DelItem(dict_out, keys[i])) { + Py_DECREF(dict_out); + return NULL; } } return dict_out; @@ -769,30 +688,81 @@ static PyObject* __Pyx_MatchCase_DoubleStarCapture{{tag}}(PyObject *map, PyObjec ////////////////////////////// ClassPositionalPatterns.proto //////////////////////// -#include <stdarg.h> - #if CYTHON_REFNANNY #define __Pyx_MatchCase_ClassPositional(...) __Pyx__MatchCase_ClassPositional(__pyx_refnanny, __VA_ARGS__) #else #define __Pyx_MatchCase_ClassPositional(...) __Pyx__MatchCase_ClassPositional(NULL, __VA_ARGS__) #endif -static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subject, PyTypeObject *type, PyObject *keysnames_tuple, int match_self, int num_args, ...); /* proto */ +static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subject, PyTypeObject *type, PyObject *fixed_names[], Py_ssize_t n_fixed, int match_self, PyObject **subjects[], Py_ssize_t n_subjects); /* proto */ /////////////////////////////// ClassPositionalPatterns ////////////////////////////// -//@requires: DuplicateKeyCheck + +static int __Pyx_MatchCase_ClassCheckDuplicateAttrs(const char *tp_name, PyObject *fixed_names[], Py_ssize_t n_fixed, PyObject *match_args, Py_ssize_t num_args) { + // a lot of the basic logic of this is shared with __Pyx_MatchCase_CheckMappingDuplicateKeys + // but they take different input types so it isn't easy to actually share the code. + + // Inputs are tuples, and typically fairly small. It may be more efficient to + // loop over the tuple than create a set. + + PyObject *attrs_set; + PyObject *attr = NULL; + Py_ssize_t n; + int contains; + + attrs_set = PySet_New(NULL); + if (!attrs_set) return -1; + + num_args = PyTuple_GET_SIZE(match_args) < num_args ? PyTuple_GET_SIZE(match_args) : num_args; + for (n=0; n < num_args; ++n) { + attr = PyTuple_GET_ITEM(match_args, n); + contains = PySet_Contains(attrs_set, attr); + if (contains < 0) { + goto bad; + } else if (contains == 1) { + goto raise_error; + } else { + if (PySet_Add(attrs_set, attr)) { + goto bad; + } + } + } + for (n=0; n < n_fixed; ++n) { + attr = fixed_names[n]; + contains = PySet_Contains(attrs_set, attr); + if (contains < 0) { + goto bad; + } else if (contains == 1) { + goto raise_error; + } + } + Py_DECREF(attrs_set); + return 0; + + raise_error: + #if PY_MAJOR_VERSION > 2 + PyErr_Format(PyExc_TypeError, "%s() got multiple sub-patterns for attribute %R", + tp_name, attr); + #else + // DW has no interest in working around the lack of %R in Python 2.7 + PyErr_Format(PyExc_TypeError, "%s() got multiple sub-patterns for attribute", + tp_name); + #endif + bad: + Py_DECREF(attrs_set); + return -1; +} // Adapted from ceval.c "match_class" in CPython // // The argument match_self can equal 1 for "known to be true" // 0 for "known to be false" // -1 for "unknown", runtime test - -static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subject, PyTypeObject *type, PyObject *keysnames_tuple, int match_self, int num_args, ...) +// nargs is >= 0 otherwise this function will be skipped +static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subject, PyTypeObject *type, PyObject *fixed_names[], Py_ssize_t n_fixed, int match_self, PyObject **subjects[], Py_ssize_t n_subjects) { - PyObject *match_args, *dup_key; + PyObject *match_args; Py_ssize_t allowed, i; int result; - va_list subjects; match_args = PyObject_GetAttrString((PyObject*)type, "__match_args__"); if (!match_args) { @@ -805,19 +775,17 @@ static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subj _Py_TPFLAGS_MATCH_SELF); #else // probably an earlier version of Python. Go off the known list in the specification - match_self = (PyType_IsSubtype(type, &PyByteArray_Type) || - PyType_IsSubtype(type, &PyBytes_Type) || - PyType_IsSubtype(type, &PyDict_Type) || + match_self = ((PyType_GetFlags(type) & + // long should capture bool too + (Py_TPFLAGS_LONG_SUBCLASS | Py_TPFLAGS_LIST_SUBCLASS | Py_TPFLAGS_TUPLE_SUBCLASS | + Py_TPFLAGS_BYTES_SUBCLASS | Py_TPFLAGS_UNICODE_SUBCLASS | Py_TPFLAGS_DICT_SUBCLASS + #if PY_MAJOR_VERSION < 3 + | Py_TPFLAGS_IN_SUBCLASS + #endif + )) || + PyType_IsSubtype(type, &PyByteArray_Type) || PyType_IsSubtype(type, &PyFloat_Type) || PyType_IsSubtype(type, &PyFrozenSet_Type) || - PyType_IsSubtype(type, &PyLong_Type) || // This should capture bool too - #if PY_MAJOR_VERSION < 3 - PyType_IsSubtype(type, &PyInt_Type) || - #endif - PyType_IsSubtype(type, &PyList_Type) || - PyType_IsSubtype(type, &PySet_Type) || - PyType_IsSubtype(type, &PyUnicode_Type) || - PyType_IsSubtype(type, &PyTuple_Type) ); #endif } @@ -838,18 +806,17 @@ static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subj allowed = match_self ? 1 : (match_args ? PyTuple_GET_SIZE(match_args) : 0); - if (allowed < num_args) { + if (allowed < n_subjects) { const char *plural = (allowed == 1) ? "" : "s"; PyErr_Format(PyExc_TypeError, "%s() accepts %d positional sub-pattern%s (%d given)", type->tp_name, - allowed, plural, num_args); + allowed, plural, n_subjects); Py_XDECREF(match_args); return -1; } - va_start(subjects, num_args); if (match_self) { - PyObject **self_subject = va_arg(subjects, PyObject**); + PyObject **self_subject = subjects[0]; if (self_subject) { // Easy. Copy the subject itself, and move on to kwargs. __Pyx_XDECREF_SET(*self_subject, subject); @@ -858,20 +825,13 @@ static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subj result = 1; goto end_match_self; } - // next stage is to check for duplicate keys. Reuse code from mapping - dup_key = __Pyx_MatchCase_CheckDuplicateKeys(keysnames_tuple, match_args, num_args); - if (dup_key) { - PyErr_Format(PyExc_TypeError, "%s() got multiple sub-patterns for attribute %R", - type->tp_name, dup_key); - Py_DECREF(dup_key); - result = -1; - goto end; - } else if (PyErr_Occurred()) { + // next stage is to check for duplicate attributes. + if (__Pyx_MatchCase_ClassCheckDuplicateAttrs(type->tp_name, fixed_names, n_fixed, match_args, n_subjects)) { result = -1; goto end; } - for (i = 0; i < num_args; i++) { + for (i = 0; i < n_subjects; i++) { PyObject *attr; PyObject **subject_i; PyObject *name = PyTuple_GET_ITEM(match_args, i); @@ -889,7 +849,7 @@ static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subj result = 0; goto end; } - subject_i = va_arg(subjects, PyObject**); + subject_i = subjects[i]; if (subject_i) { __Pyx_XDECREF_SET(*subject_i, attr); __Pyx_GOTREF(attr); @@ -902,7 +862,6 @@ static int __Pyx__MatchCase_ClassPositional(void *__pyx_refnanny, PyObject *subj end: Py_DECREF(match_args); end_match_self: // because match_args isn't set - va_end(subjects); return result; } @@ -913,6 +872,13 @@ static PyTypeObject* __Pyx_MatchCase_IsType(PyObject* type); /* proto */ //////////////////////// MatchClassIsType ///////////////////////////// static PyTypeObject* __Pyx_MatchCase_IsType(PyObject* type) { + #if PY_MAJOR_VERSION < 3 + if (PyClass_Check(type)) { + // I don't really think it's worth the effort getting this to work! + PyErr_Format(PyExc_TypeError, "called match pattern must be a new-style class."); + return NULL; + } + #endif if (!PyType_Check(type)) { PyErr_Format(PyExc_TypeError, "called match pattern must be a type"); return NULL; |