Kaydet (Commit) af8143bc authored tarafından Matthew J. Francis's avatar Matthew J. Francis Kaydeden (comit) Matthew Francis

Make PyUNO provide more Pythonic behaviour

- Simplifies working with UNO objects by giving the behaviour of
Python lists, dicts and iterators to objects which implement UNO
container interfaces

- Applies a custom behaviour to allow objects which implement
com::sun::star::table::XCellRange to yield cells and cell ranges by
subscript

- When UNO container objects are addressed in the new style,
eliminates the requirement to manually construct Any objects for
contained elements which are typed sequences

- Allows lists and iterators to be passed wherever a UNO method
accepts a sequence

- Relaxes the requirements for initialising UNO structs to allow
some members to be skipped when all initialisers are passed by name

1. Collection interfaces
========================

Objects which implement core UNO collection interfaces are made to
behave in a way that is more natural for Python code.

com::sun::star::container::XIndexAccess
com::sun::star::container::XIndexReplace
com::sun::star::container::XIndexContainer
- Objects provide Python list access semantics
    num = len(obj)              # Number of elements
    val = obj[0]                # Access by index
    val1,val2 = obj[2:4]        # Access by slice
    val1,val2 = obj[0:3:2]      # Access by extended slice
    if val in obj: ...          # Test value presence
    for val in obj: ...         # Implicit iterator (values)
    itr = iter(obj)             # Named iterator (values)
    obj[0] = val                # Replace by index
    obj[2:4] = val1,val2        # Replace by slice
    obj[0:3:2] = val1,val2      # Replace by extended 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)
    del obj[0]                  # Delete by index
    del obj[2:4]                # Delete by slice

com::sun::star::container::XNameAccess
com::sun::star::container::XNameReplace
com::sun::star::container::XNameContainer
- Objects provide Python dict access semantics
    num = len(obj)              # Number of keys
    val = obj[key]              # Access by key
    if key in obj: ...          # Test key presence
    for key in obj: ...         # Implicit iterator (keys)
    itr = iter(obj)             # Named iterator (keys)
    obj[key] = val              # Replace by key
    obj[key] = val              # Insert by key
    del obj[key]                # Delete by key

com::sun::star::container::XEnumerationAccess
- Objects provide Python iterable semantics
    for val in obj: ...         # Implicit iterator
    itr = iter(obj)             # Named iterator

com::sun::star::container::XEnumeration
- Objects provide Python iterator semantics
    for val in itr: ...         # Iteration of named iterator
    if val in itr: ...          # Test value presence

Objects which implement both XIndex* and XName* are supported, and
respond to both integer and string keys. However, iterating over
such an object will return the keys (like a Python dict) rather than
the values (like a Python list).

2. Cell ranges
==============

A custom behaviour is applied to objects which implement
com::sun::star::table::XCellRange to allow their cells and cell
ranges to be addressed by subscript, in the style of a Python list
or dict (read-only). This is applicable to Calc spreadsheet sheets,
Writer text tables and cell ranges created upon these.
    cell = cellrange[0,0]       # Access cell by indices
    rng = cellrange[0,1:2]      # Access cell range by index,slice
    rng = cellrange[1:2,0]      # Access cell range by slice,index
    rng = cellrange[0:1,2:3]    # Access cell range by slices
    rng = cellrange['A1:B2']    # Access cell range by descriptor
    rng = cellrange['Name']     # Access cell range by name

Note that the indices used are in Python/C order, and differ from
the arguments to methods provided by XCellRange.
- The statement cellrange[r,c], which returns the cell from row r
and column c, is equivalent to calling
    XCellRange::getCellByPosition(c,r)
- The statement cellrange[t:b,l:r], which returns a cell range
covering rows t to b(non-inclusive) and columns l to r(non-
inclusive), is equivalent to calling
    XCellRange::getCellRangeByPosition(l,t,r-1,b-1).

In contrast to the handling of objects implementing XIndex*,
extended slice syntax is not supported. Negative indices (from-end
addresses) are supported only for objects which also implement
com::sun::star::table::XColumnRowRange (currently Calc spreadsheet
sheets and cell ranges created upon these). For such objects, the
following syntax is also available:
    rng = cellrange[0]          # Access cell range by row index
    rng = cellrange[0,:]        # Access cell range by row index
    rng = cellrange[:,0]        # Access cell range by column index

3. Elimination of explicit Any
==============================

PyUNO has not previously been able to cope with certain method
arguments which are typed as Any but require a sequence of specific
type to be passed. This is a particular issue for container
interfaces such as XIndexContainer and XNameContainer.

The existing solution to dealing with such methods is to use a
special method to pass an explicitly typed Any, giving code such as:

    index = doc.createInstance("com.sun.star.text.ContentIndex");
    ...
    uno.invoke( index.LevelParagraphStyles , "replaceByIndex",
                (0, uno.Any("[]string", ('Caption',))) )

The new Pythonic container access is able to correctly infer the
expected type of the sequences required by these arguments. In the
new style, the above call to .replaceByIndex() can instead be
written:

    index.LevelParagraphStyles[0] = ('Caption',)

4. List and iterator arguments
==============================

Wherever a UNO API expects a sequence, a Python list or iterator can
now be passed. This enables the use of list comprehensions and
generator expressions for method calls and property assignments.

Example:

    tbl = doc.createInstance('com.sun.star.text.TextTable')
    tbl.initialize(10,10)
    # ... insert table ...
    # Assign numbers 0..99 to the cells using a generator expression
    tbl.Data = ((y for y in range(10*x,10*x + 10)) for x in range(10))

5. Tolerant struct initialisation
=================================

Previously, a UNO struct could be created fully uninitialised, or by
passing a combination of positional and/or named arguments to its
constructor. However, if any arguments were passed, all members were
required to be initialised or an exception was thrown.
This requirement is relaxed such that when all arguments passed to a
struct constructor are by name, some may be omitted. The existing
requirement that all members must be explicitly initialised when
some constructor arguments are unnamed (positional) is not affected.

Example:

    from com.sun.star.beans import PropertyValue
    prop = PropertyValue(Name='foo', Value='bar')

Change-Id: Id29bff10a18099b1a00af1abee1a6c1bc58b3978
Reviewed-on: https://gerrit.libreoffice.org/16272Tested-by: 's avatarJenkins <ci@libreoffice.org>
Reviewed-by: 's avatarMatthew Francis <mjay.francis@gmail.com>
üst eab89b7f
......@@ -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:
......@@ -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
......
......@@ -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) \
......
# -*- 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:
......@@ -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( );
......
This diff is collapsed.
#!/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
#!/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
This diff is collapsed.
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
#!/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
This diff is collapsed.
......@@ -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 );
......
/* -*- 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: */
......@@ -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());
}
}
}
}
......
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment