From d16894f9bf9a2dfea1204469bf818a2ca7aeaa38 Mon Sep 17 00:00:00 2001 From: Boxiang Sun Date: Fri, 8 Jun 2018 07:34:47 +0800 Subject: Implement proper slice assignment for QByteArray Index assignment now only accept str/bytes, bytearray, QByteArray with size 1; Slice assignment only accept str/bytes, bytearray, QByteArray with limitation, that is if the step is not 1, then the number slots and the size of the target value must be equal. Range delete: a[2:5] = None Shrink: value length smaller than the slot length of the slice Expanse: value length bigger than the slot length of the slice Range assignment with step: a[2:5:1] = ... Range assignment with step which bigger than 1: a[2:9:2] = ... Range assignment with native step: a[5:2:-1] Change-Id: Ib9b929d09a691ed18c91e0c1c6b5dde827bf8d42 Reviewed-by: Qt CI Bot Reviewed-by: Christian Tismer --- .../PySide2/QtCore/glue/qbytearray_msetitem.cpp | 158 +++++++++++++++++++++ .../PySide2/QtCore/typesystem_core_common.xml | 6 + sources/pyside2/tests/QtCore/qbytearray_test.py | 125 ++++++++++------ 3 files changed, 249 insertions(+), 40 deletions(-) create mode 100644 sources/pyside2/PySide2/QtCore/glue/qbytearray_msetitem.cpp diff --git a/sources/pyside2/PySide2/QtCore/glue/qbytearray_msetitem.cpp b/sources/pyside2/PySide2/QtCore/glue/qbytearray_msetitem.cpp new file mode 100644 index 000000000..6745fc964 --- /dev/null +++ b/sources/pyside2/PySide2/QtCore/glue/qbytearray_msetitem.cpp @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt for Python. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +if (PyIndex_Check(_key)) { + Py_ssize_t _i = PyNumber_AsSsize_t(_key, PyExc_IndexError); + if (_i == -1 && PyErr_Occurred()) + return -1; + + if (_i < 0) + _i += %CPPSELF.count(); + + if (_i < 0 || _i >= %CPPSELF.size()) { + PyErr_SetString(PyExc_IndexError, "QByteArray index out of range"); + return -1; + } + + // Provide more specific error message for bytes/str, bytearray, QByteArray respectively +#ifdef IS_PY3K + if (PyBytes_Check(_value)) { + if (Py_SIZE(_value) != 1) { + PyErr_SetString(PyExc_ValueError, "bytes must be of size 1"); +#else + if (PyString_CheckExact(_value)) { + if (Py_SIZE(_value) != 1) { + PyErr_SetString(PyExc_ValueError, "str must be of size 1"); +#endif + return -1; + } + } else if (PyByteArray_Check(_value)) { + if (Py_SIZE(_value) != 1) { + PyErr_SetString(PyExc_ValueError, "bytearray must be of size 1"); + return -1; + } + } else if (PepType(Py_TYPE(_value)) == PepType(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX])) { + if (PyObject_Length(_value) != 1) { + PyErr_SetString(PyExc_ValueError, "QByteArray must be of size 1"); + return -1; + } + } else { +#ifdef IS_PY3K + PyErr_SetString(PyExc_ValueError, "a bytes, bytearray, QByteArray of size 1 is required"); +#else + PyErr_SetString(PyExc_ValueError, "a str, bytearray, QByteArray of size 1 is required"); +#endif + return -1; + } + + // Not support int or long. + %CPPSELF.remove(_i, 1); + PyObject *args = Py_BuildValue("(nO)", _i, _value); + PyObject *result = Sbk_QByteArrayFunc_insert(self, args); + Py_DECREF(args); + Py_XDECREF(result); + return !result ? -1 : 0; +} else if (PySlice_Check(_key)) { + Py_ssize_t start, stop, step, slicelength, value_length; + +#ifdef IS_PY3K + PyObject *key = _key; +#else + PySliceObject *key = reinterpret_cast(_key); +#endif + if (PySlice_GetIndicesEx(key, %CPPSELF.count(), &start, &stop, &step, &slicelength) < 0) { + return -1; + } + // The parameter candidates are: bytes/str, bytearray, QByteArray itself. + // Not support iterable which contains ints between 0~255 + + // case 1: value is NULL, means delete the items within the range + // case 2: step is 1, means shrink or expanse + // case 3: step is not 1, then the number of slots have to equal the number of items in _value + QByteArray ba; + if (_value == NULL || _value == Py_None) { + ba = QByteArray(); + value_length = 0; + } else if (!(PyBytes_Check(_value) || PyByteArray_Check(_value) || PepType(Py_TYPE(_value)) == PepType(SbkPySide2_QtCoreTypes[SBK_QBYTEARRAY_IDX]))) { + PyErr_Format(PyExc_TypeError, "bytes, bytearray or QByteArray is required, not %.200s", PepType(Py_TYPE(_value))->tp_name); + return -1; + } else { + value_length = PyObject_Length(_value); + } + + if (step != 1 && value_length != slicelength) { + PyErr_Format(PyExc_ValueError, "attempt to assign %s of size %d to extended slice of size %d",PepType(Py_TYPE(_value))->tp_name, value_length, slicelength); + return -1; + } + + if (step != 1) { + int i = start; + for (int j = 0; j < slicelength; j++) { + PyObject *item = PyObject_GetItem(_value, PyLong_FromLong(j)); + QByteArray temp; +#ifdef IS_PY3K + if (PyLong_Check(item)) { +#else + if (PyLong_Check(item) || PyInt_Check(item)) { +#endif + int overflow; + long ival = PyLong_AsLongAndOverflow(item, &overflow); + // Not suppose to bigger than 255 because only bytes, bytearray, QByteArray were accept + const char *el = reinterpret_cast(&ival); + temp = QByteArray(el); + } else { + temp = %CONVERTTOCPP[QByteArray](item); + } + + %CPPSELF.replace(i, 1, temp); + i += step; + } + return 0; + } else { + ba = %CONVERTTOCPP[QByteArray](_value); + %CPPSELF.replace(start, slicelength, ba); + return 0; + } +} else { + PyErr_Format(PyExc_TypeError, "QBytearray indices must be integers or slices, not %.200s", + PepType(Py_TYPE(_key))->tp_name); + return -1; +} + + diff --git a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml index d0806a19f..cc63c0b7a 100644 --- a/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml +++ b/sources/pyside2/PySide2/QtCore/typesystem_core_common.xml @@ -2530,6 +2530,9 @@ %out = %OUTTYPE(Shiboken::String::toCString(%in), Shiboken::String::len(%in)); #endif + + %out = %OUTTYPE(PyByteArray_AsString(%in), PyByteArray_Size(%in)); + %out = %OUTTYPE(Shiboken::String::toCString(%in), Shiboken::String::len(%in)); @@ -2925,6 +2928,9 @@ return !result ? -1 : 0; + + + diff --git a/sources/pyside2/tests/QtCore/qbytearray_test.py b/sources/pyside2/tests/QtCore/qbytearray_test.py index 3f7de66fb..dba9ecfea 100644 --- a/sources/pyside2/tests/QtCore/qbytearray_test.py +++ b/sources/pyside2/tests/QtCore/qbytearray_test.py @@ -108,46 +108,6 @@ class QByteArrayOperatorAtSetter(unittest.TestCase): obj[1] = py3k.b('0') self.assertEqual(obj, QByteArray(py3k.b('103456'))) - def testSetterStringLarge(self): - '''QByteArray[x] = pythonstring (larget than 1 char)''' - obj = QByteArray(py3k.b('123456')) - obj[3] = py3k.b('abba') - self.assertEqual(obj, QByteArray(py3k.b('123abba56'))) - - def testSetterQByteArray(self): - '''QByteArray[x] = qbytearray''' - obj = QByteArray(py3k.b('123456')) - obj[3] = QByteArray(py3k.b('array')) - self.assertEqual(obj, QByteArray(py3k.b('123array56'))) - - -class QByteArrayOperatorAtSetterNegativeIndex(unittest.TestCase): - '''Test case for QByteArray[] - __setitem__ - for negative index''' - - def testSetterNegativeIndex(self): - '''QByteArray[x] = string - negative index''' - obj = QByteArray(py3k.b('123456')) - obj[-3] = py3k.b('array') - self.assertEqual(obj, QByteArray(py3k.b('123array56'))) - - -class QByteArrayOperatorAtSetterLargeIndex(unittest.TestCase): - '''Test case for QByteArray[] - __setitem__ - for 'overflown' index''' - - def testSetterLargeIndexEmpty(self): - '''QByteArray[x] = somestring - Overflow index on empty string''' - # should pad with spaces if the index is larger - obj = QByteArray(py3k.b('')) - obj[2] = py3k.b('a') - self.assertEqual(obj, QByteArray(py3k.b(' a'))) - - def testSetterLargeIndexNormal(self): - '''QByteArray[x] = somestring - Overflow index on normal string''' - # should pad with spaces if the index is larger - obj = QByteArray(py3k.b('mystring')) - obj[10] = py3k.b('normal') - self.assertEqual(obj, QByteArray(py3k.b('mystring normal'))) - class QByteArrayOnQDataStream(unittest.TestCase): ''' Bug PYSIDE-232 @@ -221,5 +181,90 @@ class QByteArrayImplicitConvert(unittest.TestCase): self.assertRaises(TypeError, obj.setObjectName, ba) +class QByteArraySliceAssignment(unittest.TestCase): + def testIndexAssignment(self): + a = QByteArray(py3k.b('abc')) + a[0] = py3k.b('x') + self.assertEqual(a[0], py3k.b('x')) + + def test_1(): + a[0] = py3k.b('xy') + self.assertRaises(ValueError, test_1) + + def testSliceAssignmentBytes(self): + b = QByteArray(py3k.b('0123456789')) + b[2:8] = py3k.b('abcdef') + self.assertEqual(b[2:8], py3k.b('abcdef')) + # Delete behavior + b[2:8] = None + self.assertEqual(b, py3k.b('0189')) + + # number of slots and number of values doesn't match + def test_2(): + b[2:8:2] = py3k.b('') + self.assertRaises(ValueError, test_2) + b = QByteArray(py3k.b('0123456789')) + # reverse slice + b[5:2:-1] = py3k.b('ABC') + self.assertEqual(b, py3k.b('012CBA6789')) + # step is not 1 + b[2:9:3] = py3k.b('XYZ') + self.assertEqual(b, py3k.b('01XCBY67Z9')) + b = QByteArray(py3k.b('0123456789')) + b[9:2:-3] = py3k.b('XYZ') + self.assertEqual(b, py3k.b('012Z45Y78X')) + + def testSliceAssignmentQByteArray(self): + b = QByteArray(py3k.b('0123456789')) + b[2:8] = QByteArray(py3k.b('abcdef')) + self.assertEqual(b[2:8], py3k.b('abcdef')) + # shrink + b[2:8] = QByteArray(py3k.b('aaa')) + self.assertEqual(b, py3k.b('01aaa89')) + # expanse + b[2:5] = QByteArray(py3k.b('uvwxyz')) + self.assertEqual(b, py3k.b('01uvwxyz89')) + # Delete behavior + b[2:8] = QByteArray() + self.assertEqual(b, py3k.b('0189')) + + b = QByteArray(py3k.b('0123456789')) + # reverse assginment + b[5:2:-1] = QByteArray(py3k.b('ABC')) + self.assertEqual(b, py3k.b('012CBA6789')) + # step is not 1 + b[2:9:3] = QByteArray(py3k.b('XYZ')) + self.assertEqual(b, py3k.b('01XCBY67Z9')) + b = QByteArray(py3k.b('0123456789')) + b[9:2:-3] = QByteArray(py3k.b('XYZ')) + self.assertEqual(b, py3k.b('012Z45Y78X')) + + def testSliceAssignmentByteArray(self): + b = QByteArray(py3k.b('0123456789')) + # replace + b[2:8] = bytearray(py3k.b('abcdef')) + self.assertEqual(b[2:8], py3k.b('abcdef')) + # shrink + b[2:8] = bytearray(py3k.b('aaa')) + self.assertEqual(b, py3k.b('01aaa89')) + # expanse + b[2:5] = bytearray(py3k.b('uvwxyz')) + self.assertEqual(b, py3k.b('01uvwxyz89')) + # Delete behavior + b[2:8] = bytearray(py3k.b('')) + self.assertEqual(b, py3k.b('0189')) + + b = QByteArray(py3k.b('0123456789')) + # reverse assginment + b[5:2:-1] = bytearray(py3k.b('ABC')) + self.assertEqual(b, py3k.b('012CBA6789')) + # step is not 1 + b[2:9:3] = bytearray(py3k.b('XYZ')) + self.assertEqual(b, py3k.b('01XCBY67Z9')) + b = QByteArray(py3k.b('0123456789')) + b[9:2:-3] = bytearray(py3k.b('XYZ')) + self.assertEqual(b, py3k.b('012Z45Y78X')) + + if __name__ == '__main__': unittest.main() -- cgit v1.2.3