summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--pyuno/Library_pyuno.mk1
-rw-r--r--pyuno/Module_pyuno.mk1
-rw-r--r--pyuno/PythonTest_pytests.mk1
-rw-r--r--pyuno/PythonTest_pyuno_pytests_testcollections.mk26
-rw-r--r--pyuno/inc/pyuno/pyuno.hxx6
-rw-r--r--pyuno/qa/pytests/testcollections_XCellRange.py365
-rw-r--r--pyuno/qa/pytests/testcollections_XEnumeration.py111
-rw-r--r--pyuno/qa/pytests/testcollections_XEnumerationAccess.py131
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexAccess.py299
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexContainer.py198
-rw-r--r--pyuno/qa/pytests/testcollections_XIndexReplace.py219
-rw-r--r--pyuno/qa/pytests/testcollections_XNameAccess.py182
-rw-r--r--pyuno/qa/pytests/testcollections_XNameContainer.py116
-rw-r--r--pyuno/qa/pytests/testcollections_XNameReplace.py73
-rw-r--r--pyuno/qa/pytests/testcollections_base.py58
-rw-r--r--pyuno/qa/pytests/testcollections_misc.py75
-rw-r--r--pyuno/qa/pytests/testcollections_mixednameindex.py48
-rw-r--r--pyuno/source/module/pyuno.cxx1039
-rw-r--r--pyuno/source/module/pyuno_impl.hxx49
-rw-r--r--pyuno/source/module/pyuno_iterator.cxx312
-rw-r--r--pyuno/source/module/pyuno_module.cxx24
-rw-r--r--pyuno/source/module/pyuno_runtime.cxx46
22 files changed, 3356 insertions, 24 deletions
diff --git a/pyuno/Library_pyuno.mk b/pyuno/Library_pyuno.mk
index 013765dc3c84..3bf08ccf3715 100644
--- a/pyuno/Library_pyuno.mk
+++ b/pyuno/Library_pyuno.mk
@@ -45,6 +45,7 @@ $(eval $(call gb_Library_add_exception_objects,pyuno,\
pyuno/source/module/pyuno_except \
pyuno/source/module/pyuno_adapter \
pyuno/source/module/pyuno_gc \
+ pyuno/source/module/pyuno_iterator \
))
# vim:set noet sw=4 ts=4:
diff --git a/pyuno/Module_pyuno.mk b/pyuno/Module_pyuno.mk
index ac09d980090c..4179a7318add 100644
--- a/pyuno/Module_pyuno.mk
+++ b/pyuno/Module_pyuno.mk
@@ -71,6 +71,7 @@ endif
ifneq (,$(filter PythonTest_pytests,$(MAKECMDGOALS)))
$(eval $(call gb_Module_add_targets,pyuno, \
PythonTest_pytests \
+ PythonTest_pyuno_pytests_testcollections \
PythonTest_pyuno_pytests_insertremovecells \
))
endif
diff --git a/pyuno/PythonTest_pytests.mk b/pyuno/PythonTest_pytests.mk
index 177195fb6d85..71255b638e5a 100644
--- a/pyuno/PythonTest_pytests.mk
+++ b/pyuno/PythonTest_pytests.mk
@@ -24,6 +24,7 @@
$(eval $(call gb_PythonTest_PythonTest,pytests))
$(call gb_PythonTest_get_target,pytests) : \
+ $(call gb_PythonTest_get_target,pyuno_pytests_testcollections) \
$(call gb_PythonTest_get_target,pyuno_pytests_insertremovecells) \
$(call gb_PythonTest_get_target,pyuno_pytests_ssl) \
diff --git a/pyuno/PythonTest_pyuno_pytests_testcollections.mk b/pyuno/PythonTest_pyuno_pytests_testcollections.mk
new file mode 100644
index 000000000000..ba8fe2e8cb7f
--- /dev/null
+++ b/pyuno/PythonTest_pyuno_pytests_testcollections.mk
@@ -0,0 +1,26 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+$(eval $(call gb_PythonTest_PythonTest,pyuno_pytests_testcollections))
+
+$(eval $(call gb_PythonTest_add_modules,pyuno_pytests_testcollections,$(SRCDIR)/pyuno/qa/pytests,\
+ testcollections_XIndexAccess \
+ testcollections_XIndexReplace \
+ testcollections_XIndexContainer \
+ testcollections_XNameAccess \
+ testcollections_XNameReplace \
+ testcollections_XNameContainer \
+ testcollections_XEnumerationAccess \
+ testcollections_XEnumeration \
+ testcollections_XCellRange \
+ testcollections_mixednameindex \
+ testcollections_misc \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/pyuno/inc/pyuno/pyuno.hxx b/pyuno/inc/pyuno/pyuno.hxx
index c83beb99957f..825fa93c455f 100644
--- a/pyuno/inc/pyuno/pyuno.hxx
+++ b/pyuno/inc/pyuno/pyuno.hxx
@@ -171,6 +171,12 @@ enum ConversionMode { ACCEPT_UNO_ANY, REJECT_UNO_ANY };
class LO_DLLPUBLIC_PYUNO Runtime
{
RuntimeImpl *impl;
+
+ /**
+ Safely unpacks a Python iterator into a sequence, then
+ stores it in an Any. Used internally by pyObject2Any
+ */
+ bool pyIterUnpack( PyObject *const, com::sun::star::uno::Any & ) const;
public:
~Runtime( );
diff --git a/pyuno/qa/pytests/testcollections_XCellRange.py b/pyuno/qa/pytests/testcollections_XCellRange.py
new file mode 100644
index 000000000000..ad8819b0b869
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XCellRange.py
@@ -0,0 +1,365 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+from com.sun.star.table import CellAddress
+
+
+# Tests behaviour of objects implementing XCellRange using the new-style
+# collection accessors
+
+class TestXCellRange(CollectionsTestBase):
+
+ # TODO negative indices
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Spreadsheet
+ # Cell at Row 0, Col 0
+ def test_XCellRange_Spreadsheet_Cell_00(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ cell = sht[0,0]
+
+ # Then
+ self.assertEqual(0, cell.CellAddress.Sheet)
+ self.assertEqual(0, cell.CellAddress.Row)
+ self.assertEqual(0, cell.CellAddress.Column)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Text table
+ # Cell at Row 0, Col 0
+ def test_XCellRange_Table_Cell_00(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+
+ # When
+ cell = tbl[0,0]
+
+ # Then
+ self.assertEqual('A1', cell.CellName)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Spreadsheet
+ # Cell at Row 3, Col 7
+ def test_XCellRange_Spreadsheet_Cell_37(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[3,7]
+
+ # Then
+ self.assertEqual(0, rng.CellAddress.Sheet)
+ self.assertEqual(3, rng.CellAddress.Row)
+ self.assertEqual(7, rng.CellAddress.Column)
+
+ # Tests syntax:
+ # cell = cellrange[0,0] # Access cell by indices
+ # For:
+ # Text table
+ # Cell at Row 3, Col 7
+ def test_XCellRange_Table_Cell_37(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+
+ # When
+ cell = tbl[3,7]
+
+ # Then
+ self.assertEqual('H4', cell.CellName)
+
+ # Tests syntax:
+ # rng = cellrange[0,1:2] # Access cell range by index,slice
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Index_Slice(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[0,1:3]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(1, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(2, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[0,1:2] # Access cell range by index,slice
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Index_Slice(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x,10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[0,1:3]
+
+ # Then
+ self.assertEqual((('101', '102'),), rng.DataArray)
+
+ # Tests syntax:
+ # rng = cellrange[1:2,0] # Access cell range by slice,index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Slice_Index(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[1:3,0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(2, rng.RangeAddress.EndRow)
+ self.assertEqual(0, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[1:2,0] # Access cell range by index,slice
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Slice_Index(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x,10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[1:3,0]
+
+ # Then
+ self.assertEqual((('110',), ('120',)), rng.DataArray)
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Slices(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[1:3,3:5]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(3, rng.RangeAddress.StartColumn)
+ self.assertEqual(2, rng.RangeAddress.EndRow)
+ self.assertEqual(4, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Spreadsheet
+ # Zero rows/columns
+ def test_XCellRange_Spreadsheet_Range_Slices_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ rng = sht[1:1,3:5]
+ with self.assertRaises(KeyError):
+ rng = sht[1:3,3:3]
+
+ # Tests syntax:
+ # rng = cellrange[0:1,2:3] # Access cell range by slices
+ # For:
+ # Text table
+ def test_XCellRange_Table_Range_Slices(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x,10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl[1:3,3:5]
+
+ # Then
+ self.assertEqual((('113', '114'), ('123', '124')), rng.DataArray)
+
+
+ # Tests syntax:
+ # rng = cellrange['A1:B2'] # Access cell range by descriptor
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Descriptor(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht['A3:B4']
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(2, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(3, rng.RangeAddress.EndRow)
+ self.assertEqual(1, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange['A1:B2'] # Access cell range by descriptor
+ # For:
+ # Table
+ def test_XCellRange_Table_Range_Descriptor(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance('com.sun.star.text.TextTable')
+ textTable.initialize(10,10)
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+ tbl = doc.TextTables.getByIndex(0)
+ doc.lockControllers()
+ tbl.DataArray = tuple(tuple(str(100 + y) for y in range(10*x,10*x + 10)) for x in range(10))
+ doc.unlockControllers()
+
+ # When
+ rng = tbl['A3:B4']
+
+ # Then
+ self.assertEqual((('120', '121'), ('130', '131')), rng.DataArray)
+
+ # Tests syntax:
+ # rng = cellrange['Name'] # Access cell range by name
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_Name(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+ expr = '$' + sht.Name + '.$C2:F10'
+ addr = CellAddress(Sheet=0,Row=1,Column=2)
+ sht.NamedRanges.addNewByName('foo', expr, addr, 0)
+
+ # When
+ rng = sht['foo']
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(1, rng.RangeAddress.StartRow)
+ self.assertEqual(2, rng.RangeAddress.StartColumn)
+ self.assertEqual(9, rng.RangeAddress.EndRow)
+ self.assertEqual(5, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[0] # Access cell range by row index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_RowIndex(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(1023, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[0,:] # Access cell range by row index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_RowIndex_FullSlice(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[0,:]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(0, rng.RangeAddress.EndRow)
+ self.assertEqual(1023, rng.RangeAddress.EndColumn)
+
+ # Tests syntax:
+ # rng = cellrange[:,0] # Access cell range by column index
+ # For:
+ # Spreadsheet
+ def test_XCellRange_Spreadsheet_Range_FullSlice_ColumnIndex(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ sht = spr.Sheets.getByIndex(0)
+
+ # When
+ rng = sht[:,0]
+
+ # Then
+ self.assertEqual(0, rng.RangeAddress.Sheet)
+ self.assertEqual(0, rng.RangeAddress.StartRow)
+ self.assertEqual(0, rng.RangeAddress.StartColumn)
+ self.assertEqual(1048575, rng.RangeAddress.EndRow)
+ self.assertEqual(0, rng.RangeAddress.EndColumn)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XEnumeration.py b/pyuno/qa/pytests/testcollections_XEnumeration.py
new file mode 100644
index 000000000000..9be1a60b9985
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XEnumeration.py
@@ -0,0 +1,111 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XEnumeration using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXEnumeration(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in itr: ... # Iteration of named iterator
+ # For:
+ # 1 element
+ def test_XEnumeration_ForIn(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraphs = []
+ itr = iter(doc.Text.createEnumeration())
+ for para in itr:
+ paragraphs.append(para)
+
+ # Then
+ self.assertEqual(1, len(paragraphs))
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Present value
+ def test_XEnumeration_IfIn_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc.Text.createEnumeration().nextElement()
+ itr = iter(doc.Text.createEnumeration())
+ result = paragraph in itr
+
+ # Then
+ self.assertTrue(result)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Absent value
+ def test_XEnumeration_IfIn_Absent(self):
+ # Given
+ doc1 = self.createBlankTextDocument()
+ doc2 = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc2.Text.createEnumeration().nextElement()
+ itr = iter(doc1.Text.createEnumeration())
+ result = paragraph in itr
+
+ # Then
+ self.assertFalse(result)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # None
+ def test_XEnumeration_IfIn_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text.createEnumeration())
+ result = None in itr
+
+ # Then
+ self.assertFalse(result)
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Invalid value (string)
+ # Note: Ideally this would raise TypeError in the same manner as for
+ # XEnumerationAccess, but an XEnumeration doesn't know the type of its
+ # values
+ def test_XEnumeration_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text.createEnumeration())
+ result = 'foo' in itr
+
+ # Then
+ self.assertFalse(result)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XEnumerationAccess.py b/pyuno/qa/pytests/testcollections_XEnumerationAccess.py
new file mode 100644
index 000000000000..1ad7a08f31fa
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XEnumerationAccess.py
@@ -0,0 +1,131 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XEnumerationAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXEnumerationAccess(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator
+ # For:
+ # 1 element
+ def test_XEnumerationAccess_ForIn(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraphs = []
+ for para in doc.Text:
+ paragraphs.append(para)
+
+ # Then
+ self.assertEqual(1, len(paragraphs))
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator
+ # For:
+ # 1 element
+ def test_XEnumerationAccess_Iter(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ itr = iter(doc.Text)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Present value
+ def test_XEnumerationAccess_IfIn_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc.Text.createEnumeration().nextElement()
+ result = paragraph in doc.Text
+
+ # Then
+ self.assertTrue(result)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent value
+ def test_XEnumerationAccess_IfIn_Absent(self):
+ # Given
+ doc1 = self.createBlankTextDocument()
+ doc2 = self.createBlankTextDocument()
+
+ # When
+ paragraph = doc2.Text.createEnumeration().nextElement()
+ result = paragraph in doc1.Text
+
+ # Then
+ self.assertFalse(result)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # None
+ def test_XEnumerationAccess_IfIn_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ result = None in doc.Text
+
+ # Then
+ self.assertFalse(result)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Invalid value (string)
+ def test_XEnumerationAccess_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ result = 'foo' in doc.Text
+
+ # Then
+ self.assertFalse(result)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Invalid value (dict)
+ def test_XEnumerationAccess_IfIn_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ result = {} in doc.Text
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XIndexAccess.py b/pyuno/qa/pytests/testcollections_XIndexAccess.py
new file mode 100644
index 000000000000..753373b6b933
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexAccess.py
@@ -0,0 +1,299 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from inspect import isclass
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XIndexAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexAccess(CollectionsTestBase):
+
+ def insertTestFootnotes(self, doc, count):
+ cursor = doc.Text.createTextCursor()
+ for i in range(count):
+ footnote = doc.createInstance("com.sun.star.text.Footnote")
+ footnote.Label = 'n'+str(i)
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ def readValuesTestFixture(self, doc, count, key, expected):
+ # Given
+ doc.Text.setString('')
+ self.insertTestFootnotes(doc, count)
+
+ # When
+ captured = None
+ try:
+ actual = doc.Footnotes[key]
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isclass(expected) and issubclass(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(expected.__name__, type(captured).__name__)
+ elif type(expected) is tuple:
+ # expected is tuple
+ self.assertEqual(None, captured)
+ self.assertTrue(type(actual) is tuple)
+ self.assertEqual(len(expected), len(actual))
+ for i in range(len(expected)):
+ self.assertEqual('n'+str(expected[i]), actual[i].Label)
+ else:
+ # expected is value
+ self.assertEqual(None, captured)
+ self.assertTrue(type(actual) is not tuple)
+ self.assertEqual('n'+str(expected), actual.Label)
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # length = 0
+ def test_XIndexAccess_Len_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ count = len(doc.Footnotes)
+
+ # Then
+ self.assertEqual(0, count)
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # length = 1
+ def test_XIndexAccess_Len_1(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = doc.createInstance("com.sun.star.text.Footnote")
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ count = len(doc.Footnotes)
+
+ # Then
+ self.assertEqual(1, count)
+
+ # Tests syntax:
+ # val = obj[0] # Access by index
+ # For:
+ # Single indices
+ def test_XIndexAccess_ReadIndex_Single(self):
+ doc = self.createBlankTextDocument()
+ self.readValuesTestFixture(doc, 0, -1, IndexError)
+ self.readValuesTestFixture(doc, 0, 0, IndexError)
+ self.readValuesTestFixture(doc, 0, 1, IndexError)
+ self.readValuesTestFixture(doc, 1, -3, IndexError)
+ self.readValuesTestFixture(doc, 1, -2, IndexError)
+ self.readValuesTestFixture(doc, 1, -1, 0)
+ self.readValuesTestFixture(doc, 1, 0, 0)
+ self.readValuesTestFixture(doc, 1, 1, IndexError)
+ self.readValuesTestFixture(doc, 1, 2, IndexError)
+ self.readValuesTestFixture(doc, 2, -4, IndexError)
+ self.readValuesTestFixture(doc, 2, -3, IndexError)
+ self.readValuesTestFixture(doc, 2, -2, 0)
+ self.readValuesTestFixture(doc, 2, -1, 1)
+ self.readValuesTestFixture(doc, 2, 0, 0)
+ self.readValuesTestFixture(doc, 2, 1, 1)
+ self.readValuesTestFixture(doc, 2, 2, IndexError)
+ self.readValuesTestFixture(doc, 2, 3, IndexError)
+
+ def test_XIndexAccess_ReadIndex_Single_Invalid(self):
+ doc = self.createBlankTextDocument()
+ self.readValuesTestFixture(doc, 0, None, TypeError)
+ self.readValuesTestFixture(doc, 0, 'foo', TypeError)
+ self.readValuesTestFixture(doc, 0, 12.34, TypeError)
+ self.readValuesTestFixture(doc, 0, (0,1), TypeError)
+ self.readValuesTestFixture(doc, 0, [0,1], TypeError)
+ self.readValuesTestFixture(doc, 0, {'a': 'b'}, TypeError)
+
+ # Tests syntax:
+ # val1,val2 = obj[2:4] # Access by slice
+ def test_XIndexAccess_ReadSlice(self):
+ doc = self.createBlankTextDocument()
+ testMax = 4
+ for i in range(testMax):
+ t = tuple(range(i))
+ for j in [x for x in range(-testMax-2,testMax+3)] + [None]:
+ for k in [x for x in range(-testMax-2,testMax+3)] + [None]:
+ key = slice(j,k)
+ expected = t[key]
+ self.readValuesTestFixture(doc, i, key, expected)
+
+ # Tests syntax:
+ # val1,val2 = obj[0:3:2] # Access by extended slice
+ def test_XIndexAccess_ReadExtendedSlice(self):
+ doc = self.createBlankTextDocument()
+ testMax = 4
+ for i in range(testMax):
+ t = tuple(range(i))
+ for j in [x for x in range(-testMax-2,testMax+3)] + [None]:
+ for k in [x for x in range(-testMax-2,testMax+3)] + [None]:
+ for l in [-2,-1,2]:
+ key = slice(j,k,l)
+ expected = t[key]
+ self.readValuesTestFixture(doc, i, key, expected)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Present element
+ def test_XIndexAccess_In_Present(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = doc.createInstance("com.sun.star.text.Footnote")
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+ footnote = doc.Footnotes.getByIndex(0);
+
+ # When
+ present = footnote in doc.Footnotes
+
+ # Then
+ self.assertTrue(present)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # None
+ def test_XIndexAccess_In_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ present = None in doc.Footnotes
+
+ # Then
+ self.assertFalse(present)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent element (string)
+ def test_XIndexAccess_In_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ present = "foo" in doc.Footnotes
+
+ # Then
+ self.assertFalse(present)
+
+ # Tests syntax:
+ # if val in obj: ... # Test value presence
+ # For:
+ # Absent element (dict)
+ def test_XIndexAccess_In_Dict(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ present = {} in doc.Footnotes
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 0 elements
+ def test_XIndexAccess_ForIn_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When
+ readFootnotes = []
+ for f in doc.Footnotes:
+ readFootnotes.append(f)
+
+ # Then
+ self.assertEqual(0, len(readFootnotes))
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 1 element
+ def test_XIndexAccess_ForIn_1(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = doc.createInstance("com.sun.star.text.Footnote")
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ readFootnotes = []
+ for f in doc.Footnotes:
+ readFootnotes.append(f)
+
+ # Then
+ self.assertEqual(1, len(readFootnotes))
+ self.assertEqual('foo', readFootnotes[0].Label)
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator (values)
+ # For:
+ # 2 elements
+ def test_XIndexAccess_ForIn_2(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote1 = doc.createInstance("com.sun.star.text.Footnote")
+ footnote2 = doc.createInstance("com.sun.star.text.Footnote")
+ footnote1.setLabel('foo')
+ footnote2.setLabel('bar')
+ doc.Text.insertTextContent(cursor, footnote1, 0)
+ doc.Text.insertTextContent(cursor, footnote2, 0)
+
+ # When
+ readFootnotes = []
+ for f in doc.Footnotes:
+ readFootnotes.append(f)
+
+ # Then
+ self.assertEqual(2, len(readFootnotes))
+ self.assertEqual('foo', readFootnotes[0].Label)
+ self.assertEqual('bar', readFootnotes[1].Label)
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator (values)
+ # For:
+ # 1 element
+ def test_XIndexAccess_Iter_0(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ cursor = doc.Text.createTextCursor()
+ footnote = doc.createInstance("com.sun.star.text.Footnote")
+ footnote.setLabel('foo')
+ doc.Text.insertTextContent(cursor, footnote, 0)
+
+ # When
+ itr = iter(doc.Footnotes)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XIndexContainer.py b/pyuno/qa/pytests/testcollections_XIndexContainer.py
new file mode 100644
index 000000000000..aad9b848b841
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexContainer.py
@@ -0,0 +1,198 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XIndexContainer using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexContainer(CollectionsTestBase):
+
+ def generateTestPropertyValues(self, count):
+ sm = self.context.ServiceManager
+ values = sm.createInstanceWithContext("com.sun.star.document.IndexedPropertyValues", self.context)
+ for i in range(count):
+ properties = (PropertyValue(Name='n'+str(i), Value='v'+str(i)),)
+ uno.invoke (values, "insertByIndex", (i, uno.Any("[]com.sun.star.beans.PropertyValue", properties)))
+ return values
+
+ def generateTestTuple(self, values):
+ properties = []
+ for i in values:
+ properties.append((PropertyValue(Name='n'+str(i), Value='v'+str(i)),))
+ return tuple(properties)
+
+ def assignValuesTestFixture(self, count, key, values, expected):
+ # Given
+ propertyValues = self.generateTestPropertyValues(count)
+ if type(values) is list:
+ toAssign = self.generateTestTuple(values)
+ else:
+ toAssign = values
+ if not (isinstance(expected, Exception)):
+ toCompare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ propertyValues[key] = toAssign
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ self.assertEqual(len(expected), propertyValues.getCount())
+ for i in range(propertyValues.getCount()):
+ self.assertEqual(toCompare[i][0].Name, propertyValues.getByIndex(i)[0].Name)
+
+ def deleteValuesTestFixture(self, count, key, expected):
+ # Given
+ propertyValues = self.generateTestPropertyValues(count)
+ if not (isinstance(expected, Exception)):
+ toCompare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ del propertyValues[key]
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ self.assertEqual(len(expected), propertyValues.getCount())
+ for i in range(propertyValues.getCount()):
+ self.assertEqual(toCompare[i][0].Name, propertyValues.getByIndex(i)[0].Name)
+
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # obj[2:3] = val1,val2 # Insert/replace by slice
+ # obj[2:2] = (val,) # Insert by slice
+ # obj[2:4] = (val,) # Replace/delete by slice
+ # obj[2:3] = () # Delete by slice (implicit)
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexContainer_AssignSlice(self):
+ baseMax = 5
+ assignMax = 5
+ for i in range(baseMax):
+ for j in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ for k in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ key = slice(j,k)
+ for l in range(assignMax):
+ assign = [y+100 for y in range(l)]
+ expected = list(range(i))
+ expected[key] = assign
+ self.assignValuesTestFixture(i, key, assign, expected)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # obj[2:3] = val1,val2 # Insert/replace by slice
+ # obj[2:2] = (val,) # Insert by slice
+ # For:
+ # Cases not requiring sequence type coercion
+ # Invalid values
+ def test_XIndexContainer_AssignSlice_Invalid(self):
+ self.assignValuesTestFixture(2, slice(0,2), None, TypeError())
+ self.assignValuesTestFixture(2, slice(0,2), 'foo', TypeError())
+ self.assignValuesTestFixture(2, slice(0,2), 12.34, TypeError())
+ self.assignValuesTestFixture(2, slice(0,2), {'a':'b'}, TypeError())
+ self.assignValuesTestFixture(2, slice(0,2), ('foo',), TypeError())
+ self.assignValuesTestFixture(2, slice(0,2), ('foo','foo'), TypeError())
+
+ # Tests syntax:
+ # obj[2:2] = (val,) # Insert by slice
+ # For:
+ # Cases not requiring sequence type coercion
+ def test_XIndexContainer_AssignSlice_NoCoercion(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ form = doc.createInstance("com.sun.star.form.component.DataForm")
+ form.Name = 'foo'
+
+ # When
+ doc.DrawPage.Forms[0:0] = (form,)
+
+ # Then
+ self.assertEqual('foo', doc.DrawPage.Forms[0].Name)
+
+ # Tests syntax:
+ # obj[0:3:2] = val1,val2 # Replace by extended slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexContainer_AssignExtendedSlice(self):
+ baseMax = 5
+ assignMax = 5
+ for i in range(baseMax):
+ for j in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ for k in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ for l in [-2,-1,1,2]:
+ key = slice(j,k,l)
+ for m in range(assignMax):
+ assign = [y+100 for y in range(m)]
+ expected = list(range(i))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+ self.assignValuesTestFixture(i, key, assign, expected)
+
+ # Tests syntax:
+ # del obj[0] # Delete by index
+ def test_XIndexContainer_DelIndex(self):
+ baseMax = 5
+ for i in range(baseMax):
+ for j in [x for x in range(-baseMax-2,baseMax+3)]:
+ expected = list(range(i))
+ try:
+ del expected[j]
+ except Exception as e:
+ expected = e
+ self.deleteValuesTestFixture(i, j, expected)
+
+ # Tests syntax:
+ # del obj[2:4] # Delete by slice
+ def test_XIndexContainer_DelSlice(self):
+ baseMax = 5
+ for i in range(baseMax):
+ for j in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ for k in [x for x in range(-baseMax-2,baseMax+3)] + [None]:
+ key = slice(j,k)
+ expected = list(range(i))
+ try:
+ del expected[key]
+ except Exception as e:
+ expected = e
+ self.deleteValuesTestFixture(i, key, expected)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XIndexReplace.py b/pyuno/qa/pytests/testcollections_XIndexReplace.py
new file mode 100644
index 000000000000..b8e0da59326e
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XIndexReplace.py
@@ -0,0 +1,219 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XIndexReplace using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXIndexReplace(CollectionsTestBase):
+
+ def generateTestContentIndex(self, doc):
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+ for i in range(10):
+ styles = ('n'+str(i),)
+ uno.invoke (index.LevelParagraphStyles, "replaceByIndex", (i, uno.Any("[]string", styles)))
+ return index
+
+ def generateTestTuple(self, values):
+ properties = []
+ for i in values:
+ properties.append(('n'+str(i),),)
+ return tuple(properties)
+
+ def assignValuesTestFixture(self, doc, key, values, expected):
+ # Given
+ index = self.generateTestContentIndex(doc)
+ toAssign = self.generateTestTuple(values)
+ if not (isinstance(expected, Exception)):
+ toCompare = self.generateTestTuple(expected)
+
+ # When
+ captured = None
+ try:
+ index.LevelParagraphStyles[key] = toAssign
+ except Exception as e:
+ captured = e
+
+ # Then
+ if isinstance(expected, Exception):
+ # expected is exception
+ self.assertNotEqual(None, captured)
+ self.assertEqual(type(expected).__name__, type(captured).__name__)
+ else:
+ # expected is list
+ self.assertEqual(None, captured)
+ for i in range(10):
+ self.assertEqual(toCompare[i][0], index.LevelParagraphStyles.getByIndex(i)[0])
+
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceIndex(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When
+ index.LevelParagraphStyles[0] = ('Caption',)
+
+ # Then
+ self.assertEqual(('Caption',), index.LevelParagraphStyles[0])
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (None)
+ def test_XIndexReplace_ReplaceIndex_Invalid_None(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = None
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (String)
+ def test_XIndexReplace_ReplaceIndex_Invalid_String(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = 'foo'
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (Float)
+ def test_XIndexReplace_ReplaceIndex_Invalid_Float(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = 12.34
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (List)
+ def test_XIndexReplace_ReplaceIndex_Invalid_List(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = [0,1]
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (Dict)
+ def test_XIndexReplace_ReplaceIndex_Invalid_Dict(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = {'a': 'b'}
+
+ # Tests syntax:
+ # obj[0] = val # Replace by index
+ # For:
+ # Invalid value (inconsistently typed tuple)
+ def test_XIndexReplace_ReplaceIndex_Invalid_InconsistentTuple(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0] = ('Caption', ())
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceSlice(self):
+ assignMax = 12
+ doc = self.createBlankTextDocument()
+ t = tuple(range(10))
+ for j in [x for x in range(-12,13)] + [None]:
+ for k in [x for x in range(-12,13)] + [None]:
+ key = slice(j,k)
+ for l in range(assignMax):
+ assign = [y+100 for y in range(l)]
+ expected = list(range(10))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+ if (len(expected) != 10):
+ expected = ValueError()
+ self.assignValuesTestFixture(doc, key, assign, expected)
+
+ # Tests syntax:
+ # obj[2:4] = val1,val2 # Replace by slice
+ # For:
+ # Invalid values (inconsistently value types in tuple)
+ def test_XIndexReplace_ReplaceSlice_Invalid_InconsistentTuple(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ index = doc.createInstance("com.sun.star.text.ContentIndex");
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ index.LevelParagraphStyles[0:2] = (
+ ('Caption',),
+ 12.34
+ )
+
+ # Tests syntax:
+ # obj[0:3:2] = val1,val2 # Replace by extended slice
+ # For:
+ # Cases requiring sequence type coercion
+ def test_XIndexReplace_ReplaceExtendedSlice(self):
+ assignMax = 12
+ doc = self.createBlankTextDocument()
+ t = tuple(range(10))
+ for j in [x for x in range(-12,13)] + [None]:
+ for k in [x for x in range(-12,13)] + [None]:
+ for l in [-2,-1,2]:
+ key = slice(j,k,l)
+ for m in range(assignMax):
+ assign = [y+100 for y in range(m)]
+ expected = list(range(10))
+ try:
+ expected[key] = assign
+ except Exception as e:
+ expected = e
+ self.assignValuesTestFixture(doc, key, assign, expected)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XNameAccess.py b/pyuno/qa/pytests/testcollections_XNameAccess.py
new file mode 100644
index 000000000000..f123641fc324
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameAccess.py
@@ -0,0 +1,182 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XNameAccess using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameAccess(CollectionsTestBase):
+
+ # Tests syntax:
+ # num = len(obj) # Number of keys
+ # For:
+ # 2 elements
+ def test_XNameAccess_Len(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When
+ length = len (drw.Links)
+
+ # Then
+ self.assertEqual(2, length)
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # 1/2 elements
+ def test_XNameAccess_ReadKey(self):
+ # Given
+ drw = self.createBlankDrawing()
+ drw.DrawPages.getByIndex(0).Name = 'foo'
+
+ # When
+ link = drw.Links['foo']
+
+ # Then
+ self.assertEqual('foo', link.getName())
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Missing key
+ def test_XNameAccess_ReadKey_Missing(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ link = drw.Links['foo']
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (None)
+ def test_XNameAccess_ReadKey_Invalid_None(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[None]
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (float)
+ def test_XNameAccess_ReadKey_Invalid_Float(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[12.34]
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (tuple)
+ def test_XNameAccess_ReadKey_Invalid_Tuple(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[(1,2)]
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (list)
+ def test_XNameAccess_ReadKey_Invalid_List(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[[1,2]]
+
+ # Tests syntax:
+ # val = obj[key] # Access by key
+ # For:
+ # Invalid key type (dict)
+ def test_XNameAccess_ReadKey_Invalid_Dict(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ link = drw.Links[{'a':'b'}]
+
+ # Tests syntax:
+ # if key in obj: ... # Test key presence
+ # For:
+ # 1/2 elements
+ def test_XNameAccess_In(self):
+ # Given
+ drw = self.createBlankDrawing()
+ drw.DrawPages.getByIndex(0).Name = 'foo'
+
+ # When
+ present = 'foo' in drw.Links
+
+ # Then
+ self.assertTrue(present)
+
+
+ # Tests syntax:
+ # for key in obj: ... # Implicit iterator (keys)
+ # For:
+ # 2 elements
+ def test_XNameAccess_ForIn(self):
+ # Given
+ drw = self.createBlankDrawing()
+ i = 0
+ for name in drw.Links.getElementNames():
+ drw.Links.getByName(name).Name = 'foo' + str(i)
+ i += 1
+
+ # When
+ readLinks = []
+ for link in drw.Links:
+ readLinks.append(link)
+
+ # Then
+ self.assertEqual(['foo0','foo1'], readLinks)
+
+ # Tests syntax:
+ # itr = iter(obj) # Named iterator (keys)
+ # For:
+ # 2 elements
+ def test_XNameAccess_Iter(self):
+ # Given
+ drw = self.createBlankDrawing()
+
+ # When
+ itr = iter(drw.Links)
+
+ # Then
+ self.assertIsNotNone(next(itr))
+ self.assertIsNotNone(next(itr))
+ with self.assertRaises(StopIteration):
+ next(itr)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XNameContainer.py b/pyuno/qa/pytests/testcollections_XNameContainer.py
new file mode 100644
index 000000000000..80b60465882d
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameContainer.py
@@ -0,0 +1,116 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XNameContainer using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameContainer(CollectionsTestBase):
+
+ # Tests syntax:
+ # obj[key] = val # Insert by key
+ # For:
+ # 0->1 element
+ def test_XNameContainer_InsertName(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = spr.createInstance("com.sun.star.sheet.SheetCellRanges")
+ newRange = spr.Sheets.getByIndex(0).getCellRangeByPosition( 1, 2, 1, 2 )
+
+ # When
+ ranges['foo'] = newRange
+
+ # Then
+ self.assertEqual(1, len(ranges.ElementNames))
+
+ # Tests syntax:
+ # obj[key] = val # Insert by key
+ # For:
+ # Invalid key
+ def test_XNameContainer_InsertName_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = spr.createInstance("com.sun.star.sheet.SheetCellRanges")
+ newRange = spr.Sheets.getByIndex(0).getCellRangeByPosition( 1, 2, 1, 2 )
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ ranges[12.34] = newRange
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ def test_XNameContainer_ReplaceName(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ ranges = spr.createInstance("com.sun.star.sheet.SheetCellRanges")
+ newRange1 = spr.Sheets.getByIndex(0).getCellRangeByPosition( 1, 2, 1, 2 )
+ newRange2 = spr.Sheets.getByIndex(0).getCellRangeByPosition( 6, 6, 6, 6 )
+
+ # When
+ ranges['foo'] = newRange1
+ ranges['foo'] = newRange2
+
+ # Then
+ self.assertEqual(1, len(ranges.ElementNames))
+ readRange = ranges['foo']
+ self.assertEqual(6, readRange.CellAddress.Column)
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # 1/2 elements
+ def test_XNameContainer_DelKey(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+ spr.Sheets.insertNewByName('foo', 1)
+
+ # When
+ del spr.Sheets['foo']
+
+ # Then
+ self.assertEqual(1, len(spr.Sheets))
+ self.assertFalse('foo' in spr.Sheets)
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # Missing key
+ def test_XNameContainer_DelKey_Missing(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ del spr.Sheets['foo']
+
+ # Tests syntax:
+ # del obj[key] # Delete by key
+ # For:
+ # Invalid key (float)
+ def test_XNameContainer_DelKey_Invalid(self):
+ # Given
+ spr = self.createBlankSpreadsheet()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ del spr.Sheets[12.34]
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_XNameReplace.py b/pyuno/qa/pytests/testcollections_XNameReplace.py
new file mode 100644
index 000000000000..622311433c41
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_XNameReplace.py
@@ -0,0 +1,73 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing XNameReplace using the new-style
+# collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestXNameReplace(CollectionsTestBase):
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # 1 element
+ def test_XNameReplace_ReplaceName(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ scriptName = 'macro://Standard.Module1.MySave()'
+ eventProperties = (PropertyValue(Name='Script', Value=scriptName),)
+
+ # When
+ doc.Events['OnSave'] = eventProperties
+
+ # Then
+ onSave = [p.Value for p in doc.Events['OnSave'] if p.Name == 'Script'][0]
+ self.assertEqual(scriptName, onSave)
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # Invalid key
+ def test_XNameReplace_ReplaceName_Invalid(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ scriptName = 'macro://Standard.Module1.MySave()'
+ eventProperties = (PropertyValue(Name='Script', Value=scriptName),)
+
+ # When / Then
+ with self.assertRaises(KeyError):
+ doc.Events['qqqqq'] = eventProperties
+
+ # Tests syntax:
+ # obj[key] = val # Replace by key
+ # For:
+ # Invalid key type
+ def test_XNameReplace_ReplaceName_Invalid(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ scriptName = 'macro://Standard.Module1.MySave()'
+ eventProperties = (PropertyValue(Name='Script', Value=scriptName),)
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ doc.Events[12.34] = eventProperties
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_base.py b/pyuno/qa/pytests/testcollections_base.py
new file mode 100644
index 000000000000..2efb6f4a7578
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_base.py
@@ -0,0 +1,58 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from org.libreoffice.unotest import pyuno
+from com.sun.star.beans import PropertyValue
+
+class CollectionsTestBase(unittest.TestCase):
+
+ @classmethod
+ def setUpClass(cls):
+ cls.context = pyuno.getComponentContext()
+ pyuno.private_initTestEnvironment()
+
+ def setUp(self):
+ self._components = []
+
+ def tearDown(self):
+ for component in self._components:
+ try:
+ component.close(True)
+ except Exception:
+ pass
+
+ def createHiddenWindow(self, url):
+ serviceManager = self.context.ServiceManager
+ desktop = serviceManager.createInstanceWithContext('com.sun.star.frame.Desktop', self.context)
+ loadProps = (
+ PropertyValue(Name='Hidden', Value=True),
+ PropertyValue(Name='ReadOnly', Value=False)
+ )
+ component = desktop.loadComponentFromURL(url, '_blank', 0, loadProps)
+ return component
+
+ def createBlankTextDocument(self):
+ component = self.createHiddenWindow('private:factory/swriter')
+ self._components.append(component)
+ return component
+
+ def createBlankSpreadsheet(self):
+ component = self.createHiddenWindow('private:factory/scalc')
+ self._components.append(component)
+ return component
+
+ def createBlankDrawing(self):
+ component = self.createHiddenWindow('private:factory/sdraw')
+ self._components.append(component)
+ return component
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_misc.py b/pyuno/qa/pytests/testcollections_misc.py
new file mode 100644
index 000000000000..c66f6f440598
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_misc.py
@@ -0,0 +1,75 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Miscellaneous tests of the behaviour of UNO objects using the new-style
+# collection accessors
+
+class TestMisc(CollectionsTestBase):
+
+ # Tests syntax:
+ # for val in obj: ... # Implicit iterator
+ # For:
+ # Invalid type
+ def test_misc_IterateInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ for val in doc.UIConfigurationManager:
+ pass
+
+ # Tests syntax:
+ # if val in itr: ... # Test value presence
+ # For:
+ # Invalid type
+ def test_misc_InInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ foo = "bar" in doc.UIConfigurationManager
+
+ # Tests syntax:
+ # num = len(obj) # Number of elements
+ # For:
+ # Invalid type
+ def test_misc_LenInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ len(doc.UIConfigurationManager)
+
+ # Tests syntax:
+ # val = obj[0] # Access by index
+ # For:
+ # Invalid type
+ def test_misc_SubscriptInvalidType(self):
+ # Given
+ doc = self.createBlankTextDocument()
+
+ # When / Then
+ with self.assertRaises(TypeError):
+ doc.UIConfigurationManager[0]
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/qa/pytests/testcollections_mixednameindex.py b/pyuno/qa/pytests/testcollections_mixednameindex.py
new file mode 100644
index 000000000000..cf4c9e18e7ba
--- /dev/null
+++ b/pyuno/qa/pytests/testcollections_mixednameindex.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python
+#
+# This file is part of the LibreOffice project.
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+import unittest
+import uno
+
+from testcollections_base import CollectionsTestBase
+from com.sun.star.beans import PropertyValue
+
+
+# Tests behaviour of objects implementing both XIndexAccess and XNameAccess
+# using the new-style collection accessors
+# The objects chosen have no special meaning, they just happen to implement the
+# tested interfaces
+
+class TestMixedNameIndex(CollectionsTestBase):
+
+ # Tests:
+ # Ability to access a dual XName*/XIndex* object by both name and index
+ def testWriterTextTableNameAndIndex(self):
+ # Given
+ doc = self.createBlankTextDocument()
+ textTable = doc.createInstance("com.sun.star.text.TextTable")
+ textTable.initialize(2,2)
+ textTable.Name = 'foo'
+ cursor = doc.Text.createTextCursor()
+ doc.Text.insertTextContent(cursor, textTable, False)
+
+ # When
+ tableByName = doc.TextTables['foo']
+ tableByIndex = doc.TextTables[0]
+
+ # Then
+ self.assertEqual('foo', tableByName.Name)
+ self.assertEqual('foo', tableByIndex.Name)
+ self.assertEqual(tableByName,tableByIndex)
+
+
+if __name__ == '__main__':
+ unittest.main()
+
+# vim:set shiftwidth=4 softtabstop=4 expandtab: \ No newline at end of file
diff --git a/pyuno/source/module/pyuno.cxx b/pyuno/source/module/pyuno.cxx
index 86220376e0cb..ce9983eb42f8 100644
--- a/pyuno/source/module/pyuno.cxx
+++ b/pyuno/source/module/pyuno.cxx
@@ -19,6 +19,7 @@
#include <sal/config.h>
+#include <algorithm>
#include <cassert>
#include "pyuno_impl.hxx"
@@ -28,10 +29,21 @@
#include <osl/thread.h>
+#include <typelib/typedescription.hxx>
+
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/lang/XTypeProvider.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XMaterialHolder.hpp>
+#include <com/sun/star/container/XElementAccess.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XEnumerationAccess.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+#include <com/sun/star/container/XIndexReplace.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/container/XNameContainer.hpp>
+#include <com/sun/star/container/XNameReplace.hpp>
using com::sun::star::uno::Sequence;
using com::sun::star::uno::Reference;
@@ -41,6 +53,7 @@ using com::sun::star::uno::makeAny;
using com::sun::star::uno::UNO_QUERY;
using com::sun::star::uno::Type;
using com::sun::star::uno::TypeClass;
+using com::sun::star::uno::TypeDescription;
using com::sun::star::uno::RuntimeException;
using com::sun::star::uno::Exception;
using com::sun::star::uno::XComponentContext;
@@ -50,6 +63,15 @@ using com::sun::star::lang::XTypeProvider;
using com::sun::star::script::XTypeConverter;
using com::sun::star::script::XInvocation2;
using com::sun::star::beans::XMaterialHolder;
+using com::sun::star::container::XElementAccess;
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XEnumerationAccess;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::container::XIndexContainer;
+using com::sun::star::container::XIndexReplace;
+using com::sun::star::container::XNameAccess;
+using com::sun::star::container::XNameContainer;
+using com::sun::star::container::XNameReplace;
namespace pyuno
{
@@ -293,6 +315,57 @@ OUString val2str( const void * pVal, typelib_TypeDescriptionReference * pTypeRef
return buf.makeStringAndClear();
}
+sal_Int32 lcl_PyNumber_AsSal_Int32( PyObject *pObj )
+{
+ // Check object is an index
+ PyRef rIndex( PyNumber_Index( pObj ), SAL_NO_ACQUIRE );
+ if ( !rIndex.is() )
+ return -1;
+
+ // Convert Python number to platform long, then check actual value against
+ // bounds of sal_Int32
+ int nOverflow;
+ long nResult = PyLong_AsLongAndOverflow( pObj, &nOverflow );
+ if ( nOverflow || nResult > SAL_MAX_INT32 || nResult < SAL_MIN_INT32) {
+ PyErr_SetString( PyExc_IndexError, "Python int too large to convert to UNO long" );
+ return -1;
+ }
+
+ return nResult;
+}
+
+int lcl_PySlice_GetIndicesEx( PyObject *pObject, sal_Int32 nLen, sal_Int32 *nStart, sal_Int32 *nStop, sal_Int32 *nStep, sal_Int32 *nSliceLength )
+{
+ Py_ssize_t nStart_ssize, nStop_ssize, nStep_ssize, nSliceLength_ssize;
+
+ int nResult = PySlice_GetIndicesEx( (PySliceObject_t*)pObject, nLen, &nStart_ssize, &nStop_ssize, &nStep_ssize, &nSliceLength_ssize );
+ if (nResult == -1)
+ return -1;
+
+ if ( nStart_ssize > SAL_MAX_INT32 || nStart_ssize < SAL_MIN_INT32
+ || nStop_ssize > SAL_MAX_INT32 || nStop_ssize < SAL_MIN_INT32
+ || nStep_ssize > SAL_MAX_INT32 || nStep_ssize < SAL_MIN_INT32
+ || nSliceLength_ssize > SAL_MAX_INT32 || nSliceLength_ssize < SAL_MIN_INT32 )
+ {
+ PyErr_SetString( PyExc_IndexError, "Python int too large to convert to UNO long" );
+ return -1;
+ }
+
+ *nStart = (sal_Int32)nStart_ssize;
+ *nStop = (sal_Int32)nStop_ssize;
+ *nStep = (sal_Int32)nStep_ssize;
+ *nSliceLength = (sal_Int32)nSliceLength_ssize;
+ return 0;
+}
+
+bool lcl_hasInterfaceByName( Any &object, OUString interfaceName )
+{
+ Reference< XInterface > xInterface( object, UNO_QUERY );
+ TypeDescription typeDesc( interfaceName );
+ Any aInterface = xInterface->queryInterface( typeDesc.get()->pWeakRef );
+
+ return aInterface.hasValue();
+}
PyObject *PyUNO_repr( PyObject * self )
{
@@ -451,6 +524,880 @@ PyObject* PyUNO_dir (PyObject* self)
return member_list;
}
+sal_Int32 lcl_detach_getLength( PyUNO *me )
+{
+ PyThreadDetach antiguard;
+
+ // If both XIndexContainer and XNameContainer are implemented, it is
+ // assumed that getCount() gives the same result as the number of names
+ // returned by getElementNames(), or the user may be surprised.
+
+ // For XIndexContainer
+ Reference< XIndexAccess > xIndexAccess( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ {
+ return xIndexAccess->getCount();
+ }
+
+ // For XNameContainer
+ // Not terribly efficient - get the count of all the names
+ Reference< XNameAccess > xNameAccess( me->members->wrappedObject, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ return xNameAccess->getElementNames().getLength();
+ }
+
+ return -1;
+}
+
+int PyUNO_bool( PyObject* self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ int nLen = lcl_detach_getLength( me );
+ if (nLen >= 0)
+ return !!nLen;
+
+ // Anything which doesn't have members is a scalar object and therefore true
+ return 1;
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return -1;
+}
+
+Py_ssize_t PyUNO_len( PyObject* self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ int nLen = lcl_detach_getLength( me );
+ if (nLen >= 0)
+ return nLen;
+
+ PyErr_SetString( PyExc_TypeError, "object has no len()" );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return -1;
+}
+
+void lcl_getRowsColumns( PyUNO* me, sal_Int32& nRows, sal_Int32& nColumns )
+{
+ Sequence<short> aOutParamIndex;
+ Sequence<Any> aOutParam;
+ Sequence<Any> aParams;
+ Any aRet;
+
+ aRet = me->members->xInvocation->invoke ( "getRows", aParams, aOutParamIndex, aOutParam );
+ Reference< XIndexAccess > xIndexAccessRows( aRet, UNO_QUERY );
+ nRows = xIndexAccessRows->getCount();
+ aRet = me->members->xInvocation->invoke ( "getColumns", aParams, aOutParamIndex, aOutParam );
+ Reference< XIndexAccess > xIndexAccessCols( aRet, UNO_QUERY );
+ nColumns = xIndexAccessCols->getCount();
+}
+
+PyRef lcl_indexToSlice( PyRef rIndex )
+{
+ Py_ssize_t nIndex = PyNumber_AsSsize_t( rIndex.get(), PyExc_IndexError );
+ if (nIndex == -1 && PyErr_Occurred())
+ return NULL;
+ PyRef rStart( PyLong_FromSsize_t( nIndex ), SAL_NO_ACQUIRE );
+ PyRef rStop( PyLong_FromSsize_t( nIndex+1 ), SAL_NO_ACQUIRE );
+ PyRef rStep( PyLong_FromLong( 1 ), SAL_NO_ACQUIRE );
+ PyRef rSlice( PySlice_New( rStart.get(), rStop.get(), rStep.get() ), SAL_NO_ACQUIRE );
+
+ return rSlice;
+}
+
+PyObject* lcl_getitem_XCellRange( PyUNO* me, PyObject* pKey )
+{
+ Runtime runtime;
+
+ Sequence<short> aOutParamIndex;
+ Sequence<Any> aOutParam;
+ Sequence<Any> aParams;
+ Any aRet;
+
+ // Single string key is sugar for getCellRangeByName()
+ if ( PyStr_Check( pKey ) ) {
+
+ aParams.realloc (1);
+ aParams[0] <<= pyString2ustring( pKey );
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellRangeByName", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject ( aRet );
+ return rRet.getAcquired();
+
+ }
+
+ PyRef rKey0, rKey1;
+ if ( PyIndex_Check( pKey ) )
+ {
+ // [0] is equivalent to [0,:]
+ rKey0 = pKey;
+ rKey1 = PySlice_New( NULL, NULL, NULL );
+ }
+ else if ( PyTuple_Check( pKey ) && (PyTuple_Size( pKey ) == 2) )
+ {
+ rKey0 = PyTuple_GetItem( pKey, 0 );
+ rKey1 = PyTuple_GetItem( pKey, 1 );
+ }
+ else
+ {
+ PyErr_SetString( PyExc_KeyError, "invalid subscript" );
+ return NULL;
+ }
+
+ // If both keys are indices, return the corresponding cell
+ if ( PyIndex_Check( rKey0.get() ) && PyIndex_Check( rKey1.get() ))
+ {
+ sal_Int32 nKey0_s = lcl_PyNumber_AsSal_Int32( rKey0.get() );
+ sal_Int32 nKey1_s = lcl_PyNumber_AsSal_Int32( rKey1.get() );
+
+ if ( ((nKey0_s == -1) || (nKey1_s == -1)) && PyErr_Occurred() )
+ return NULL;
+
+ aParams.realloc( 2 );
+ aParams[0] <<= nKey1_s;
+ aParams[1] <<= nKey0_s;
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellByPosition", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ // If either argument is an index, coerce it to a slice
+ if ( PyIndex_Check( rKey0.get() ) )
+ rKey0 = lcl_indexToSlice( rKey0 );
+
+ if ( PyIndex_Check( rKey1.get() ) )
+ rKey1 = lcl_indexToSlice( rKey1 );
+
+ // If both arguments are slices, return the corresponding cell range
+ if ( PySlice_Check( rKey0.get() ) && PySlice_Check( rKey1.get() ) )
+ {
+ sal_Int32 nLen0 = SAL_MAX_INT32, nLen1 = SAL_MAX_INT32;
+ sal_Int32 nStart0 = 0, nStop0 = 0, nStep0 = 0, nSliceLength0 = 0;
+ sal_Int32 nStart1 = 0, nStop1 = 0, nStep1 = 0, nSliceLength1 = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ if ( lcl_hasInterfaceByName( me->members->wrappedObject, "com.sun.star.table.XColumnRowRange" ) )
+ {
+ lcl_getRowsColumns (me, nLen0, nLen1);
+ }
+ }
+
+ int nSuccess1 = lcl_PySlice_GetIndicesEx( rKey0.get(), nLen0, &nStart0, &nStop0, &nStep0, &nSliceLength0 );
+ int nSuccess2 = lcl_PySlice_GetIndicesEx( rKey1.get(), nLen1, &nStart1, &nStop1, &nStep1, &nSliceLength1 );
+ if ( ((nSuccess1 == -1) || (nSuccess2 == -1)) && PyErr_Occurred() )
+ return NULL;
+
+ if ( nSliceLength0 <= 0 || nSliceLength1 <= 0 )
+ {
+ PyErr_SetString( PyExc_KeyError, "invalid number of rows or columns" );
+ return NULL;
+ }
+
+ if ( nStep0 == 1 && nStep1 == 1 )
+ {
+ aParams.realloc (4);
+ aParams[0] <<= nStart1;
+ aParams[1] <<= nStart0;
+ aParams[2] <<= nStop1 - 1;
+ aParams[3] <<= nStop0 - 1;
+ {
+ PyThreadDetach antiguard;
+ aRet = me->members->xInvocation->invoke (
+ "getCellRangeByPosition", aParams, aOutParamIndex, aOutParam );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ PyErr_SetString( PyExc_KeyError, "step != 1 not supported" );
+ return NULL;
+ }
+
+ PyErr_SetString( PyExc_KeyError, "invalid subscript" );
+ return NULL;
+}
+
+PyObject* lcl_getitem_index( PyUNO *me, PyObject *pKey, Runtime& runtime )
+{
+ Any aRet;
+ sal_Int32 nIndex;
+
+ nIndex = lcl_PyNumber_AsSal_Int32( pKey );
+ if (nIndex == -1 && PyErr_Occurred())
+ return NULL;
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XIndexAccess > xIndexAccess( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ {
+ if (nIndex < 0)
+ nIndex += xIndexAccess->getCount();
+ aRet = xIndexAccess->getByIndex( nIndex );
+ }
+ }
+ if ( aRet.hasValue() )
+ {
+ PyRef rRet ( runtime.any2PyObject( aRet ) );
+ return rRet.getAcquired();
+ }
+
+ return NULL;
+}
+
+PyObject* lcl_getitem_slice( PyUNO *me, PyObject *pKey )
+{
+ Runtime runtime;
+
+ Reference< XIndexAccess > xIndexAccess;
+ sal_Int32 nLen = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexAccess.set( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexAccess.is() )
+ nLen = xIndexAccess->getCount();
+ }
+
+ if ( xIndexAccess.is() )
+ {
+ sal_Int32 nStart = 0, nStop = 0, nStep = 0, nSliceLength = 0;
+ int nSuccess = lcl_PySlice_GetIndicesEx(pKey, nLen, &nStart, &nStop, &nStep, &nSliceLength);
+ if ( nSuccess == -1 && PyErr_Occurred() )
+ return NULL;
+
+ PyRef rTuple( PyTuple_New( nSliceLength ), SAL_NO_ACQUIRE, NOT_NULL );
+ sal_Int32 nCur, i;
+ for ( nCur = nStart, i = 0; i < nSliceLength; nCur += nStep, i++ )
+ {
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+
+ aRet = xIndexAccess->getByIndex( nCur );
+ }
+ PyRef rRet = runtime.any2PyObject( aRet );
+ PyTuple_SetItem( rTuple.get(), i, rRet.getAcquired() );
+ }
+
+ return rTuple.getAcquired();
+ }
+
+ return NULL;
+}
+
+PyObject* lcl_getitem_string( PyUNO *me, PyObject *pKey, Runtime& runtime )
+{
+ OUString sKey = pyString2ustring( pKey );
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XNameAccess > xNameAccess( me->members->wrappedObject, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ aRet = xNameAccess->getByName( sKey );
+ }
+ }
+ if ( aRet.hasValue() )
+ {
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ return NULL;
+}
+
+PyObject* PyUNO_getitem( PyObject *self, PyObject *pKey )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+ Runtime runtime;
+
+ try
+ {
+ // XIndexAccess access by index
+ if ( PyIndex_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_index( me, pKey, runtime );
+ if ( pRet != NULL || PyErr_Occurred() )
+ return pRet;
+ }
+
+ // XIndexAccess access by slice
+ if ( PySlice_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_slice( me, pKey );
+ if ( pRet != NULL || PyErr_Occurred() )
+ return pRet;
+ }
+
+ // XNameAccess access by key
+ if ( PyStr_Check( pKey ) )
+ {
+ PyObject* pRet = lcl_getitem_string( me, pKey, runtime );
+ if ( pRet != NULL )
+ return pRet;
+ }
+
+ // XCellRange/XColumnRowRange specialisation
+ // Uses reflection as we can't have a hard dependency on XCellRange here
+ bool hasXCellRange = false;
+
+ {
+ PyThreadDetach antiguard;
+
+ hasXCellRange = lcl_hasInterfaceByName( me->members->wrappedObject, "com.sun.star.table.XCellRange" );
+ }
+ if ( hasXCellRange )
+ {
+ return lcl_getitem_XCellRange( me, pKey );
+ }
+
+
+ // If the object is an XIndexAccess and/or XNameAccess, but the
+ // key passed wasn't suitable, give a TypeError which specifically
+ // describes this
+ Reference< XIndexAccess > xIndexAccess( me->members->wrappedObject, UNO_QUERY );
+ Reference< XNameAccess > xNameAccess( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexAccess.is() || xNameAccess.is() )
+ {
+ PyErr_SetString( PyExc_TypeError, "subscription with invalid type" );
+ return NULL;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "object is not subscriptable" );
+ }
+ catch( const ::com::sun::star::lang::IndexOutOfBoundsException )
+ {
+ PyErr_SetString( PyExc_IndexError, "index out of range" );
+ }
+ catch( const ::com::sun::star::container::NoSuchElementException )
+ {
+ PyErr_SetString( PyExc_KeyError, "key not found" );
+ }
+ catch( const com::sun::star::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const com::sun::star::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return NULL;
+}
+
+int lcl_setitem_index( PyUNO *me, PyObject *pKey, PyObject *pValue )
+{
+ Runtime runtime;
+
+ Reference< XIndexContainer > xIndexContainer;
+ Reference< XIndexReplace > xIndexReplace;
+ sal_Int32 nIndex = lcl_PyNumber_AsSal_Int32( pKey );
+ if ( nIndex == -1 && PyErr_Occurred() )
+ return 0;
+
+ bool isTuple = false;
+
+ Any aValue;
+ if ( pValue != NULL )
+ {
+ isTuple = PyTuple_Check( pValue );
+
+ try
+ {
+ aValue <<= runtime.pyObject2Any( pValue );
+ }
+ catch ( const ::com::sun::star::uno::RuntimeException )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw ::com::sun::star::script::CannotConvertException();
+ }
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer.set( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexContainer.is() )
+ xIndexReplace.set( xIndexContainer, UNO_QUERY );
+ else
+ xIndexReplace.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( xIndexReplace.is() && nIndex < 0 )
+ nIndex += xIndexReplace->getCount();
+
+ // XIndexReplace replace by index
+ if ( (pValue != NULL) && xIndexReplace.is() )
+ {
+ if ( isTuple )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xIndexReplace->getElementType();
+ aValue = runtime.getImpl()->cargo->xTypeConverter->convertTo( aValue, aType );
+ }
+
+ xIndexReplace->replaceByIndex( nIndex, aValue );
+ return 0;
+ }
+
+ // XIndexContainer remove by index
+ if ( (pValue == NULL) && xIndexContainer.is() )
+ {
+ xIndexContainer->removeByIndex( nIndex );
+ return 0;
+ }
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+int lcl_setitem_slice( PyUNO *me, PyObject *pKey, PyObject *pValue )
+{
+ // XIndexContainer insert/remove/replace by slice
+ Runtime runtime;
+
+ Reference< XIndexReplace > xIndexReplace;
+ Reference< XIndexContainer > xIndexContainer;
+ sal_Int32 nLen = 0;
+
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer.set( me->members->wrappedObject, UNO_QUERY );
+ if ( xIndexContainer.is() )
+ xIndexReplace.set( xIndexContainer, UNO_QUERY );
+ else
+ xIndexReplace.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( xIndexReplace.is() )
+ nLen = xIndexReplace->getCount();
+ }
+
+ if ( xIndexReplace.is() )
+ {
+ sal_Int32 nStart = 0, nStop = 0, nStep = 0, nSliceLength = 0;
+ int nSuccess = lcl_PySlice_GetIndicesEx( pKey, nLen, &nStart, &nStop, &nStep, &nSliceLength );
+ if ( (nSuccess == -1) && PyErr_Occurred() )
+ return 0;
+
+ if ( pValue == NULL )
+ {
+ pValue = PyTuple_New( 0 );
+ }
+
+ if ( !PyTuple_Check (pValue) )
+ {
+ PyErr_SetString( PyExc_TypeError, "value is not a tuple" );
+ return 1;
+ }
+
+ Py_ssize_t nTupleLength_ssize = PyTuple_Size( pValue );
+ if ( nTupleLength_ssize > SAL_MAX_INT32 )
+ {
+ PyErr_SetString( PyExc_ValueError, "tuple too large" );
+ return 1;
+ }
+ sal_Int32 nTupleLength = (sal_Int32)nTupleLength_ssize;
+
+ if ( (nTupleLength != nSliceLength) && (nStep != 1) )
+ {
+ PyErr_SetString( PyExc_ValueError, "number of items assigned must be equal" );
+ return 1;
+ }
+
+ if ( (nTupleLength != nSliceLength) && !xIndexContainer.is() )
+ {
+ PyErr_SetString( PyExc_ValueError, "cannot change length" );
+ return 1;
+ }
+
+ sal_Int32 nCur, i;
+ sal_Int32 nMax = ::std::max( nSliceLength, nTupleLength );
+ for ( nCur = nStart, i = 0; i < nMax; nCur += nStep, i++ )
+ {
+ if ( i < nTupleLength )
+ {
+ PyRef rItem = PyTuple_GetItem( pValue, i );
+ bool isTuple = PyTuple_Check( rItem.get() );
+
+ Any aItem;
+ try
+ {
+ aItem <<= runtime.pyObject2Any( rItem.get() );
+ }
+ catch ( const ::com::sun::star::uno::RuntimeException )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw ::com::sun::star::script::CannotConvertException();
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ if ( isTuple )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xIndexReplace->getElementType();
+ aItem = runtime.getImpl()->cargo->xTypeConverter->convertTo( aItem, aType );
+ }
+
+ if ( i < nSliceLength )
+ {
+ xIndexReplace->replaceByIndex( nCur, aItem );
+ }
+ else
+ {
+ xIndexContainer->insertByIndex( nCur, aItem );
+ }
+ }
+ }
+ else
+ {
+ PyThreadDetach antiguard;
+
+ xIndexContainer->removeByIndex( nCur );
+ nCur--;
+ }
+ }
+
+ return 0;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+int lcl_setitem_string( PyUNO *me, PyObject *pKey, PyObject *pValue )
+{
+ Runtime runtime;
+
+ OUString sKey = pyString2ustring( pKey );
+ bool isTuple = false;
+
+ Any aValue;
+ if ( pValue != NULL)
+ {
+ isTuple = PyTuple_Check( pValue );
+ try
+ {
+ aValue <<= runtime.pyObject2Any( pValue );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw ::com::sun::star::script::CannotConvertException();
+ }
+ }
+
+ {
+ PyThreadDetach antiguard;
+
+ Reference< XNameContainer > xNameContainer( me->members->wrappedObject, UNO_QUERY );
+ Reference< XNameReplace > xNameReplace;
+ if ( xNameContainer.is() )
+ xNameReplace.set( xNameContainer, UNO_QUERY );
+ else
+ xNameReplace.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( xNameReplace.is() )
+ {
+ if ( isTuple && aValue.hasValue() )
+ {
+ // Apply type specialisation to ensure the correct kind of sequence is passed
+ Type aType = xNameReplace->getElementType();
+ aValue = runtime.getImpl()->cargo->xTypeConverter->convertTo( aValue, aType );
+ }
+
+ if ( aValue.hasValue() )
+ {
+ if ( xNameContainer.is() )
+ {
+ try {
+ xNameContainer->insertByName( sKey, aValue );
+ return 0;
+ }
+ catch( com::sun::star::container::ElementExistException )
+ {
+ // Fall through, try replace instead
+ }
+ }
+
+ xNameReplace->replaceByName( sKey, aValue );
+ return 0;
+ }
+ else if ( xNameContainer.is() )
+ {
+ xNameContainer->removeByName( sKey );
+ return 0;
+ }
+ }
+ }
+
+ PyErr_SetString( PyExc_TypeError, "cannot assign to object" );
+ return 1;
+}
+
+int PyUNO_setitem( PyObject *self, PyObject *pKey, PyObject *pValue )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ if ( PyIndex_Check( pKey ) )
+ {
+ return lcl_setitem_index( me, pKey, pValue );
+ }
+ else if ( PySlice_Check( pKey ) )
+ {
+ return lcl_setitem_slice( me, pKey, pValue );
+ }
+ else if ( PyStr_Check( pKey ) )
+ {
+ return lcl_setitem_string( me, pKey, pValue );
+ }
+
+ PyErr_SetString( PyExc_TypeError, "list index has invalid type" );
+ }
+ catch( const ::com::sun::star::lang::IndexOutOfBoundsException )
+ {
+ PyErr_SetString( PyExc_IndexError, "list index out of range" );
+ }
+ catch( const ::com::sun::star::container::NoSuchElementException )
+ {
+ PyErr_SetString( PyExc_KeyError, "key not found" );
+ }
+ catch( const ::com::sun::star::lang::IllegalArgumentException )
+ {
+ PyErr_SetString( PyExc_TypeError, "value has invalid type" );
+ }
+ catch( com::sun::star::script::CannotConvertException )
+ {
+ PyErr_SetString( PyExc_TypeError, "value has invalid type" );
+ }
+ catch( const ::com::sun::star::container::ElementExistException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const::com::sun::star::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return 1;
+}
+
+PyObject* PyUNO_iter( PyObject *self )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ try
+ {
+ Reference< XEnumerationAccess > xEnumerationAccess;
+ Reference< XEnumeration > xEnumeration;
+ Reference< XIndexAccess > xIndexAccess;
+ Reference< XNameAccess > xNameAccess;
+
+ {
+ PyThreadDetach antiguard;
+
+ xEnumerationAccess.set( me->members->wrappedObject, UNO_QUERY );
+ if ( xEnumerationAccess.is() )
+ xEnumeration = xEnumerationAccess->createEnumeration();
+ else
+ xEnumeration.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( !xEnumeration.is() )
+ xIndexAccess.set( me->members->wrappedObject, UNO_QUERY );
+
+ if ( !xIndexAccess.is() )
+ xNameAccess.set( me->members->wrappedObject, UNO_QUERY );
+ }
+
+ // XEnumerationAccess iterator
+ // XEnumeration iterator
+ if (xEnumeration.is())
+ {
+ return PyUNO_iterator_new( xEnumeration );
+ }
+
+ // XIndexAccess iterator
+ if ( xIndexAccess.is() )
+ {
+ // We'd like to be able to use PySeqIter_New() here, but we're not
+ // allowed to because we also implement the mapping protocol
+ return PyUNO_list_iterator_new( xIndexAccess );
+ }
+
+ // XNameAccess iterator
+ if (xNameAccess.is())
+ {
+ // There's no generic mapping iterator, but we can cobble our own
+ // together using PySeqIter_New()
+ Runtime runtime;
+ Any aRet;
+
+ {
+ PyThreadDetach antiguard;
+ aRet <<= xNameAccess->getElementNames();
+ }
+ PyRef rNames = runtime.any2PyObject( aRet );
+ return PySeqIter_New( rNames.getAcquired() );
+ }
+
+ PyErr_SetString ( PyExc_TypeError, "object is not iterable" );
+ }
+ catch( com::sun::star::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( com::sun::star::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return NULL;
+}
+
+int PyUNO_contains( PyObject *self, PyObject *pKey )
+{
+ PyUNO* me = reinterpret_cast<PyUNO*>(self);
+
+ Runtime runtime;
+
+ try
+ {
+ Any aValue;
+ try
+ {
+ aValue <<= runtime.pyObject2Any( pKey );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException )
+ {
+ // TODO pyObject2Any can't convert e.g. dicts but only throws
+ // RuntimeException on failure. Fixing this will require an audit of
+ // all the rest of PyUNO
+ throw ::com::sun::star::script::CannotConvertException();
+ }
+
+ // XNameAccess is tried first, because checking key presence is much more
+ // useful for objects which implement both XIndexAccess and XNameAccess
+
+ // For XNameAccess
+ if ( PyStr_Check( pKey ) )
+ {
+ OUString sKey;
+ aValue >>= sKey;
+ Reference< XNameAccess > xNameAccess;
+
+ {
+ PyThreadDetach antiguard;
+
+ xNameAccess.set( me->members->wrappedObject, UNO_QUERY );
+ if ( xNameAccess.is() )
+ {
+ sal_Bool hasKey = xNameAccess->hasByName( sKey );
+ return hasKey == sal_True ? 1 : 0;
+ }
+ }
+ }
+
+ // For any other type of PyUNO iterable: Ugly iterative search by
+ // content (XIndexAccess, XEnumerationAccess, XEnumeration)
+ PyRef rIterator( PyUNO_iter( self ), SAL_NO_ACQUIRE );
+ if ( rIterator.is() )
+ {
+ PyObject* pItem;
+ while ( (pItem = PyIter_Next( rIterator.get() )) )
+ {
+ PyRef rItem( pItem, SAL_NO_ACQUIRE );
+ if ( PyObject_RichCompareBool( pKey, rItem.get(), Py_EQ ) == 1 )
+ {
+ return 1;
+ }
+ }
+ return 0;
+ }
+
+ PyErr_SetString( PyExc_TypeError, "argument is not iterable" );
+ }
+ catch( const com::sun::star::script::CannotConvertException )
+ {
+ PyErr_SetString( PyExc_TypeError, "invalid type passed as left argument to 'in'" );
+ }
+ catch( const ::com::sun::star::container::NoSuchElementException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::lang::IndexOutOfBoundsException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const com::sun::star::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return -1;
+}
PyObject* PyUNO_getattr (PyObject* self, char* name)
{
@@ -644,17 +1591,83 @@ static PyObject* PyUNO_cmp( PyObject *self, PyObject *that, int op )
static PyMethodDef PyUNOMethods[] =
{
- {"__dir__", reinterpret_cast<PyCFunction>(PyUNO_dir), METH_NOARGS, NULL},
- {NULL, NULL, 0, NULL}
+ {"__dir__", reinterpret_cast<PyCFunction>(PyUNO_dir), METH_NOARGS, NULL},
+ {NULL, NULL, 0, NULL}
};
-
-/* Python 2 has a tp_flags value for rich comparisons. Python 3 does not (on by default) */
-#ifdef Py_TPFLAGS_HAVE_RICHCOMPARE
-#define TP_FLAGS (Py_TPFLAGS_HAVE_RICHCOMPARE)
-#else
-#define TP_FLAGS 0
+static PyNumberMethods PyUNONumberMethods[] =
+{
+ nullptr, /* nb_add */
+ nullptr, /* nb_subtract */
+ nullptr, /* nb_multiply */
+#if PY_MAJOR_VERSION < 3
+ nullptr, /* nb_divide */
#endif
+ nullptr, /* nb_remainder */
+ nullptr, /* nb_divmod */
+ nullptr, /* nb_power */
+ nullptr, /* nb_negative */
+ nullptr, /* nb_positive */
+ nullptr, /* nb_absolute */
+ PyUNO_bool, /* nb_bool */
+ nullptr, /* nb_invert */
+ nullptr, /* nb_lshift */
+ nullptr, /* nb_rshift */
+ nullptr, /* nb_and */
+ nullptr, /* nb_xor */
+ nullptr, /* nb_or */
+#if PY_MAJOR_VERSION < 3
+ nullptr, /* nb_coerce */
+#endif
+ nullptr, /* nb_int */
+ nullptr, /* nb_reserved */
+ nullptr, /* nb_float */
+#if PY_MAJOR_VERSION < 3
+ nullptr, /* nb_oct */
+ nullptr, /* nb_hex */
+#endif
+ nullptr, /* nb_inplace_add */
+ nullptr, /* nb_inplace_subtract */
+ nullptr, /* nb_inplace_multiply */
+#if PY_MAJOR_VERSION < 3
+ nullptr, /* nb_inplace_divide */
+#endif
+ nullptr, /* nb_inplace_remainder */
+ nullptr, /* nb_inplace_power */
+ nullptr, /* nb_inplace_lshift */
+ nullptr, /* nb_inplace_rshift */
+ nullptr, /* nb_inplace_and */
+ nullptr, /* nb_inplace_xor */
+ nullptr, /* nb_inplace_or */
+
+ nullptr, /* nb_floor_divide */
+ nullptr, /* nb_true_divide */
+ nullptr, /* nb_inplace_floor_divide */
+ nullptr, /* nb_inplace_true_divide */
+
+ nullptr, /* nb_index */
+};
+
+static PySequenceMethods PyUNOSequenceMethods[] =
+{
+ nullptr, /* sq_length */
+ nullptr, /* sq_concat */
+ nullptr, /* sq_repeat */
+ nullptr, /* sq_item */
+ nullptr, /* sq_slice */
+ nullptr, /* sq_ass_item */
+ nullptr, /* sq_ass_slice */
+ PyUNO_contains, /* sq_contains */
+ nullptr, /* sq_inplace_concat */
+ nullptr /* sq_inplace_repeat */
+};
+
+static PyMappingMethods PyUNOMappingMethods[] =
+{
+ PyUNO_len, /* mp_length */
+ PyUNO_getitem, /* mp_subscript */
+ PyUNO_setitem, /* mp_ass_subscript */
+};
static PyTypeObject PyUNOType =
{
@@ -668,22 +1681,22 @@ static PyTypeObject PyUNOType =
PyUNO_setattr,
/* this type does not exist in Python 3: (cmpfunc) */ 0,
PyUNO_repr,
- 0,
- 0,
- 0,
+ PyUNONumberMethods,
+ PyUNOSequenceMethods,
+ PyUNOMappingMethods,
nullptr,
nullptr,
PyUNO_str,
nullptr,
nullptr,
NULL,
- TP_FLAGS,
+ Py_TPFLAGS_HAVE_ITER | Py_TPFLAGS_HAVE_RICHCOMPARE | Py_TPFLAGS_HAVE_SEQUENCE_IN,
NULL,
nullptr,
nullptr,
PyUNO_cmp,
0,
- nullptr,
+ PyUNO_iter,
nullptr,
PyUNOMethods,
NULL,
diff --git a/pyuno/source/module/pyuno_impl.hxx b/pyuno/source/module/pyuno_impl.hxx
index e066b7327b12..f7824ba50566 100644
--- a/pyuno/source/module/pyuno_impl.hxx
+++ b/pyuno/source/module/pyuno_impl.hxx
@@ -31,6 +31,17 @@
#define PyVarObject_HEAD_INIT(type, size) PyObject_HEAD_INIT(type) size,
#endif
+//Python 3.0 and newer don't have these flags
+#ifndef Py_TPFLAGS_HAVE_ITER
+# define Py_TPFLAGS_HAVE_ITER 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_RICHCOMPARE
+# define Py_TPFLAGS_HAVE_RICHCOMPARE 0
+#endif
+#ifndef Py_TPFLAGS_HAVE_SEQUENCE_IN
+# define Py_TPFLAGS_HAVE_SEQUENCE_IN 0
+#endif
+
#include <pyuno/pyuno.hxx>
#include <unordered_map>
@@ -43,6 +54,8 @@
#include <com/sun/star/reflection/XIdlReflection.hpp>
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
#include <com/sun/star/lang/XUnoTunnel.hpp>
@@ -130,6 +143,13 @@ inline PyObject* PyStrBytes_FromStringAndSize(const char *string, Py_ssize_t len
}
#endif /* PY_MAJOR_VERSION >= 3 */
+// Type of argument to PySlice_GetIndicesEx() changed in Python 3.2
+#if PY_VERSION_HEX >= 0x030200f0
+typedef PyObject PySliceObject_t;
+#else
+typedef PySliceObject PySliceObject_t;
+#endif
+
namespace pyuno
{
@@ -212,6 +232,35 @@ typedef struct
PyUNOInternals* members;
} PyUNO;
+PyObject* PyUNO_iterator_new (
+ const com::sun::star::uno::Reference<com::sun::star::container::XEnumeration> xEnumeration);
+
+typedef struct
+{
+ com::sun::star::uno::Reference <com::sun::star::container::XEnumeration> xEnumeration;
+} PyUNO_iterator_Internals;
+
+typedef struct
+{
+ PyObject_HEAD
+ PyUNO_iterator_Internals* members;
+} PyUNO_iterator;
+
+PyObject* PyUNO_list_iterator_new (
+ const com::sun::star::uno::Reference<com::sun::star::container::XIndexAccess> &xIndexAccess);
+
+typedef struct
+{
+ com::sun::star::uno::Reference <com::sun::star::container::XIndexAccess> xIndexAccess;
+ int index;
+} PyUNO_list_iterator_Internals;
+
+typedef struct
+{
+ PyObject_HEAD
+ PyUNO_list_iterator_Internals* members;
+} PyUNO_list_iterator;
+
PyRef ustring2PyUnicode( const OUString &source );
PyRef ustring2PyString( const OUString & source );
OUString pyString2ustring( PyObject *str );
diff --git a/pyuno/source/module/pyuno_iterator.cxx b/pyuno/source/module/pyuno_iterator.cxx
new file mode 100644
index 000000000000..f9dd8d0904d6
--- /dev/null
+++ b/pyuno/source/module/pyuno_iterator.cxx
@@ -0,0 +1,312 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <cassert>
+
+#include "pyuno_impl.hxx"
+
+#include <com/sun/star/container/XEnumeration.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/WrappedTargetException.hpp>
+
+using com::sun::star::container::XEnumeration;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::lang::IndexOutOfBoundsException;
+using com::sun::star::lang::WrappedTargetException;
+using com::sun::star::uno::Any;
+using com::sun::star::uno::Reference;
+using com::sun::star::uno::RuntimeException;
+using com::sun::star::uno::UNO_QUERY;
+
+
+namespace pyuno
+{
+
+void PyUNO_iterator_del( PyObject* self )
+{
+ PyUNO_iterator* me = reinterpret_cast<PyUNO_iterator*>(self);
+
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del( self );
+}
+
+PyObject* PyUNO_iterator_iter( PyObject *self )
+{
+ Py_INCREF( self );
+ return self;
+}
+
+PyObject* PyUNO_iterator_next( PyObject *self )
+{
+ PyUNO_iterator* me = reinterpret_cast<PyUNO_iterator*>(self);
+
+ Runtime runtime;
+ sal_Bool hasMoreElements = sal_False;
+ Any aRet;
+
+ try
+ {
+ {
+ PyThreadDetach antiguard;
+
+ hasMoreElements = me->members->xEnumeration->hasMoreElements();
+ if ( hasMoreElements )
+ {
+ aRet = me->members->xEnumeration->nextElement();
+ }
+ }
+
+ if ( hasMoreElements )
+ {
+ PyRef rRet = runtime.any2PyObject( aRet );
+ return rRet.getAcquired();
+ }
+
+ PyErr_SetString( PyExc_StopIteration, "" );
+ return NULL;
+ }
+ catch( com::sun::star::container::NoSuchElementException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( com::sun::star::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( com::sun::star::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return NULL;
+}
+
+
+static PyTypeObject PyUNO_iterator_Type =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "PyUNO_iterator",
+ sizeof (PyUNO_iterator),
+ 0,
+ PyUNO_iterator_del,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_ITER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ PyUNO_iterator_iter, // Generic, reused between the iterator types
+ PyUNO_iterator_next,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#endif
+};
+
+PyObject* PyUNO_iterator_new( const Reference< XEnumeration > xEnumeration )
+{
+ PyUNO_iterator* self = PyObject_New( PyUNO_iterator, &PyUNO_iterator_Type );
+ if ( self == NULL )
+ return NULL; // == error
+ self->members = new PyUNO_iterator_Internals();
+ self->members->xEnumeration = xEnumeration;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+void PyUNO_list_iterator_del( PyObject* self )
+{
+ PyUNO_list_iterator* me = reinterpret_cast<PyUNO_list_iterator*>(self);
+
+ {
+ PyThreadDetach antiguard;
+ delete me->members;
+ }
+ PyObject_Del( self );
+}
+
+
+PyObject* PyUNO_list_iterator_next( PyObject *self )
+{
+ PyUNO_list_iterator* me = reinterpret_cast<PyUNO_list_iterator*>(self);
+
+ Runtime runtime;
+ Any aRet;
+ bool noMoreElements = false;
+
+ try
+ {
+ {
+ PyThreadDetach antiguard;
+ try {
+ aRet = me->members->xIndexAccess->getByIndex( me->members->index );
+ }
+ catch( com::sun::star::lang::IndexOutOfBoundsException )
+ {
+ noMoreElements = true;
+ }
+ }
+
+ if ( noMoreElements )
+ {
+ PyErr_SetString( PyExc_StopIteration, "" );
+ return NULL;
+ }
+
+ PyRef rRet = runtime.any2PyObject( aRet );
+ me->members->index++;
+ return rRet.getAcquired();
+ }
+ catch( com::sun::star::script::CannotConvertException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( com::sun::star::lang::IllegalArgumentException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::lang::WrappedTargetException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+ catch( const ::com::sun::star::uno::RuntimeException &e )
+ {
+ raisePyExceptionWithAny( com::sun::star::uno::makeAny( e ) );
+ }
+
+ return NULL;
+}
+
+
+static PyTypeObject PyUNO_list_iterator_Type =
+{
+ PyVarObject_HEAD_INIT( &PyType_Type, 0 )
+ "PyUNO_iterator",
+ sizeof (PyUNO_list_iterator),
+ 0,
+ PyUNO_list_iterator_del,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ Py_TPFLAGS_HAVE_ITER,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ PyUNO_iterator_iter, // Generic, reused between the iterator types
+ PyUNO_list_iterator_next,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ 0
+#if PY_VERSION_HEX >= 0x03040000
+ , nullptr
+#endif
+};
+
+PyObject* PyUNO_list_iterator_new( const Reference<XIndexAccess> &xIndexAccess )
+{
+ PyUNO_list_iterator* self = PyObject_New( PyUNO_list_iterator, &PyUNO_list_iterator_Type );
+ if ( self == NULL )
+ return NULL; // == error
+ self->members = new PyUNO_list_iterator_Internals();
+ self->members->xIndexAccess = xIndexAccess;
+ self->members->index = 0;
+ return reinterpret_cast<PyObject*>(self);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/pyuno/source/module/pyuno_module.cxx b/pyuno/source/module/pyuno_module.cxx
index c2187afcccd8..722e9226b1a7 100644
--- a/pyuno/source/module/pyuno_module.cxx
+++ b/pyuno/source/module/pyuno_module.cxx
@@ -180,18 +180,22 @@ static void fillStruct(
inv->setValue( pMemberName, a );
}
}
- for ( int i = 0; i < nMembers ; ++i)
+ if ( PyTuple_Size( initializer ) > 0 )
{
- const OUString memberName (pCompType->ppMemberNames[i]);
- if ( ! state.isInitialised( memberName ) )
+ // Allow partial initialisation when only keyword arguments are given
+ for ( int i = 0; i < nMembers ; ++i)
{
- OUStringBuffer buf;
- buf.appendAscii( "pyuno._createUnoStructHelper: member '");
- buf.append(memberName);
- buf.appendAscii( "' of struct type '");
- buf.append(pCompType->aBase.pTypeName);
- buf.appendAscii( "' not given a value.");
- throw RuntimeException(buf.makeStringAndClear());
+ const OUString memberName (pCompType->ppMemberNames[i]);
+ if ( ! state.isInitialised( memberName ) )
+ {
+ OUStringBuffer buf;
+ buf.appendAscii( "pyuno._createUnoStructHelper: member '");
+ buf.append(memberName);
+ buf.appendAscii( "' of struct type '");
+ buf.append(pCompType->aBase.pTypeName);
+ buf.appendAscii( "' not given a value.");
+ throw RuntimeException(buf.makeStringAndClear());
+ }
}
}
}
diff --git a/pyuno/source/module/pyuno_runtime.cxx b/pyuno/source/module/pyuno_runtime.cxx
index 8b927a6c2514..9d69797730d5 100644
--- a/pyuno/source/module/pyuno_runtime.cxx
+++ b/pyuno/source/module/pyuno_runtime.cxx
@@ -30,6 +30,7 @@
#include <rtl/ustrbuf.hxx>
#include <rtl/bootstrap.hxx>
+#include <list>
#include <typelib/typedescription.hxx>
#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
@@ -620,6 +621,39 @@ lcl_ExceptionMessage(PyObject *const o, OUString const*const pWrapped)
return buf.makeStringAndClear();
}
+// For Python 2.7 - see https://bugs.python.org/issue24161
+// Fills aSeq and returns true if pObj is a valid iterator
+bool Runtime::pyIterUnpack( PyObject *const pObj, Any &a ) const
+{
+ if( !PyIter_Check( pObj ))
+ return false;
+
+ PyObject *pItem = PyIter_Next( pObj );
+ if( !pItem )
+ {
+ if( PyErr_Occurred() )
+ {
+ PyErr_Clear();
+ return false;
+ }
+ return true;
+ }
+
+ ::std::list<Any> items;
+ do
+ {
+ PyRef rItem( pItem, SAL_NO_ACQUIRE );
+ items.push_back( pyObject2Any( rItem.get() ) );
+ }
+ while( (pItem = PyIter_Next( pObj )) );
+ Sequence<Any> aSeq( items.size() );
+ ::std::list<Any>::iterator it = items.begin();
+ for( int i = 0; it != items.end(); ++it )
+ aSeq[i++] = *it;
+ a <<= aSeq;
+ return true;
+}
+
Any Runtime::pyObject2Any ( const PyRef & source, enum ConversionMode mode ) const
throw ( com::sun::star::uno::RuntimeException )
{
@@ -728,7 +762,17 @@ Any Runtime::pyObject2Any ( const PyRef & source, enum ConversionMode mode ) con
}
a <<= s;
}
- else
+ else if (PyList_Check (o))
+ {
+ Py_ssize_t l = PyList_Size (o);
+ Sequence<Any> s (l);
+ for (int i = 0; i < l; i++)
+ {
+ s[i] = pyObject2Any (PyList_GetItem (o, i), mode );
+ }
+ a <<= s;
+ }
+ else if (!pyIterUnpack (o, a))
{
Runtime runtime;
// should be removed, in case ByteSequence gets derived from String