aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBoxiang Sun <daetalusun@gmail.com>2018-06-08 07:34:47 +0800
committerBoxiang Sun <daetalusun@gmail.com>2018-07-02 13:47:21 +0000
commitd16894f9bf9a2dfea1204469bf818a2ca7aeaa38 (patch)
tree52bb92ab05ac863eb05585cf4479be9236464b34
parentf70ecd23821c41b5c689cdb52d58af767ad45f97 (diff)
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 <qt_ci_bot@qt-project.org> Reviewed-by: Christian Tismer <tismer@stackless.com>
-rw-r--r--sources/pyside2/PySide2/QtCore/glue/qbytearray_msetitem.cpp158
-rw-r--r--sources/pyside2/PySide2/QtCore/typesystem_core_common.xml6
-rw-r--r--sources/pyside2/tests/QtCore/qbytearray_test.py125
3 files changed, 249 insertions, 40 deletions
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<PySliceObject *>(_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<const char*>(&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
</add-conversion>
+ <add-conversion type="PyByteArray">
+ %out = %OUTTYPE(PyByteArray_AsString(%in), PyByteArray_Size(%in));
+ </add-conversion>
<add-conversion type="PyString" check="Shiboken::String::check(%in) &amp;&amp; !PyUnicode_Check(%in)">
%out = %OUTTYPE(Shiboken::String::toCString(%in), Shiboken::String::len(%in));
</add-conversion>
@@ -2925,6 +2928,9 @@
return !result ? -1 : 0;
</inject-code>
</add-function>
+ <add-function signature="__msetitem__">
+ <inject-code class="target" position="beginning" file="glue/qbytearray_msetitem.cpp" />
+ </add-function>
</value-type>
<value-type name="QTextBoundaryFinder">
<enum-type name="BoundaryReason" flags="BoundaryReasons"/>
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()